| // 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. | |
| #include <stdint.h> | |
| #include <array> | |
| #include "base/command_line.h" | |
| #include "base/compiler_specific.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.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; | |
| 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]); | |
| UNSAFE_TODO(EXPECT_EQ(1, result->GetData()[1])); | |
| UNSAFE_TODO(EXPECT_EQ(1, result->GetData()[2])); | |
| UNSAFE_TODO(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; | |
| 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; | |
| 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; | |
| 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; | |
| 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; | |
| 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(0x0009 /* 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(0x0009 /* 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(0x0009 /* 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 constexpr auto faces = std::to_array<GLenum>({ | |
| 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 (GLenum face : faces) { | |
| 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; | |
| 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 for ES2. | |
| constexpr GLenum fb_target = GL_FRAMEBUFFER; | |
| 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; | |
| 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 for ES3. | |
| constexpr GLenum fb_target = GL_DRAW_FRAMEBUFFER; | |
| 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()); | |
| } | |
| // Regression test for crbug.com/514508415. | |
| TEST_P(GLES3DecoderWithShaderTest, | |
| DrawArraysUniformBlockSharedBindingTooSmall) { | |
| SetupTexture(); | |
| // The default ES3 program has two uniform blocks: index 0 (data size 32) | |
| // bound at binding 0 and index 1 (data size 16) bound at binding 1. Move | |
| // index 1 to binding 0 so that both blocks share the same binding point. | |
| EXPECT_CALL(*gl_, UniformBlockBinding(kServiceProgramId, 1, 0)) | |
| .Times(1) | |
| .RetiresOnSaturation(); | |
| cmds::UniformBlockBinding ubb_cmd; | |
| ubb_cmd.Init(client_program_id_, 1, 0); | |
| EXPECT_EQ(error::kNoError, ExecuteCmd(ubb_cmd)); | |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); | |
| // Back the shared binding with a buffer that is large enough for the | |
| // smaller block but not for the larger one. | |
| DoBindBuffer(GL_UNIFORM_BUFFER, client_element_buffer_id_, | |
| kServiceElementBufferId); | |
| DoBufferData(GL_UNIFORM_BUFFER, 16); | |
| EXPECT_CALL(*gl_, | |
| BindBufferBase(GL_UNIFORM_BUFFER, 0, kServiceElementBufferId)) | |
| .Times(1) | |
| .RetiresOnSaturation(); | |
| cmds::BindBufferBase bbb_cmd; | |
| bbb_cmd.Init(GL_UNIFORM_BUFFER, 0, client_element_buffer_id_); | |
| EXPECT_EQ(error::kNoError, ExecuteCmd(bbb_cmd)); | |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); | |
| 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(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 |