chromium/gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "gpu/command_buffer/service/gles2_cmd_decoder.h"

#include <stdint.h>

#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
#include "gpu/command_buffer/common/gles2_cmd_format.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/context_state.h"
#include "gpu/command_buffer/service/gl_surface_mock.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder_unittest.h"

#include "gpu/command_buffer/service/mocks.h"
#include "gpu/command_buffer/service/program_manager.h"
#include "gpu/command_buffer/service/test_helper.h"
#include "gpu/config/gpu_switches.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_mock.h"
#include "ui/gl/gl_surface_stub.h"

#if !defined(GL_DEPTH24_STENCIL8)
#define GL_DEPTH24_STENCIL8 0x88F0
#endif

using ::gl::MockGLInterface;
using ::testing::_;
using ::testing::DoAll;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::MatcherCast;
using ::testing::Mock;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArrayArgument;
using ::testing::SetArgPointee;
using ::testing::StrEq;
using ::testing::StrictMock;

namespace gpu {
namespace gles2 {

class GLES2DecoderGeometryInstancingTest : public GLES2DecoderWithShaderTest {
 public:
  GLES2DecoderGeometryInstancingTest() : GLES2DecoderWithShaderTest() {}

  void SetUp() override {
    InitState init;
    init.extensions = "GL_ANGLE_instanced_arrays";
    init.has_alpha = true;
    init.has_depth = true;
    init.request_alpha = true;
    init.request_depth = true;
    init.bind_generates_resource = true;
    InitDecoder(init);
    SetupDefaultProgram();
  }
};

INSTANTIATE_TEST_SUITE_P(Service,
                         GLES2DecoderGeometryInstancingTest,
                         ::testing::Bool());

void GLES2DecoderManualInitTest::DirtyStateMaskTest(GLuint color_bits,
                                                    bool depth_mask,
                                                    GLuint front_stencil_mask,
                                                    GLuint back_stencil_mask) {
  cmds::ColorMask color_mask_cmd;
  color_mask_cmd.Init((color_bits & 0x1000) != 0,
                      (color_bits & 0x0100) != 0,
                      (color_bits & 0x0010) != 0,
                      (color_bits & 0x0001) != 0);
  EXPECT_EQ(error::kNoError, ExecuteCmd(color_mask_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  cmds::DepthMask depth_mask_cmd;
  depth_mask_cmd.Init(depth_mask);
  EXPECT_EQ(error::kNoError, ExecuteCmd(depth_mask_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  cmds::StencilMaskSeparate front_stencil_mask_cmd;
  front_stencil_mask_cmd.Init(GL_FRONT, front_stencil_mask);
  EXPECT_EQ(error::kNoError, ExecuteCmd(front_stencil_mask_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  cmds::StencilMaskSeparate back_stencil_mask_cmd;
  back_stencil_mask_cmd.Init(GL_BACK, back_stencil_mask);
  EXPECT_EQ(error::kNoError, ExecuteCmd(back_stencil_mask_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  SetupExpectationsForApplyingDirtyState(
      false,               // Framebuffer is RGB
      true,                // Framebuffer has depth
      true,                // Framebuffer has stencil
      color_bits,          // color bits
      depth_mask,          // depth mask
      false,               // depth enabled
      front_stencil_mask,  // front stencil mask
      back_stencil_mask,   // back stencil mask
      false);              // stencil enabled

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays draw_cmd;
  draw_cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(draw_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

// Test that with an RGB backbuffer if we set the color mask to 1,1,1,1 it is
// set to 1,1,1,0 at Draw time but is 1,1,1,1 at query time.
TEST_P(GLES2DecoderRGBBackbufferTest, RGBBackbufferColorMask) {
  cmds::ColorMask cmd;
  cmd.Init(true, true, true, true);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  SetupTexture();
  SetupExpectationsForApplyingDirtyState(true,    // Framebuffer is RGB
                                         false,   // Framebuffer has depth
                                         false,   // Framebuffer has stencil
                                         0x1110,  // color bits
                                         false,   // depth mask
                                         false,   // depth enabled
                                         0,       // front stencil mask
                                         0,       // back stencil mask
                                         false);  // stencil enabled

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays draw_cmd;
  draw_cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(draw_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  EXPECT_CALL(*gl_, GetError())
      .WillOnce(Return(GL_NO_ERROR))
      .WillOnce(Return(GL_NO_ERROR))
      .RetiresOnSaturation();
  auto* result =
      static_cast<cmds::GetIntegerv::Result*>(shared_memory_address_);
  EXPECT_CALL(*gl_, GetIntegerv(GL_COLOR_WRITEMASK, result->GetData()))
      .Times(0);
  result->size = 0;
  cmds::GetIntegerv cmd2;
  cmd2.Init(GL_COLOR_WRITEMASK, shared_memory_id_, shared_memory_offset_);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
  EXPECT_EQ(
      decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_COLOR_WRITEMASK),
      result->GetNumResults());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
  EXPECT_EQ(1, result->GetData()[0]);
  EXPECT_EQ(1, result->GetData()[1]);
  EXPECT_EQ(1, result->GetData()[2]);
  EXPECT_EQ(1, result->GetData()[3]);
}

// Test that with no depth if we set DepthMask true that it's set to false at
// draw time but querying it returns true.
TEST_P(GLES2DecoderRGBBackbufferTest, RGBBackbufferDepthMask) {
  EXPECT_CALL(*gl_, DepthMask(true)).Times(0).RetiresOnSaturation();
  cmds::DepthMask cmd;
  cmd.Init(true);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  SetupTexture();
  SetupExpectationsForApplyingDirtyState(true,    // Framebuffer is RGB
                                         false,   // Framebuffer has depth
                                         false,   // Framebuffer has stencil
                                         0x1110,  // color bits
                                         false,   // depth mask
                                         false,   // depth enabled
                                         0,       // front stencil mask
                                         0,       // back stencil mask
                                         false);  // stencil enabled

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays draw_cmd;
  draw_cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(draw_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  EXPECT_CALL(*gl_, GetError())
      .WillOnce(Return(GL_NO_ERROR))
      .WillOnce(Return(GL_NO_ERROR))
      .RetiresOnSaturation();
  auto* result =
      static_cast<cmds::GetIntegerv::Result*>(shared_memory_address_);
  EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_WRITEMASK, result->GetData()))
      .Times(0);
  result->size = 0;
  cmds::GetIntegerv cmd2;
  cmd2.Init(GL_DEPTH_WRITEMASK, shared_memory_id_, shared_memory_offset_);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
  EXPECT_EQ(
      decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_DEPTH_WRITEMASK),
      result->GetNumResults());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
  EXPECT_EQ(1, result->GetData()[0]);
}

// Test that with no stencil if we set the stencil mask it's still set to 0 at
// draw time but gets our value if we query.
TEST_P(GLES2DecoderRGBBackbufferTest, RGBBackbufferStencilMask) {
  const GLint kMask = 123;
  EXPECT_CALL(*gl_, StencilMask(kMask)).Times(0).RetiresOnSaturation();
  cmds::StencilMask cmd;
  cmd.Init(kMask);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  SetupTexture();
  SetupExpectationsForApplyingDirtyState(true,    // Framebuffer is RGB
                                         false,   // Framebuffer has depth
                                         false,   // Framebuffer has stencil
                                         0x1110,  // color bits
                                         false,   // depth mask
                                         false,   // depth enabled
                                         0,       // front stencil mask
                                         0,       // back stencil mask
                                         false);  // stencil enabled

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays draw_cmd;
  draw_cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(draw_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  EXPECT_CALL(*gl_, GetError())
      .WillOnce(Return(GL_NO_ERROR))
      .WillOnce(Return(GL_NO_ERROR))
      .RetiresOnSaturation();
  auto* result =
      static_cast<cmds::GetIntegerv::Result*>(shared_memory_address_);
  EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_WRITEMASK, result->GetData()))
      .Times(0);
  result->size = 0;
  cmds::GetIntegerv cmd2;
  cmd2.Init(GL_STENCIL_WRITEMASK, shared_memory_id_, shared_memory_offset_);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
  EXPECT_EQ(
      decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_STENCIL_WRITEMASK),
      result->GetNumResults());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
  EXPECT_EQ(kMask, result->GetData()[0]);
}

// Test that if an FBO is bound we get the correct masks.
TEST_P(GLES2DecoderRGBBackbufferTest, RGBBackbufferColorMaskFBO) {
  cmds::ColorMask cmd;
  cmd.Init(true, true, true, true);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  SetupTexture();
  SetupVertexBuffer();
  DoEnableVertexAttribArray(0);
  DoVertexAttribPointer(0, 2, GL_FLOAT, 0, 0);
  DoEnableVertexAttribArray(1);
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);
  DoEnableVertexAttribArray(2);
  DoVertexAttribPointer(2, 2, GL_FLOAT, 0, 0);
  SetupExpectationsForApplyingDirtyState(true,    // Framebuffer is RGB
                                         false,   // Framebuffer has depth
                                         false,   // Framebuffer has stencil
                                         0x1110,  // color bits
                                         false,   // depth mask
                                         false,   // depth enabled
                                         0,       // front stencil mask
                                         0,       // back stencil mask
                                         false);  // stencil enabled

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays draw_cmd;
  draw_cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(draw_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  // Check that no extra calls are made on the next draw.
  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_EQ(error::kNoError, ExecuteCmd(draw_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  // Setup Frame buffer.
  // needs to be 1x1 or else it's not renderable.
  const GLsizei kWidth = 1;
  const GLsizei kHeight = 1;
  const GLenum kFormat = GL_RGB;
  // Use a different texture for framebuffer to avoid drawing feedback loops.
  EXPECT_CALL(*gl_, GenTextures(_, _))
      .WillOnce(SetArgPointee<1>(kNewServiceId))
      .RetiresOnSaturation();
  GenHelper<cmds::GenTexturesImmediate>(kNewClientId);
  DoBindTexture(GL_TEXTURE_2D, kNewClientId, kNewServiceId);
  // Pass some data so the texture will be marked as cleared.
  DoTexImage2D(GL_TEXTURE_2D, 0, kFormat, kWidth, kHeight, 0, kFormat,
               GL_UNSIGNED_BYTE, shared_memory_id_, kSharedMemoryOffset);
  DoBindFramebuffer(
      GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
  DoFramebufferTexture2D(GL_FRAMEBUFFER,
                         GL_COLOR_ATTACHMENT0,
                         GL_TEXTURE_2D,
                         kNewClientId,
                         kNewServiceId,
                         0,
                         GL_NO_ERROR);
  DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
  EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_FRAMEBUFFER))
      .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
      .RetiresOnSaturation();

  // This time state needs to be set.
  SetupExpectationsForApplyingDirtyState(false,   // Framebuffer is RGB
                                         false,   // Framebuffer has depth
                                         false,   // Framebuffer has stencil
                                         0x1110,  // color bits
                                         false,   // depth mask
                                         false,   // depth enabled
                                         0,       // front stencil mask
                                         0,       // back stencil mask
                                         false);  // stencil enabled

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_EQ(error::kNoError, ExecuteCmd(draw_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  // Check that no extra calls are made on the next draw.
  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_EQ(error::kNoError, ExecuteCmd(draw_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  // Unbind
  DoBindFramebuffer(GL_FRAMEBUFFER, 0, 0);

  SetupExpectationsForApplyingDirtyState(true,    // Framebuffer is RGB
                                         false,   // Framebuffer has depth
                                         false,   // Framebuffer has stencil
                                         0x1110,  // color bits
                                         false,   // depth mask
                                         false,   // depth enabled
                                         0,       // front stencil mask
                                         0,       // back stencil mask
                                         false);  // stencil enabled

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_EQ(error::kNoError, ExecuteCmd(draw_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderManualInitTest, DepthEnableWithDepth) {
  InitState init;
  init.has_depth = true;
  init.request_depth = true;
  init.bind_generates_resource = true;
  InitDecoder(init);

  cmds::Enable cmd;
  cmd.Init(GL_DEPTH_TEST);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  SetupDefaultProgram();
  SetupTexture();
  SetupExpectationsForApplyingDirtyState(true,    // Framebuffer is RGB
                                         true,    // Framebuffer has depth
                                         false,   // Framebuffer has stencil
                                         0x1110,  // color bits
                                         true,    // depth mask
                                         true,    // depth enabled
                                         0,       // front stencil mask
                                         0,       // back stencil mask
                                         false);  // stencil enabled

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays draw_cmd;
  draw_cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(draw_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  EXPECT_CALL(*gl_, GetError())
      .WillOnce(Return(GL_NO_ERROR))
      .WillOnce(Return(GL_NO_ERROR))
      .RetiresOnSaturation();
  auto* result =
      static_cast<cmds::GetIntegerv::Result*>(shared_memory_address_);
  EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_TEST, _))
      .Times(0)
      .RetiresOnSaturation();
  result->size = 0;
  cmds::GetIntegerv cmd2;
  cmd2.Init(GL_DEPTH_TEST, shared_memory_id_, shared_memory_offset_);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
  EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_DEPTH_TEST),
            result->GetNumResults());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
  EXPECT_EQ(1, result->GetData()[0]);
}

TEST_P(GLES2DecoderManualInitTest, StencilEnableWithStencil) {
  InitState init;
  init.has_stencil = true;
  init.request_stencil = true;
  init.bind_generates_resource = true;
  InitDecoder(init);

  cmds::Enable cmd;
  cmd.Init(GL_STENCIL_TEST);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  SetupDefaultProgram();
  SetupTexture();
  SetupExpectationsForApplyingDirtyState(
      true,                               // Framebuffer is RGB
      false,                              // Framebuffer has depth
      true,                               // Framebuffer has stencil
      0x1110,                             // color bits
      false,                              // depth mask
      false,                              // depth enabled
      GLES2Decoder::kDefaultStencilMask,  // front stencil mask
      GLES2Decoder::kDefaultStencilMask,  // back stencil mask
      true);                              // stencil enabled

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays draw_cmd;
  draw_cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(draw_cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  EXPECT_CALL(*gl_, GetError())
      .WillOnce(Return(GL_NO_ERROR))
      .WillOnce(Return(GL_NO_ERROR))
      .RetiresOnSaturation();
  auto* result =
      static_cast<cmds::GetIntegerv::Result*>(shared_memory_address_);
  EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_TEST, _))
      .Times(0)
      .RetiresOnSaturation();
  result->size = 0;
  cmds::GetIntegerv cmd2;
  cmd2.Init(GL_STENCIL_TEST, shared_memory_id_, shared_memory_offset_);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
  EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_STENCIL_TEST),
            result->GetNumResults());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
  EXPECT_EQ(1, result->GetData()[0]);
}

TEST_P(GLES2DecoderManualInitTest, CachedColorMask) {
  InitState init;
  init.has_alpha = true;
  init.has_depth = true;
  init.has_stencil = true;
  init.request_alpha = true;
  init.request_depth = true;
  init.request_stencil = true;
  init.bind_generates_resource = true;
  InitDecoder(init);

  SetupDefaultProgram();
  SetupAllNeededVertexBuffers();
  SetupTexture();

  // Test all color_bits combinations twice.
  for (int i = 0; i < 32; i++) {
    GLuint color_bits = (i & 1 ? 0x0001 : 0x0000) | (i & 2 ? 0x0010 : 0x0000) |
                        (i & 4 ? 0x0100 : 0x0000) | (i & 8 ? 0x1000 : 0x0000);

    // Toggle depth_test to force ApplyDirtyState each time.
    DirtyStateMaskTest(color_bits, false, 0xffffffff, 0xffffffff);
    DirtyStateMaskTest(color_bits, true, 0xffffffff, 0xffffffff);
    DirtyStateMaskTest(color_bits, false, 0xffffffff, 0xffffffff);
  }
}

TEST_P(GLES2DecoderManualInitTest, CachedDepthMask) {
  InitState init;
  init.has_alpha = true;
  init.has_depth = true;
  init.has_stencil = true;
  init.request_alpha = true;
  init.request_depth = true;
  init.request_stencil = true;
  init.bind_generates_resource = true;
  InitDecoder(init);

  SetupDefaultProgram();
  SetupAllNeededVertexBuffers();
  SetupTexture();

  // Test all depth_mask combinations twice.
  for (int i = 0; i < 4; i++) {
    bool depth_mask = (i & 1) == 1;

    // Toggle color masks to force ApplyDirtyState each time.
    DirtyStateMaskTest(0x1010, depth_mask, 0xffffffff, 0xffffffff);
    DirtyStateMaskTest(0x0101, depth_mask, 0xffffffff, 0xffffffff);
    DirtyStateMaskTest(0x1010, depth_mask, 0xffffffff, 0xffffffff);
  }
}

TEST_P(GLES2DecoderManualInitTest, CachedStencilMask) {
  InitState init;
  init.has_alpha = true;
  init.has_depth = true;
  init.has_stencil = true;
  init.request_alpha = true;
  init.request_depth = true;
  init.request_stencil = true;
  init.bind_generates_resource = true;
  InitDecoder(init);

  SetupDefaultProgram();
  SetupAllNeededVertexBuffers();
  SetupTexture();

  // Test all stencil_mask combinations twice.
  for (int i = 0; i < 4; i++) {
    GLuint stencil_mask = (i & 1) ? 0xf0f0f0f0 : 0x0f0f0f0f;

    // Toggle color masks to force ApplyDirtyState each time.
    DirtyStateMaskTest(0x1010, true, stencil_mask, 0xffffffff);
    DirtyStateMaskTest(0x0101, true, stencil_mask, 0xffffffff);
    DirtyStateMaskTest(0x1010, true, stencil_mask, 0xffffffff);
  }

  for (int i = 0; i < 4; i++) {
    GLuint stencil_mask = (i & 1) ? 0xf0f0f0f0 : 0x0f0f0f0f;

    // Toggle color masks to force ApplyDirtyState each time.
    DirtyStateMaskTest(0x1010, true, 0xffffffff, stencil_mask);
    DirtyStateMaskTest(0x0101, true, 0xffffffff, stencil_mask);
    DirtyStateMaskTest(0x1010, true, 0xffffffff, stencil_mask);
  }
}

TEST_P(GLES2DecoderWithShaderTest, DrawArraysNoAttributesSucceeds) {
  SetupTexture();
  SetupExpectationsForApplyingDefaultDirtyState();

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawArraysBadTextureUsesBlack) {
  DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
  // This is an NPOT texture. As the default filtering requires mips
  // this should trigger replacing with black textures before rendering.
  DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 3, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
               shared_memory_id_, kSharedMemoryOffset);
  {
    InSequence sequence;
    EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0))
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(
        *gl_, BindTexture(GL_TEXTURE_2D, TestHelper::kServiceBlackTexture2dId))
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0))
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_2D, kServiceTextureId))
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0))
        .Times(1)
        .RetiresOnSaturation();
  }
  SetupExpectationsForApplyingDefaultDirtyState();
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawArraysMissingAttributesFails) {
  DoEnableVertexAttribArray(1);

  EXPECT_CALL(*gl_, DrawArrays(_, _, _)).Times(0);
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest,
       DrawArraysMissingAttributesZeroCountSucceeds) {
  DoEnableVertexAttribArray(1);

  EXPECT_CALL(*gl_, DrawArrays(_, _, _)).Times(0);
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, 0);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawArraysIntOverflow) {
  DoEnableVertexAttribArray(1);

  GLint large = std::numeric_limits<GLint>::max();

  EXPECT_CALL(*gl_, DrawArrays(_, _, _)).Times(0);
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, large, large);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawArraysValidAttributesSucceeds) {
  SetupTexture();
  SetupVertexBuffer();
  DoEnableVertexAttribArray(1);
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);
  SetupExpectationsForApplyingDefaultDirtyState();

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawArraysDeletedBufferFails) {
  const GLuint kNewServiceBufferId = 1897u;
  SetupVertexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, 0))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, GenBuffersARB(1, _))
      .WillOnce(SetArgPointee<1>(kNewServiceBufferId))
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, kNewServiceBufferId))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, VertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, DeleteBuffersARB(1, _)).Times(1).RetiresOnSaturation();
  EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, 0))
      .Times(1)
      .RetiresOnSaturation();
  DeleteVertexBuffer();

  EXPECT_CALL(*gl_, DrawArrays(_, _, _)).Times(0);
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawArraysDeletedProgramSucceeds) {
  SetupTexture();
  SetupExpectationsForApplyingDefaultDirtyState();
  DoDeleteProgram(client_program_id_, kServiceProgramId);

  EXPECT_CALL(*gl_, DrawArrays(_, _, _)).Times(1).RetiresOnSaturation();
  EXPECT_CALL(*gl_, DeleteProgram(kServiceProgramId)).Times(1);
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawArraysWithInvalidModeFails) {
  SetupVertexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  EXPECT_CALL(*gl_, DrawArrays(_, _, _)).Times(0);
  cmds::DrawArrays cmd;
  cmd.Init(GL_QUADS, 0, 1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
  cmd.Init(GL_POLYGON, 0, 1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawArraysInvalidCountFails) {
  SetupVertexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  // Try start > 0
  EXPECT_CALL(*gl_, DrawArrays(_, _, _)).Times(0);
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 1, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  // Try with count > size
  cmd.Init(GL_TRIANGLES, 0, kNumVertices + 1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  // Try with attrib offset > 0
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 4);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  // Try with size > 2 (ie, vec3 instead of vec2)
  DoVertexAttribPointer(1, 3, GL_FLOAT, 0, 0);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  // Try with stride > 8 (vec2 + vec2 byte)
  DoVertexAttribPointer(1, 2, GL_FLOAT, sizeof(GLfloat) * 3, 0);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawArraysInstancedANGLEFails) {
  SetupTexture();
  SetupVertexBuffer();
  DoEnableVertexAttribArray(1);
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  EXPECT_CALL(*gl_, DrawArraysInstancedANGLE(_, _, _, _))
      .Times(0)
      .RetiresOnSaturation();
  cmds::DrawArraysInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices, 1);
  EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}

TEST_P(GLES2DecoderWithShaderTest, VertexAttribDivisorANGLEFails) {
  SetupTexture();
  SetupVertexBuffer();
  DoEnableVertexAttribArray(1);
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  EXPECT_CALL(*gl_, VertexAttribDivisorANGLE(_, _))
      .Times(0)
      .RetiresOnSaturation();

  cmds::VertexAttribDivisorANGLE cmd;
  cmd.Init(0, 1);
  EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawArraysInstancedANGLENoAttributesFails) {
  SetupTexture();

  EXPECT_CALL(*gl_, DrawArraysInstancedANGLE(_, _, _, _))
      .Times(0)
      .RetiresOnSaturation();
  cmds::DrawArraysInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices, 1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawArraysInstancedANGLEMissingAttributesFails) {
  DoEnableVertexAttribArray(1);

  EXPECT_CALL(*gl_, DrawArraysInstancedANGLE(_, _, _, _)).Times(0);
  cmds::DrawArraysInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices, 1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawArraysInstancedANGLEMissingAttributesZeroCountSucceeds) {
  DoEnableVertexAttribArray(1);

  EXPECT_CALL(*gl_, DrawArraysInstancedANGLE(_, _, _, _)).Times(0);
  cmds::DrawArraysInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES, 0, 0, 1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawArraysInstancedANGLEValidAttributesSucceeds) {
  SetupTexture();
  SetupVertexBuffer();
  DoEnableVertexAttribArray(1);
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);
  SetupExpectationsForApplyingDefaultDirtyState();

  EXPECT_CALL(*gl_, DrawArraysInstancedANGLE(GL_TRIANGLES, 0, kNumVertices, 1))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArraysInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices, 1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawArraysInstancedANGLEWithInvalidModeFails) {
  SetupVertexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  EXPECT_CALL(*gl_, DrawArraysInstancedANGLE(_, _, _, _)).Times(0);
  cmds::DrawArraysInstancedANGLE cmd;
  cmd.Init(GL_QUADS, 0, 1, 1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
  cmd.Init(GL_POLYGON, 0, 1, 1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawArraysInstancedANGLEInvalidPrimcountFails) {
  SetupVertexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  EXPECT_CALL(*gl_, DrawArraysInstancedANGLE(_, _, _, _)).Times(0);
  cmds::DrawArraysInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES, 0, 1, -1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
}

// Per-instance data is twice as large, but number of instances is half
TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawArraysInstancedANGLELargeInstanceSucceeds) {
  SetupTexture();
  SetupVertexBuffer();
  SetupExpectationsForApplyingDefaultDirtyState();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  DoEnableVertexAttribArray(0);
  DoVertexAttribPointer(0, 4, GL_FLOAT, 0, 0);
  DoVertexAttribDivisorANGLE(0, 1);
  EXPECT_CALL(
      *gl_,
      DrawArraysInstancedANGLE(GL_TRIANGLES, 0, kNumVertices, kNumVertices / 2))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArraysInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices, kNumVertices / 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

// Regular drawArrays takes the divisor into account
TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawArraysWithDivisorSucceeds) {
  SetupTexture();
  SetupVertexBuffer();
  SetupExpectationsForApplyingDefaultDirtyState();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  DoEnableVertexAttribArray(0);
  // Access the data right at the end of the buffer.
  DoVertexAttribPointer(
      0, 2, GL_FLOAT, 0, (kNumVertices - 1) * 2 * sizeof(GLfloat));
  DoVertexAttribDivisorANGLE(0, 1);
  EXPECT_CALL(
      *gl_,
      DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

// Per-instance data is twice as large, but divisor is twice
TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawArraysInstancedANGLELargeDivisorSucceeds) {
  SetupTexture();
  SetupVertexBuffer();
  SetupExpectationsForApplyingDefaultDirtyState();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  DoEnableVertexAttribArray(0);
  DoVertexAttribPointer(0, 4, GL_FLOAT, 0, 0);
  DoVertexAttribDivisorANGLE(0, 2);
  EXPECT_CALL(
      *gl_,
      DrawArraysInstancedANGLE(GL_TRIANGLES, 0, kNumVertices, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArraysInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest, DrawArraysInstancedANGLELargeFails) {
  SetupTexture();
  SetupVertexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  DoEnableVertexAttribArray(0);
  DoVertexAttribPointer(0, 2, GL_FLOAT, 0, 0);
  DoVertexAttribDivisorANGLE(0, 1);
  EXPECT_CALL(*gl_, DrawArraysInstancedANGLE(_, _, _, _))
      .Times(0)
      .RetiresOnSaturation();
  cmds::DrawArraysInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices, kNumVertices + 1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  EXPECT_CALL(*gl_, DrawArraysInstancedANGLE(_, _, _, _))
      .Times(0)
      .RetiresOnSaturation();
  cmd.Init(GL_TRIANGLES, 0, kNumVertices + 1, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

// Per-index data is twice as large, but number of indices is half
TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawArraysInstancedANGLELargeIndexSucceeds) {
  SetupTexture();
  SetupVertexBuffer();
  SetupExpectationsForApplyingDefaultDirtyState();
  DoVertexAttribPointer(1, 4, GL_FLOAT, 0, 0);

  DoEnableVertexAttribArray(0);
  DoVertexAttribPointer(0, 2, GL_FLOAT, 0, 0);
  DoVertexAttribDivisorANGLE(0, 1);
  EXPECT_CALL(
      *gl_,
      DrawArraysInstancedANGLE(GL_TRIANGLES, 0, kNumVertices / 2, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArraysInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices / 2, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawArraysInstancedANGLENoDivisor0Fails) {
  SetupTexture();
  SetupVertexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  DoEnableVertexAttribArray(0);
  DoVertexAttribPointer(0, 2, GL_FLOAT, 0, 0);
  DoVertexAttribDivisorANGLE(0, 1);
  DoVertexAttribDivisorANGLE(1, 1);
  EXPECT_CALL(*gl_, DrawArraysInstancedANGLE(_, _, _, _))
      .Times(0)
      .RetiresOnSaturation();
  cmds::DrawArraysInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices, 1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawArraysNoDivisor0Fails) {
  SetupTexture();
  SetupVertexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  DoEnableVertexAttribArray(0);
  DoVertexAttribPointer(0, 2, GL_FLOAT, 0, 0);
  DoVertexAttribDivisorANGLE(0, 1);
  DoVertexAttribDivisorANGLE(1, 1);
  EXPECT_CALL(*gl_, DrawArrays(_, _, _))
      .Times(0)
      .RetiresOnSaturation();
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawElementsNoAttributesSucceeds) {
  SetupTexture();
  SetupIndexBuffer();
  SetupExpectationsForApplyingDefaultDirtyState();
  EXPECT_CALL(*gl_,
              DrawElements(GL_TRIANGLES,
                           kValidIndexRangeCount,
                           GL_UNSIGNED_SHORT,
                           BufferOffset(kValidIndexRangeStart * 2)))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawElements cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawElementsMissingAttributesFails) {
  SetupIndexBuffer();
  DoEnableVertexAttribArray(1);

  EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(0);
  cmds::DrawElements cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest,
       DrawElementsMissingAttributesZeroCountSucceeds) {
  SetupIndexBuffer();
  DoEnableVertexAttribArray(1);

  EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(0);
  cmds::DrawElements cmd;
  cmd.Init(GL_TRIANGLES, 0, GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawElementsExtraAttributesFails) {
  SetupIndexBuffer();
  DoEnableVertexAttribArray(6);

  EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(0);
  cmds::DrawElements cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawElementsValidAttributesSucceeds) {
  SetupTexture();
  SetupVertexBuffer();
  SetupIndexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);
  SetupExpectationsForApplyingDefaultDirtyState();

  EXPECT_CALL(*gl_,
              DrawElements(GL_TRIANGLES,
                           kValidIndexRangeCount,
                           GL_UNSIGNED_SHORT,
                           BufferOffset(kValidIndexRangeStart * 2)))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawElements cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawElementsDeletedBufferFails) {
  SetupVertexBuffer();
  SetupIndexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  EXPECT_CALL(*gl_, BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0))
      .Times(1)
      .RetiresOnSaturation();
  DeleteIndexBuffer();

  EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(0);
  cmds::DrawElements cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawElementsDeletedProgramSucceeds) {
  SetupTexture();
  SetupIndexBuffer();
  SetupExpectationsForApplyingDefaultDirtyState();
  DoDeleteProgram(client_program_id_, kServiceProgramId);

  EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(1);
  EXPECT_CALL(*gl_, DeleteProgram(kServiceProgramId)).Times(1);
  cmds::DrawElements cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawElementsWithInvalidModeFails) {
  SetupVertexBuffer();
  SetupIndexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(0);
  cmds::DrawElements cmd;
  cmd.Init(GL_QUADS,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
  cmd.Init(GL_POLYGON,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawElementsInvalidCountFails) {
  SetupVertexBuffer();
  SetupIndexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  // Try start > 0
  EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(0);
  cmds::DrawElements cmd;
  cmd.Init(GL_TRIANGLES, kNumIndices, GL_UNSIGNED_SHORT, 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  // Try with count > size
  cmd.Init(GL_TRIANGLES, kNumIndices + 1, GL_UNSIGNED_SHORT, 0);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawElementsOutOfRangeIndicesFails) {
  SetupVertexBuffer();
  SetupIndexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(0);
  cmds::DrawElements cmd;
  cmd.Init(GL_TRIANGLES,
           kInvalidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kInvalidIndexRangeStart * 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawElementsOddOffsetForUint16Fails) {
  SetupVertexBuffer();
  SetupIndexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(0);
  cmds::DrawElements cmd;
  cmd.Init(GL_TRIANGLES, kInvalidIndexRangeCount, GL_UNSIGNED_SHORT, 1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawElementsInstancedANGLEFails) {
  SetupTexture();
  SetupVertexBuffer();
  SetupIndexBuffer();
  DoEnableVertexAttribArray(1);
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  EXPECT_CALL(*gl_, DrawElementsInstancedANGLE(_, _, _, _, _))
      .Times(0)
      .RetiresOnSaturation();
  cmds::DrawElementsInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2,
           1);
  EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawElementsInstancedANGLENoAttributesFails) {
  SetupTexture();
  SetupIndexBuffer();

  EXPECT_CALL(*gl_, DrawElementsInstancedANGLE(_, _, _, _, _))
      .Times(0)
      .RetiresOnSaturation();
  cmds::DrawElementsInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2,
           1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawElementsInstancedANGLEMissingAttributesFails) {
  SetupIndexBuffer();
  DoEnableVertexAttribArray(1);

  EXPECT_CALL(*gl_, DrawElementsInstancedANGLE(_, _, _, _, _)).Times(0);
  cmds::DrawElementsInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2,
           1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawElementsInstancedANGLEMissingAttributesZeroCountSucceeds) {
  SetupIndexBuffer();
  DoEnableVertexAttribArray(1);

  EXPECT_CALL(*gl_, DrawElementsInstancedANGLE(_, _, _, _, _)).Times(0);
  cmds::DrawElementsInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES, 0, GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2, 1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawElementsInstancedANGLEValidAttributesSucceeds) {
  SetupIndexBuffer();
  SetupTexture();
  SetupVertexBuffer();
  DoEnableVertexAttribArray(1);
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);
  SetupExpectationsForApplyingDefaultDirtyState();

  EXPECT_CALL(
      *gl_,
      DrawElementsInstancedANGLE(GL_TRIANGLES,
                                 kValidIndexRangeCount,
                                 GL_UNSIGNED_SHORT,
                                 BufferOffset(kValidIndexRangeStart * 2),
                                 1))
      .Times(1)
      .RetiresOnSaturation();

  cmds::DrawElementsInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2,
           1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawElementsInstancedANGLEWithInvalidModeFails) {
  SetupIndexBuffer();
  SetupVertexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  EXPECT_CALL(*gl_, DrawElementsInstancedANGLE(_, _, _, _, _)).Times(0);
  cmds::DrawElementsInstancedANGLE cmd;
  cmd.Init(GL_QUADS,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2,
           1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
  cmd.Init(GL_INVALID_ENUM,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2,
           1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
}

// Per-instance data is twice as large, but number of instances is half
TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawElementsInstancedANGLELargeInstanceSucceeds) {
  SetupTexture();
  SetupIndexBuffer();
  SetupVertexBuffer();
  SetupExpectationsForApplyingDefaultDirtyState();
  // Add offset so we're sure we're accessing data near the end of the buffer.
  DoVertexAttribPointer(
      1,
      2,
      GL_FLOAT,
      0,
      (kNumVertices - kMaxValidIndex - 1) * 2 * sizeof(GLfloat));

  DoEnableVertexAttribArray(0);
  DoVertexAttribPointer(0, 4, GL_FLOAT, 0, 0);
  DoVertexAttribDivisorANGLE(0, 1);
  EXPECT_CALL(
      *gl_,
      DrawElementsInstancedANGLE(GL_TRIANGLES,
                                 kValidIndexRangeCount,
                                 GL_UNSIGNED_SHORT,
                                 BufferOffset(kValidIndexRangeStart * 2),
                                 kNumVertices / 2))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawElementsInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2,
           kNumVertices / 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

// Regular drawElements takes the divisor into account
TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawElementsWithDivisorSucceeds) {
  SetupTexture();
  SetupIndexBuffer();
  SetupVertexBuffer();
  SetupExpectationsForApplyingDefaultDirtyState();
  // Add offset so we're sure we're accessing data near the end of the buffer.
  DoVertexAttribPointer(
      1,
      2,
      GL_FLOAT,
      0,
      (kNumVertices - kMaxValidIndex - 1) * 2 * sizeof(GLfloat));

  DoEnableVertexAttribArray(0);
  // Access the data right at the end of the buffer.
  DoVertexAttribPointer(
      0, 2, GL_FLOAT, 0, (kNumVertices - 1) * 2 * sizeof(GLfloat));
  DoVertexAttribDivisorANGLE(0, 1);
  EXPECT_CALL(
      *gl_,
      DrawElements(GL_TRIANGLES,
                   kValidIndexRangeCount,
                   GL_UNSIGNED_SHORT,
                   BufferOffset(kValidIndexRangeStart * 2)))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawElements cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

// Per-instance data is twice as large, but divisor is twice
TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawElementsInstancedANGLELargeDivisorSucceeds) {
  SetupTexture();
  SetupIndexBuffer();
  SetupVertexBuffer();
  SetupExpectationsForApplyingDefaultDirtyState();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  DoEnableVertexAttribArray(0);
  DoVertexAttribPointer(0, 4, GL_FLOAT, 0, 0);
  DoVertexAttribDivisorANGLE(0, 2);
  EXPECT_CALL(
      *gl_,
      DrawElementsInstancedANGLE(GL_TRIANGLES,
                                 kValidIndexRangeCount,
                                 GL_UNSIGNED_SHORT,
                                 BufferOffset(kValidIndexRangeStart * 2),
                                 kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawElementsInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2,
           kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawElementsInstancedANGLELargeFails) {
  SetupTexture();
  SetupIndexBuffer();
  SetupVertexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  DoEnableVertexAttribArray(0);
  DoVertexAttribPointer(0, 2, GL_FLOAT, 0, 0);
  DoVertexAttribDivisorANGLE(0, 1);
  EXPECT_CALL(*gl_, DrawElementsInstancedANGLE(_, _, _, _, _))
      .Times(0)
      .RetiresOnSaturation();
  cmds::DrawElementsInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2,
           kNumVertices + 1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  EXPECT_CALL(*gl_, DrawElementsInstancedANGLE(_, _, _, _, _))
      .Times(0)
      .RetiresOnSaturation();
  cmd.Init(GL_TRIANGLES,
           kInvalidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kInvalidIndexRangeStart * 2,
           kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawElementsInstancedANGLEInvalidPrimcountFails) {
  SetupTexture();
  SetupIndexBuffer();
  SetupVertexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  DoEnableVertexAttribArray(0);
  DoVertexAttribPointer(0, 2, GL_FLOAT, 0, 0);
  DoVertexAttribDivisorANGLE(0, 1);
  EXPECT_CALL(*gl_, DrawElementsInstancedANGLE(_, _, _, _, _))
      .Times(0)
      .RetiresOnSaturation();
  cmds::DrawElementsInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2,
           -1);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

// Per-index data is twice as large, but values of indices are smaller
TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawElementsInstancedANGLELargeIndexSucceeds) {
  SetupTexture();
  SetupIndexBuffer();
  SetupVertexBuffer();
  SetupExpectationsForApplyingDefaultDirtyState();
  DoVertexAttribPointer(1, 4, GL_FLOAT, 0, 0);

  DoEnableVertexAttribArray(0);
  DoVertexAttribPointer(0, 2, GL_FLOAT, 0, 0);
  DoVertexAttribDivisorANGLE(0, 1);
  EXPECT_CALL(
      *gl_,
      DrawElementsInstancedANGLE(GL_TRIANGLES,
                                 kValidIndexRangeCount,
                                 GL_UNSIGNED_SHORT,
                                 BufferOffset(kValidIndexRangeStart * 2),
                                 kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawElementsInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2,
           kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawElementsInstancedANGLENoDivisor0Fails) {
  SetupTexture();
  SetupIndexBuffer();
  SetupVertexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  DoEnableVertexAttribArray(0);
  DoVertexAttribPointer(0, 2, GL_FLOAT, 0, 0);
  DoVertexAttribDivisorANGLE(0, 1);
  DoVertexAttribDivisorANGLE(1, 1);
  EXPECT_CALL(*gl_, DrawElementsInstancedANGLE(_, _, _, _, _))
      .Times(0)
      .RetiresOnSaturation();
  cmds::DrawElementsInstancedANGLE cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2,
           kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderGeometryInstancingTest,
       DrawElementsNoDivisor0Fails) {
  SetupTexture();
  SetupIndexBuffer();
  SetupVertexBuffer();
  DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);

  DoEnableVertexAttribArray(0);
  DoVertexAttribPointer(0, 2, GL_FLOAT, 0, 0);
  DoVertexAttribDivisorANGLE(0, 1);
  DoVertexAttribDivisorANGLE(1, 1);
  EXPECT_CALL(*gl_, DrawElements(_, _, _, _))
      .Times(0)
      .RetiresOnSaturation();
  cmds::DrawElements cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawArraysClearsAfterTexImage2DNULL) {
  SetupAllNeededVertexBuffers();
  DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
  // Create an uncleared texture with 2 levels.
  DoTexImage2D(
      GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
  DoTexImage2D(
      GL_TEXTURE_2D, 1, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
  // Expect 2 levels will be cleared.
  SetupClearTextureExpectations(kServiceTextureId, kServiceTextureId,
                                GL_TEXTURE_2D, GL_TEXTURE_2D, 0, GL_RGBA,
                                GL_UNSIGNED_BYTE, 0, 0, 2, 2, 0);
  SetupClearTextureExpectations(kServiceTextureId, kServiceTextureId,
                                GL_TEXTURE_2D, GL_TEXTURE_2D, 1, GL_RGBA,
                                GL_UNSIGNED_BYTE, 0, 0, 1, 1, 0);
  SetupExpectationsForApplyingDefaultDirtyState();
  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  // But not again
  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawElementsClearsAfterTexImage2DNULL) {
  SetupAllNeededVertexBuffers();
  SetupIndexBuffer();
  DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
  // Create an uncleared texture with 2 levels.
  DoTexImage2D(
      GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
  DoTexImage2D(
      GL_TEXTURE_2D, 1, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
  // Expect 2 levels will be cleared.
  SetupClearTextureExpectations(kServiceTextureId, kServiceTextureId,
                                GL_TEXTURE_2D, GL_TEXTURE_2D, 0, GL_RGBA,
                                GL_UNSIGNED_BYTE, 0, 0, 2, 2, 0);
  SetupClearTextureExpectations(kServiceTextureId, kServiceTextureId,
                                GL_TEXTURE_2D, GL_TEXTURE_2D, 1, GL_RGBA,
                                GL_UNSIGNED_BYTE, 0, 0, 1, 1, 0);
  SetupExpectationsForApplyingDefaultDirtyState();

  EXPECT_CALL(*gl_,
              DrawElements(GL_TRIANGLES,
                           kValidIndexRangeCount,
                           GL_UNSIGNED_SHORT,
                           BufferOffset(kValidIndexRangeStart * 2)))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawElements cmd;
  cmd.Init(GL_TRIANGLES,
           kValidIndexRangeCount,
           GL_UNSIGNED_SHORT,
           kValidIndexRangeStart * 2);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  // But not again
  EXPECT_CALL(*gl_,
              DrawElements(GL_TRIANGLES,
                           kValidIndexRangeCount,
                           GL_UNSIGNED_SHORT,
                           BufferOffset(kValidIndexRangeStart * 2)))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawClearsAfterTexImage2DNULLInFBO) {
  const GLuint kFBOClientTextureId = 4100;
  const GLuint kFBOServiceTextureId = 4101;

  SetupAllNeededVertexBuffers();
  // Register a texture id.
  EXPECT_CALL(*gl_, GenTextures(_, _))
      .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
      .RetiresOnSaturation();
  GenHelper<cmds::GenTexturesImmediate>(kFBOClientTextureId);

  // Setup "render to" texture.
  DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
  DoTexImage2D(
      GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
  DoBindFramebuffer(
      GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
  DoFramebufferTexture2D(GL_FRAMEBUFFER,
                         GL_COLOR_ATTACHMENT0,
                         GL_TEXTURE_2D,
                         kFBOClientTextureId,
                         kFBOServiceTextureId,
                         0,
                         GL_NO_ERROR);
  DoEnableDisable(GL_SCISSOR_TEST, false);
  DoScissor(0, 0, 1, 1);

  // Setup "render from" texture.
  SetupTexture();

  SetupExpectationsForFramebufferClearing(GL_FRAMEBUFFER,       // target
                                          GL_COLOR_BUFFER_BIT,  // clear bits
                                          0, 0, 0,
                                          0,      // color
                                          0,      // stencil
                                          1.0f,   // depth
                                          false,  // scissor test
                                          0, 0, 1, 1);

  SetupExpectationsForApplyingDirtyState(false,   // Framebuffer is RGB
                                         false,   // Framebuffer has depth
                                         false,   // Framebuffer has stencil
                                         0x1111,  // color bits
                                         false,   // depth mask
                                         false,   // depth enabled
                                         0,       // front stencil mask
                                         0,       // back stencil mask
                                         false);  // stencil enabled

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  // But not again.
  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawWitFBOThatCantClearDoesNotDraw) {
  const GLuint kFBOClientTextureId = 4100;
  const GLuint kFBOServiceTextureId = 4101;

  // Register a texture id.
  EXPECT_CALL(*gl_, GenTextures(_, _))
      .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
      .RetiresOnSaturation();
  GenHelper<cmds::GenTexturesImmediate>(kFBOClientTextureId);

  // Setup "render to" texture.
  DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
  DoTexImage2D(
      GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
  DoBindFramebuffer(
      GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
  DoFramebufferTexture2D(GL_FRAMEBUFFER,
                         GL_COLOR_ATTACHMENT0,
                         GL_TEXTURE_2D,
                         kFBOClientTextureId,
                         kFBOServiceTextureId,
                         0,
                         GL_NO_ERROR);

  // Setup "render from" texture.
  SetupTexture();

  EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_FRAMEBUFFER))
      .WillOnce(Return(GL_FRAMEBUFFER_UNSUPPORTED))
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, DrawArrays(_, _, _)).Times(0).RetiresOnSaturation();
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_FRAMEBUFFER_OPERATION, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest, DrawClearsAfterRenderbufferStorageInFBO) {
  SetupTexture();
  DoBindRenderbuffer(
      GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
  DoBindFramebuffer(
      GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
  DoRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 100, 50, GL_NO_ERROR);
  DoFramebufferRenderbuffer(GL_FRAMEBUFFER,
                            GL_COLOR_ATTACHMENT0,
                            GL_RENDERBUFFER,
                            client_renderbuffer_id_,
                            kServiceRenderbufferId,
                            GL_NO_ERROR);
  DoEnableDisable(GL_SCISSOR_TEST, false);
  DoScissor(0, 0, 1, 1);

  SetupExpectationsForFramebufferClearing(GL_FRAMEBUFFER,       // target
                                          GL_COLOR_BUFFER_BIT,  // clear bits
                                          0, 0, 0,
                                          0,      // color
                                          0,      // stencil
                                          1.0f,   // depth
                                          false,  // scissor test
                                          0, 0, 1, 1);

  SetupExpectationsForApplyingDirtyState(false,   // Framebuffer is RGB
                                         false,   // Framebuffer has depth
                                         false,   // Framebuffer has stencil
                                         0x1111,  // color bits
                                         false,   // depth mask
                                         false,   // depth enabled
                                         0,       // front stencil mask
                                         0,       // back stencil mask
                                         false);  // stencil enabled

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderManualInitTest, DrawArraysClearsAfterTexImage2DNULLCubemap) {
  InitState init;
  init.gl_version = "OpenGL ES 2.0";
  init.has_alpha = true;
  init.has_depth = true;
  init.request_alpha = true;
  init.request_depth = true;
  InitDecoder(init);

  static const GLenum faces[] = {
      GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
      GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
      GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
  };
  SetupCubemapProgram();
  DoBindTexture(GL_TEXTURE_CUBE_MAP, client_texture_id_, kServiceTextureId);
  // Fill out all the faces for 2 levels, leave 2 uncleared.
  for (int ii = 0; ii < 6; ++ii) {
    GLenum face = faces[ii];
    int32_t shm_id =
        (face == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y) ? 0 : shared_memory_id_;
    uint32_t shm_offset =
        (face == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y) ? 0 : kSharedMemoryOffset;
    DoTexImage2D(face,
                 0,
                 GL_RGBA,
                 2,
                 2,
                 0,
                 GL_RGBA,
                 GL_UNSIGNED_BYTE,
                 shm_id,
                 shm_offset);
    DoTexImage2D(face,
                 1,
                 GL_RGBA,
                 1,
                 1,
                 0,
                 GL_RGBA,
                 GL_UNSIGNED_BYTE,
                 shm_id,
                 shm_offset);
  }
  // Expect 2 levels will be cleared.
  SetupClearTextureExpectations(kServiceTextureId, kServiceTextureId,
                                GL_TEXTURE_CUBE_MAP,
                                GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA,
                                GL_UNSIGNED_BYTE, 0, 0, 2, 2, 0);
  SetupClearTextureExpectations(kServiceTextureId, kServiceTextureId,
                                GL_TEXTURE_CUBE_MAP,
                                GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 1, GL_RGBA,
                                GL_UNSIGNED_BYTE, 0, 0, 1, 1, 0);
  SetupExpectationsForApplyingDefaultDirtyState();
  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
}

TEST_P(GLES2DecoderWithShaderTest,
       DrawClearsAfterRenderbuffersWithMultipleAttachments) {
  const GLuint kFBOClientTextureId = 4100;
  const GLuint kFBOServiceTextureId = 4101;

  // Register a texture id.
  EXPECT_CALL(*gl_, GenTextures(_, _))
      .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
      .RetiresOnSaturation();
  GenHelper<cmds::GenTexturesImmediate>(kFBOClientTextureId);

  // Setup "render to" texture.
  DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
  DoTexImage2D(
      GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
  DoBindFramebuffer(
      GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
  DoFramebufferTexture2D(GL_FRAMEBUFFER,
                         GL_COLOR_ATTACHMENT0,
                         GL_TEXTURE_2D,
                         kFBOClientTextureId,
                         kFBOServiceTextureId,
                         0,
                         GL_NO_ERROR);

  DoBindRenderbuffer(
      GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
  DoBindFramebuffer(
      GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
  DoRenderbufferStorage(GL_RENDERBUFFER,
                        GL_DEPTH_COMPONENT16,
                        1,
                        1,
                        GL_NO_ERROR);
  DoFramebufferRenderbuffer(GL_FRAMEBUFFER,
                            GL_DEPTH_ATTACHMENT,
                            GL_RENDERBUFFER,
                            client_renderbuffer_id_,
                            kServiceRenderbufferId,
                            GL_NO_ERROR);
  DoEnableDisable(GL_SCISSOR_TEST, false);
  DoScissor(0, 0, 1, 1);
  SetupTexture();
  SetupExpectationsForFramebufferClearing(
      GL_FRAMEBUFFER,                             // target
      GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,  // clear bits
      0, 0, 0,
      0,      // color
      0,      // stencil
      1.0f,   // depth
      false,  // scissor test
      0, 0, 1, 1);

  SetupExpectationsForApplyingDirtyState(false,   // Framebuffer is RGB
                                         true,    // Framebuffer has depth
                                         false,   // Framebuffer has stencil
                                         0x1111,  // color bits
                                         true,    // depth mask
                                         false,   // depth enabled
                                         0,       // front stencil mask
                                         0,       // back stencil mask
                                         false);  // stencil enabled

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderWithShaderTest,
       DrawingWithFBOTwiceChecksForFBOCompleteOnce) {
  const GLuint kFBOClientTextureId = 4100;
  const GLuint kFBOServiceTextureId = 4101;

  SetupAllNeededVertexBuffers();

  // Register a texture id.
  EXPECT_CALL(*gl_, GenTextures(_, _))
      .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
      .RetiresOnSaturation();
  GenHelper<cmds::GenTexturesImmediate>(kFBOClientTextureId);

  // Setup "render to" texture that is cleared.
  DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
  DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
               shared_memory_id_, kSharedMemoryOffset);
  DoBindFramebuffer(
      GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
  DoFramebufferTexture2D(GL_FRAMEBUFFER,
                         GL_COLOR_ATTACHMENT0,
                         GL_TEXTURE_2D,
                         kFBOClientTextureId,
                         kFBOServiceTextureId,
                         0,
                         GL_NO_ERROR);

  // Setup "render from" texture.
  SetupTexture();

  // Make sure we check for framebuffer complete.
  EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_FRAMEBUFFER))
      .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
      .RetiresOnSaturation();

  SetupExpectationsForApplyingDirtyState(false,   // Framebuffer is RGB
                                         false,   // Framebuffer has depth
                                         false,   // Framebuffer has stencil
                                         0x1111,  // color bits
                                         false,   // depth mask
                                         false,   // depth enabled
                                         0,       // front stencil mask
                                         0,       // back stencil mask
                                         false);  // stencil enabled

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());

  // But not again.
  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderManualInitTest, DrawClearsDepthTexture) {
  InitState init;
  init.extensions = "GL_ANGLE_depth_texture";
  init.gl_version = "OpenGL ES 2.0";
  init.has_alpha = true;
  init.has_depth = true;
  init.request_alpha = true;
  init.request_depth = true;
  init.bind_generates_resource = true;
  InitDecoder(init);

  SetupDefaultProgram();
  SetupAllNeededVertexBuffers();
  constexpr GLenum attachment = GL_DEPTH_ATTACHMENT;
  constexpr GLenum target = GL_TEXTURE_2D;
  constexpr GLint level = 0;
  // Note that the target framebuffer will be GL_FRAMEBUFFER_EXT for ES2.
  constexpr GLenum fb_target = GL_FRAMEBUFFER_EXT;
  DoBindTexture(target, client_texture_id_, kServiceTextureId);

  // Create a depth texture.
  DoTexImage2D(target,
               level,
               GL_DEPTH_COMPONENT,
               1,
               1,
               0,
               GL_DEPTH_COMPONENT,
               GL_UNSIGNED_INT,
               0,
               0);

  // Set scissor rect and disable GL_SCISSOR_TEST to make sure we enable it in
  // the clear, then disable it and restore the rect again.
  DoScissor(0, 0, 32, 32);
  DoEnableDisable(GL_SCISSOR_TEST, false);

  EXPECT_CALL(*gl_, GenFramebuffersEXT(1, _)).Times(1).RetiresOnSaturation();
  EXPECT_CALL(*gl_, BindFramebufferEXT(fb_target, _))
      .Times(1)
      .RetiresOnSaturation();

  EXPECT_CALL(*gl_, FramebufferTexture2DEXT(fb_target, attachment, target,
                                            kServiceTextureId, level))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(fb_target))
      .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
      .RetiresOnSaturation();

  SetupExpectationsForColorMask(true, true, true, true);
  EXPECT_CALL(*gl_, ClearColor(0.0f, 0.0f, 0.0f, 0.0f))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, ClearStencil(0)).Times(1).RetiresOnSaturation();
  SetupExpectationsForStencilMask(GLES2Decoder::kDefaultStencilMask,
                                  GLES2Decoder::kDefaultStencilMask);
  EXPECT_CALL(*gl_, ClearDepth(1.0f)).Times(1).RetiresOnSaturation();
  SetupExpectationsForDepthMask(true);
  SetupExpectationsForEnableDisable(GL_SCISSOR_TEST, true);
  EXPECT_CALL(*gl_, Scissor(0, 0, 1, 1)).Times(1).RetiresOnSaturation();

  EXPECT_CALL(*gl_, Clear(GL_DEPTH_BUFFER_BIT)).Times(1).RetiresOnSaturation();

  SetupExpectationsForRestoreClearState(0.0f, 0.0f, 0.0f, 0.0f, 0, 1.0f, false,
                                        0, 0, 32, 32);

  EXPECT_CALL(*gl_, DeleteFramebuffersEXT(1, _)).Times(1).RetiresOnSaturation();
  EXPECT_CALL(*gl_, BindFramebufferEXT(fb_target, 0))
      .Times(1)
      .RetiresOnSaturation();

  SetupExpectationsForApplyingDefaultDirtyState();
  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

#if BUILDFLAG(IS_MAC)
TEST_P(GLES2DecoderManualInitTest, DrawClearsLargeTexture) {
  InitState init;
  init.gl_version = "OpenGL ES 3.0";
  init.has_alpha = true;
  init.has_depth = true;
  init.request_alpha = true;
  init.request_depth = true;
  init.bind_generates_resource = true;
  InitDecoder(init);

  SetupDefaultProgram();
  SetupAllNeededVertexBuffers();
  constexpr GLenum attachment = GL_COLOR_ATTACHMENT0;
  constexpr GLenum target = GL_TEXTURE_2D;
  constexpr GLint level = 0;
  // Note that the target framebuffer will be GL_DRAW_FRAMEBUFFER_EXT for ES3.
  constexpr GLenum fb_target = GL_DRAW_FRAMEBUFFER_EXT;
  DoBindTexture(target, client_texture_id_, kServiceTextureId);

  // Create an RGBA texture.
  DoTexImage2D(target, level, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE,
               0, 0);

  // Set scissor rect and disable GL_SCISSOR_TEST to make sure we enable it in
  // the clear, then disable it and restore the rect again.
  DoScissor(0, 0, 32, 32);
  DoEnableDisable(GL_SCISSOR_TEST, false);

  EXPECT_CALL(*gl_, GenFramebuffersEXT(1, _)).Times(1).RetiresOnSaturation();
  EXPECT_CALL(*gl_, BindFramebufferEXT(fb_target, _))
      .Times(1)
      .RetiresOnSaturation();

  EXPECT_CALL(*gl_, FramebufferTexture2DEXT(fb_target, attachment, target,
                                            kServiceTextureId, level))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(fb_target))
      .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
      .RetiresOnSaturation();

  SetupExpectationsForColorMask(true, true, true, true);
  EXPECT_CALL(*gl_, ClearColor(0.0f, 0.0f, 0.0f, 0.0f))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, ClearStencil(0)).Times(1).RetiresOnSaturation();
  SetupExpectationsForStencilMask(GLES2Decoder::kDefaultStencilMask,
                                  GLES2Decoder::kDefaultStencilMask);
  EXPECT_CALL(*gl_, ClearDepth(1.0f)).Times(1).RetiresOnSaturation();
  SetupExpectationsForDepthMask(true);
  SetupExpectationsForEnableDisable(GL_SCISSOR_TEST, true);
  EXPECT_CALL(*gl_, Scissor(0, 0, 256, 256)).Times(1).RetiresOnSaturation();

  EXPECT_CALL(*gl_, Clear(GL_COLOR_BUFFER_BIT)).Times(1).RetiresOnSaturation();

  SetupExpectationsForRestoreClearState(0.0f, 0.0f, 0.0f, 0.0f, 0, 1.0f, false,
                                        0, 0, 32, 32);

  EXPECT_CALL(*gl_, DeleteFramebuffersEXT(1, _)).Times(1).RetiresOnSaturation();
  EXPECT_CALL(*gl_, BindFramebufferEXT(fb_target, 0))
      .Times(1)
      .RetiresOnSaturation();

  SetupExpectationsForApplyingDefaultDirtyState();
  EXPECT_CALL(*gl_, ActiveTexture(_)).Times(3).RetiresOnSaturation();
  EXPECT_CALL(*gl_, BindTexture(_, _)).Times(2).RetiresOnSaturation();
  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
      .Times(1)
      .RetiresOnSaturation();
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
#endif

TEST_P(GLES3DecoderTest, DrawNoProgram) {
  SetupAllNeededVertexBuffers();

  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, _, _)).Times(0);
  cmds::DrawArrays cmd;
  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_NO_ERROR, GetGLError());
}

TEST_P(GLES2DecoderTest, ClearInvalidValue) {
  EXPECT_CALL(*gl_, Clear(_)).Times(0);
  cmds::Clear cmd;
  cmd.Init(0xffffffff);
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
  EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
}

}  // namespace gles2
}  // namespace gpu