// 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