// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "gpu/command_buffer/service/gles2_cmd_decoder.h" #include "base/command_line.h" #include "base/strings/string_number_conversions.h" #include "gpu/command_buffer/common/gles2_cmd_format.h" #include "gpu/command_buffer/common/gles2_cmd_utils.h" #include "gpu/command_buffer/common/id_allocator.h" #include "gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h" #include "gpu/command_buffer/service/async_pixel_transfer_manager.h" #include "gpu/command_buffer/service/async_pixel_transfer_manager_mock.h" #include "gpu/command_buffer/service/cmd_buffer_engine.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/gpu_switches.h" #include "gpu/command_buffer/service/image_manager.h" #include "gpu/command_buffer/service/mailbox_manager.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 "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 ::gfx::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::SetArgumentPointee; using ::testing::SetArgPointee; using ::testing::StrEq; using ::testing::StrictMock; namespace gpu { namespace gles2 { using namespace cmds; class GLES2DecoderTestWithExtensionsOnGLES2 : public GLES2DecoderTest { public: GLES2DecoderTestWithExtensionsOnGLES2() {} virtual void SetUp() {} void Init(const char* extensions) { InitState init; init.extensions = extensions; 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); } }; TEST_P(GLES2DecoderTest, CheckFramebufferStatusWithNoBoundTarget) { EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(_)).Times(0); CheckFramebufferStatus::Result* result = static_cast(shared_memory_address_); *result = 0; CheckFramebufferStatus cmd; cmd.Init(GL_FRAMEBUFFER, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(static_cast(GL_FRAMEBUFFER_COMPLETE), *result); } TEST_P(GLES2DecoderWithShaderTest, BindAndDeleteFramebuffer) { SetupTexture(); AddExpectationsForSimulatedAttrib0(kNumVertices, 0); SetupExpectationsForApplyingDefaultDirtyState(); DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); DoDeleteFramebuffer(client_framebuffer_id_, kServiceFramebufferId, true, GL_FRAMEBUFFER, 0, true, GL_FRAMEBUFFER, 0); EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices)) .Times(1) .RetiresOnSaturation(); DrawArrays cmd; cmd.Init(GL_TRIANGLES, 0, kNumVertices); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } TEST_P(GLES2DecoderTest, FramebufferRenderbufferWithNoBoundTarget) { EXPECT_CALL(*gl_, FramebufferRenderbufferEXT(_, _, _, _)).Times(0); FramebufferRenderbuffer cmd; cmd.Init(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, client_renderbuffer_id_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } TEST_P(GLES2DecoderTest, FramebufferTexture2DWithNoBoundTarget) { EXPECT_CALL(*gl_, FramebufferTexture2DEXT(_, _, _, _, _)).Times(0); FramebufferTexture2D cmd; cmd.Init(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, client_texture_id_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } TEST_P(GLES2DecoderTest, GetFramebufferAttachmentParameterivWithNoBoundTarget) { EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetFramebufferAttachmentParameterivEXT(_, _, _, _)) .Times(0); GetFramebufferAttachmentParameteriv cmd; cmd.Init(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } TEST_P(GLES2DecoderTest, GetFramebufferAttachmentParameterivWithRenderbuffer) { DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, FramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, kServiceRenderbufferId)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); GetFramebufferAttachmentParameteriv::Result* result = static_cast( shared_memory_address_); result->size = 0; const GLint* result_value = result->GetData(); FramebufferRenderbuffer fbrb_cmd; GetFramebufferAttachmentParameteriv cmd; fbrb_cmd.Init(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, client_renderbuffer_id_); cmd.Init(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(fbrb_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(static_cast(*result_value), client_renderbuffer_id_); } TEST_P(GLES2DecoderTest, GetFramebufferAttachmentParameterivWithTexture) { DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, FramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, kServiceTextureId, 0)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); GetFramebufferAttachmentParameteriv::Result* result = static_cast( shared_memory_address_); result->SetNumResults(0); const GLint* result_value = result->GetData(); FramebufferTexture2D fbtex_cmd; GetFramebufferAttachmentParameteriv cmd; fbtex_cmd.Init(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, client_texture_id_); cmd.Init(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(fbtex_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(static_cast(*result_value), client_texture_id_); } TEST_P(GLES2DecoderTest, GetRenderbufferParameterivWithNoBoundTarget) { EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetRenderbufferParameterivEXT(_, _, _)).Times(0); GetRenderbufferParameteriv cmd; cmd.Init(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } TEST_P(GLES2DecoderTest, RenderbufferStorageWithNoBoundTarget) { EXPECT_CALL(*gl_, RenderbufferStorageEXT(_, _, _, _)).Times(0); RenderbufferStorage cmd; cmd.Init(GL_RENDERBUFFER, GL_RGBA4, 3, 4); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } namespace { // A class to emulate glReadPixels class ReadPixelsEmulator { public: // pack_alignment is the alignment you want ReadPixels to use // when copying. The actual data passed in pixels should be contiguous. ReadPixelsEmulator(GLsizei width, GLsizei height, GLint bytes_per_pixel, const void* src_pixels, const void* expected_pixels, GLint pack_alignment) : width_(width), height_(height), pack_alignment_(pack_alignment), bytes_per_pixel_(bytes_per_pixel), src_pixels_(reinterpret_cast(src_pixels)), expected_pixels_(reinterpret_cast(expected_pixels)) {} void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) const { DCHECK_GE(x, 0); DCHECK_GE(y, 0); DCHECK_LE(x + width, width_); DCHECK_LE(y + height, height_); for (GLint yy = 0; yy < height; ++yy) { const int8* src = GetPixelAddress(src_pixels_, x, y + yy); const void* dst = ComputePackAlignmentAddress(0, yy, width, pixels); memcpy(const_cast(dst), src, width * bytes_per_pixel_); } } bool CompareRowSegment(GLint x, GLint y, GLsizei width, const void* data) const { DCHECK(x + width <= width_ || width == 0); return memcmp(data, GetPixelAddress(expected_pixels_, x, y), width * bytes_per_pixel_) == 0; } // Helper to compute address of pixel in pack aligned data. const void* ComputePackAlignmentAddress(GLint x, GLint y, GLsizei width, const void* address) const { GLint unpadded_row_size = ComputeImageDataSize(width, 1); GLint two_rows_size = ComputeImageDataSize(width, 2); GLsizei padded_row_size = two_rows_size - unpadded_row_size; GLint offset = y * padded_row_size + x * bytes_per_pixel_; return static_cast(address) + offset; } GLint ComputeImageDataSize(GLint width, GLint height) const { GLint row_size = width * bytes_per_pixel_; if (height > 1) { GLint temp = row_size + pack_alignment_ - 1; GLint padded_row_size = (temp / pack_alignment_) * pack_alignment_; GLint size_of_all_but_last_row = (height - 1) * padded_row_size; return size_of_all_but_last_row + row_size; } else { return height * row_size; } } private: const int8* GetPixelAddress(const int8* base, GLint x, GLint y) const { return base + (width_ * y + x) * bytes_per_pixel_; } GLsizei width_; GLsizei height_; GLint pack_alignment_; GLint bytes_per_pixel_; const int8* src_pixels_; const int8* expected_pixels_; }; } // anonymous namespace void GLES2DecoderTest::CheckReadPixelsOutOfRange(GLint in_read_x, GLint in_read_y, GLsizei in_read_width, GLsizei in_read_height, bool init) { const GLsizei kWidth = 5; const GLsizei kHeight = 3; const GLint kBytesPerPixel = 3; const GLint kPackAlignment = 4; const GLenum kFormat = GL_RGB; static const int8 kSrcPixels[kWidth * kHeight * kBytesPerPixel] = { 12, 13, 14, 18, 19, 18, 19, 12, 13, 14, 18, 19, 18, 19, 13, 29, 28, 23, 22, 21, 22, 21, 29, 28, 23, 22, 21, 22, 21, 28, 31, 34, 39, 37, 32, 37, 32, 31, 34, 39, 37, 32, 37, 32, 34, }; ClearSharedMemory(); // We need to setup an FBO so we can know the max size that ReadPixels will // access if (init) { DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); DoTexImage2D(GL_TEXTURE_2D, 0, kFormat, kWidth, kHeight, 0, kFormat, GL_UNSIGNED_BYTE, kSharedMemoryId, kSharedMemoryOffset); DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); DoFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, client_texture_id_, kServiceTextureId, 0, GL_NO_ERROR); EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_FRAMEBUFFER)) .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE)) .RetiresOnSaturation(); } ReadPixelsEmulator emu( kWidth, kHeight, kBytesPerPixel, kSrcPixels, kSrcPixels, kPackAlignment); typedef ReadPixels::Result Result; Result* result = GetSharedMemoryAs(); uint32 result_shm_id = kSharedMemoryId; uint32 result_shm_offset = kSharedMemoryOffset; uint32 pixels_shm_id = kSharedMemoryId; uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(*result); void* dest = &result[1]; EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); // ReadPixels will be called for valid size only even though the command // is requesting a larger size. GLint read_x = std::max(0, in_read_x); GLint read_y = std::max(0, in_read_y); GLint read_end_x = std::max(0, std::min(kWidth, in_read_x + in_read_width)); GLint read_end_y = std::max(0, std::min(kHeight, in_read_y + in_read_height)); GLint read_width = read_end_x - read_x; GLint read_height = read_end_y - read_y; if (read_width > 0 && read_height > 0) { for (GLint yy = read_y; yy < read_end_y; ++yy) { EXPECT_CALL( *gl_, ReadPixels(read_x, yy, read_width, 1, kFormat, GL_UNSIGNED_BYTE, _)) .WillOnce(Invoke(&emu, &ReadPixelsEmulator::ReadPixels)) .RetiresOnSaturation(); } } ReadPixels cmd; cmd.Init(in_read_x, in_read_y, in_read_width, in_read_height, kFormat, GL_UNSIGNED_BYTE, pixels_shm_id, pixels_shm_offset, result_shm_id, result_shm_offset, false); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); GLint unpadded_row_size = emu.ComputeImageDataSize(in_read_width, 1); scoped_ptr zero(new int8[unpadded_row_size]); scoped_ptr pack(new int8[kPackAlignment]); memset(zero.get(), 0, unpadded_row_size); memset(pack.get(), kInitialMemoryValue, kPackAlignment); for (GLint yy = 0; yy < in_read_height; ++yy) { const int8* row = static_cast( emu.ComputePackAlignmentAddress(0, yy, in_read_width, dest)); GLint y = in_read_y + yy; if (y < 0 || y >= kHeight) { EXPECT_EQ(0, memcmp(zero.get(), row, unpadded_row_size)); } else { // check off left. GLint num_left_pixels = std::max(-in_read_x, 0); GLint num_left_bytes = num_left_pixels * kBytesPerPixel; EXPECT_EQ(0, memcmp(zero.get(), row, num_left_bytes)); // check off right. GLint num_right_pixels = std::max(in_read_x + in_read_width - kWidth, 0); GLint num_right_bytes = num_right_pixels * kBytesPerPixel; EXPECT_EQ(0, memcmp(zero.get(), row + unpadded_row_size - num_right_bytes, num_right_bytes)); // check middle. GLint x = std::max(in_read_x, 0); GLint num_middle_pixels = std::max(in_read_width - num_left_pixels - num_right_pixels, 0); EXPECT_TRUE( emu.CompareRowSegment(x, y, num_middle_pixels, row + num_left_bytes)); } // check padding if (yy != in_read_height - 1) { GLint num_padding_bytes = (kPackAlignment - 1) - (unpadded_row_size % kPackAlignment); EXPECT_EQ(0, memcmp(pack.get(), row + unpadded_row_size, num_padding_bytes)); } } } TEST_P(GLES2DecoderTest, ReadPixels) { const GLsizei kWidth = 5; const GLsizei kHeight = 3; const GLint kBytesPerPixel = 3; const GLint kPackAlignment = 4; static const int8 kSrcPixels[kWidth * kHeight * kBytesPerPixel] = { 12, 13, 14, 18, 19, 18, 19, 12, 13, 14, 18, 19, 18, 19, 13, 29, 28, 23, 22, 21, 22, 21, 29, 28, 23, 22, 21, 22, 21, 28, 31, 34, 39, 37, 32, 37, 32, 31, 34, 39, 37, 32, 37, 32, 34, }; surface_->SetSize(gfx::Size(INT_MAX, INT_MAX)); ReadPixelsEmulator emu( kWidth, kHeight, kBytesPerPixel, kSrcPixels, kSrcPixels, kPackAlignment); typedef ReadPixels::Result Result; Result* result = GetSharedMemoryAs(); uint32 result_shm_id = kSharedMemoryId; uint32 result_shm_offset = kSharedMemoryOffset; uint32 pixels_shm_id = kSharedMemoryId; uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(*result); void* dest = &result[1]; EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, ReadPixels(0, 0, kWidth, kHeight, GL_RGB, GL_UNSIGNED_BYTE, _)) .WillOnce(Invoke(&emu, &ReadPixelsEmulator::ReadPixels)); ReadPixels cmd; cmd.Init(0, 0, kWidth, kHeight, GL_RGB, GL_UNSIGNED_BYTE, pixels_shm_id, pixels_shm_offset, result_shm_id, result_shm_offset, false); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); for (GLint yy = 0; yy < kHeight; ++yy) { EXPECT_TRUE(emu.CompareRowSegment( 0, yy, kWidth, emu.ComputePackAlignmentAddress(0, yy, kWidth, dest))); } } TEST_P(GLES2DecoderRGBBackbufferTest, ReadPixelsNoAlphaBackbuffer) { const GLsizei kWidth = 3; const GLsizei kHeight = 3; const GLint kBytesPerPixel = 4; const GLint kPackAlignment = 4; static const uint8 kExpectedPixels[kWidth * kHeight * kBytesPerPixel] = { 12, 13, 14, 255, 19, 18, 19, 255, 13, 14, 18, 255, 29, 28, 23, 255, 21, 22, 21, 255, 28, 23, 22, 255, 31, 34, 39, 255, 32, 37, 32, 255, 34, 39, 37, 255, }; static const uint8 kSrcPixels[kWidth * kHeight * kBytesPerPixel] = { 12, 13, 14, 18, 19, 18, 19, 12, 13, 14, 18, 19, 29, 28, 23, 22, 21, 22, 21, 29, 28, 23, 22, 21, 31, 34, 39, 37, 32, 37, 32, 31, 34, 39, 37, 32, }; surface_->SetSize(gfx::Size(INT_MAX, INT_MAX)); ReadPixelsEmulator emu(kWidth, kHeight, kBytesPerPixel, kSrcPixels, kExpectedPixels, kPackAlignment); typedef ReadPixels::Result Result; Result* result = GetSharedMemoryAs(); uint32 result_shm_id = kSharedMemoryId; uint32 result_shm_offset = kSharedMemoryOffset; uint32 pixels_shm_id = kSharedMemoryId; uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(*result); void* dest = &result[1]; EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, ReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, _)) .WillOnce(Invoke(&emu, &ReadPixelsEmulator::ReadPixels)); ReadPixels cmd; cmd.Init(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels_shm_id, pixels_shm_offset, result_shm_id, result_shm_offset, false); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); for (GLint yy = 0; yy < kHeight; ++yy) { EXPECT_TRUE(emu.CompareRowSegment( 0, yy, kWidth, emu.ComputePackAlignmentAddress(0, yy, kWidth, dest))); } } TEST_P(GLES2DecoderTest, ReadPixelsOutOfRange) { static GLint tests[][4] = { { -2, -1, 9, 5, }, // out of range on all sides { 2, 1, 9, 5, }, // out of range on right, bottom { -7, -4, 9, 5, }, // out of range on left, top { 0, -5, 9, 5, }, // completely off top { 0, 3, 9, 5, }, // completely off bottom { -9, 0, 9, 5, }, // completely off left { 5, 0, 9, 5, }, // completely off right }; for (size_t tt = 0; tt < arraysize(tests); ++tt) { CheckReadPixelsOutOfRange( tests[tt][0], tests[tt][1], tests[tt][2], tests[tt][3], tt == 0); } } TEST_P(GLES2DecoderTest, ReadPixelsInvalidArgs) { typedef ReadPixels::Result Result; Result* result = GetSharedMemoryAs(); uint32 result_shm_id = kSharedMemoryId; uint32 result_shm_offset = kSharedMemoryOffset; uint32 pixels_shm_id = kSharedMemoryId; uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(*result); EXPECT_CALL(*gl_, ReadPixels(_, _, _, _, _, _, _)).Times(0); ReadPixels cmd; cmd.Init(0, 0, -1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixels_shm_id, pixels_shm_offset, result_shm_id, result_shm_offset, false); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); cmd.Init(0, 0, 1, -1, GL_RGB, GL_UNSIGNED_BYTE, pixels_shm_id, pixels_shm_offset, result_shm_id, result_shm_offset, false); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); cmd.Init(0, 0, 1, 1, GL_RGB, GL_INT, pixels_shm_id, pixels_shm_offset, result_shm_id, result_shm_offset, false); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); cmd.Init(0, 0, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, kInvalidSharedMemoryId, pixels_shm_offset, result_shm_id, result_shm_offset, false); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); cmd.Init(0, 0, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixels_shm_id, kInvalidSharedMemoryOffset, result_shm_id, result_shm_offset, false); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); cmd.Init(0, 0, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixels_shm_id, pixels_shm_offset, kInvalidSharedMemoryId, result_shm_offset, false); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); cmd.Init(0, 0, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixels_shm_id, pixels_shm_offset, result_shm_id, kInvalidSharedMemoryOffset, false); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderManualInitTest, ReadPixelsAsyncError) { InitState init; init.extensions = "GL_ARB_sync"; init.gl_version = "opengl es 3.0"; init.has_alpha = true; init.request_alpha = true; init.bind_generates_resource = true; InitDecoder(init); typedef ReadPixels::Result Result; Result* result = GetSharedMemoryAs(); const GLsizei kWidth = 4; const GLsizei kHeight = 4; uint32 result_shm_id = kSharedMemoryId; uint32 result_shm_offset = kSharedMemoryOffset; uint32 pixels_shm_id = kSharedMemoryId; uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(*result); EXPECT_CALL(*gl_, GetError()) // first error check must pass to get to the test .WillOnce(Return(GL_NO_ERROR)) // second check is after BufferData, simulate fail here .WillOnce(Return(GL_INVALID_OPERATION)) // third error check is fall-through call to sync ReadPixels .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, ReadPixels(0, 0, kWidth, kHeight, GL_RGB, GL_UNSIGNED_BYTE, _)) .Times(1); EXPECT_CALL(*gl_, GenBuffersARB(1, _)).Times(1); EXPECT_CALL(*gl_, BindBuffer(GL_PIXEL_PACK_BUFFER_ARB, _)).Times(2); EXPECT_CALL(*gl_, BufferData(GL_PIXEL_PACK_BUFFER_ARB, _, NULL, GL_STREAM_READ)) .Times(1); ReadPixels cmd; cmd.Init(0, 0, kWidth, kHeight, GL_RGB, GL_UNSIGNED_BYTE, pixels_shm_id, pixels_shm_offset, result_shm_id, result_shm_offset, true); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } // Check that if a renderbuffer is attached and GL returns // GL_FRAMEBUFFER_COMPLETE that the buffer is cleared and state is restored. TEST_P(GLES2DecoderTest, FramebufferRenderbufferClearColor) { DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); ClearColor color_cmd; ColorMask color_mask_cmd; Enable enable_cmd; FramebufferRenderbuffer cmd; color_cmd.Init(0.1f, 0.2f, 0.3f, 0.4f); color_mask_cmd.Init(0, 1, 0, 1); enable_cmd.Init(GL_SCISSOR_TEST); cmd.Init(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, client_renderbuffer_id_); InSequence sequence; EXPECT_CALL(*gl_, ClearColor(0.1f, 0.2f, 0.3f, 0.4f)) .Times(1) .RetiresOnSaturation(); SetupExpectationsForEnableDisable(GL_SCISSOR_TEST, true); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, FramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, kServiceRenderbufferId)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(color_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(color_mask_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(enable_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderTest, FramebufferRenderbufferClearDepth) { DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); ClearDepthf depth_cmd; DepthMask depth_mask_cmd; FramebufferRenderbuffer cmd; depth_cmd.Init(0.5f); depth_mask_cmd.Init(false); cmd.Init(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, client_renderbuffer_id_); InSequence sequence; EXPECT_CALL(*gl_, ClearDepth(0.5f)).Times(1).RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, FramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, kServiceRenderbufferId)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(depth_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(depth_mask_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderTest, FramebufferRenderbufferClearStencil) { DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); ClearStencil stencil_cmd; StencilMaskSeparate stencil_mask_separate_cmd; FramebufferRenderbuffer cmd; stencil_cmd.Init(123); stencil_mask_separate_cmd.Init(GL_BACK, 0x1234u); cmd.Init(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, client_renderbuffer_id_); InSequence sequence; EXPECT_CALL(*gl_, ClearStencil(123)).Times(1).RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, FramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, kServiceRenderbufferId)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(stencil_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(stencil_mask_separate_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } #if 0 // Turn this test on once we allow GL_DEPTH_STENCIL_ATTACHMENT TEST_P(GLES2DecoderTest, FramebufferRenderbufferClearDepthStencil) { DoBindFramebuffer(GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); ClearDepthf depth_cmd; ClearStencil stencil_cmd; FramebufferRenderbuffer cmd; depth_cmd.Init(0.5f); stencil_cmd.Init(123); cmd.Init( GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, client_renderbuffer_id_); InSequence sequence; EXPECT_CALL(*gl_, ClearDepth(0.5f)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl_, ClearStencil(123)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl_, FramebufferRenderbufferEXT( GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, kServiceRenderbufferId)) .Times(1) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(depth_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(stencil_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } #endif TEST_P(GLES2DecoderManualInitTest, ActualAlphaMatchesRequestedAlpha) { InitState init; init.gl_version = "3.0"; init.has_alpha = true; init.request_alpha = true; init.bind_generates_resource = true; InitDecoder(init); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); typedef GetIntegerv::Result Result; Result* result = static_cast(shared_memory_address_); EXPECT_CALL(*gl_, GetIntegerv(GL_ALPHA_BITS, _)) .WillOnce(SetArgumentPointee<1>(8)) .RetiresOnSaturation(); result->size = 0; GetIntegerv cmd2; cmd2.Init(GL_ALPHA_BITS, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_ALPHA_BITS), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(8, result->GetData()[0]); } TEST_P(GLES2DecoderManualInitTest, ActualAlphaDoesNotMatchRequestedAlpha) { InitState init; init.gl_version = "3.0"; init.has_alpha = true; init.bind_generates_resource = true; InitDecoder(init); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); typedef GetIntegerv::Result Result; Result* result = static_cast(shared_memory_address_); EXPECT_CALL(*gl_, GetIntegerv(GL_ALPHA_BITS, _)) .WillOnce(SetArgumentPointee<1>(8)) .RetiresOnSaturation(); result->size = 0; GetIntegerv cmd2; cmd2.Init(GL_ALPHA_BITS, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_ALPHA_BITS), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(0, result->GetData()[0]); } TEST_P(GLES2DecoderManualInitTest, ActualDepthMatchesRequestedDepth) { InitState init; init.gl_version = "3.0"; init.has_depth = true; init.request_depth = true; init.bind_generates_resource = true; InitDecoder(init); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); typedef GetIntegerv::Result Result; Result* result = static_cast(shared_memory_address_); EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_BITS, _)) .WillOnce(SetArgumentPointee<1>(24)) .RetiresOnSaturation(); result->size = 0; GetIntegerv cmd2; cmd2.Init(GL_DEPTH_BITS, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_DEPTH_BITS), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(24, result->GetData()[0]); } TEST_P(GLES2DecoderManualInitTest, ActualDepthDoesNotMatchRequestedDepth) { InitState init; init.gl_version = "3.0"; init.has_depth = true; init.bind_generates_resource = true; InitDecoder(init); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); typedef GetIntegerv::Result Result; Result* result = static_cast(shared_memory_address_); EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_BITS, _)) .WillOnce(SetArgumentPointee<1>(24)) .RetiresOnSaturation(); result->size = 0; GetIntegerv cmd2; cmd2.Init(GL_DEPTH_BITS, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_DEPTH_BITS), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(0, result->GetData()[0]); } TEST_P(GLES2DecoderManualInitTest, ActualStencilMatchesRequestedStencil) { InitState init; init.gl_version = "3.0"; init.has_stencil = true; init.request_stencil = true; init.bind_generates_resource = true; InitDecoder(init); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); typedef GetIntegerv::Result Result; Result* result = static_cast(shared_memory_address_); EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_BITS, _)) .WillOnce(SetArgumentPointee<1>(8)) .RetiresOnSaturation(); result->size = 0; GetIntegerv cmd2; cmd2.Init(GL_STENCIL_BITS, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_STENCIL_BITS), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(8, result->GetData()[0]); } TEST_P(GLES2DecoderManualInitTest, ActualStencilDoesNotMatchRequestedStencil) { InitState init; init.gl_version = "3.0"; init.has_stencil = true; init.bind_generates_resource = true; InitDecoder(init); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); typedef GetIntegerv::Result Result; Result* result = static_cast(shared_memory_address_); EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_BITS, _)) .WillOnce(SetArgumentPointee<1>(8)) .RetiresOnSaturation(); result->size = 0; GetIntegerv cmd2; cmd2.Init(GL_STENCIL_BITS, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_STENCIL_BITS), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(0, result->GetData()[0]); } TEST_P(GLES2DecoderManualInitTest, PackedDepthStencilReportsCorrectValues) { InitState init; init.extensions = "GL_OES_packed_depth_stencil"; init.gl_version = "opengl es 2.0"; init.has_depth = true; init.has_stencil = true; init.request_depth = true; init.request_stencil = true; init.bind_generates_resource = true; InitDecoder(init); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); typedef GetIntegerv::Result Result; Result* result = static_cast(shared_memory_address_); result->size = 0; GetIntegerv cmd2; cmd2.Init(GL_STENCIL_BITS, shared_memory_id_, shared_memory_offset_); EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_BITS, _)) .WillOnce(SetArgumentPointee<1>(8)) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_STENCIL_BITS), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(8, result->GetData()[0]); result->size = 0; cmd2.Init(GL_DEPTH_BITS, shared_memory_id_, shared_memory_offset_); EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_BITS, _)) .WillOnce(SetArgumentPointee<1>(24)) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_DEPTH_BITS), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(24, result->GetData()[0]); } TEST_P(GLES2DecoderManualInitTest, PackedDepthStencilNoRequestedStencil) { InitState init; init.extensions = "GL_OES_packed_depth_stencil"; init.gl_version = "opengl es 2.0"; init.has_depth = true; init.has_stencil = true; init.request_depth = true; init.bind_generates_resource = true; InitDecoder(init); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); typedef GetIntegerv::Result Result; Result* result = static_cast(shared_memory_address_); result->size = 0; GetIntegerv cmd2; cmd2.Init(GL_STENCIL_BITS, shared_memory_id_, shared_memory_offset_); EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_BITS, _)) .WillOnce(SetArgumentPointee<1>(8)) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_STENCIL_BITS), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(0, result->GetData()[0]); result->size = 0; cmd2.Init(GL_DEPTH_BITS, shared_memory_id_, shared_memory_offset_); EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_BITS, _)) .WillOnce(SetArgumentPointee<1>(24)) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_DEPTH_BITS), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(24, result->GetData()[0]); } TEST_P(GLES2DecoderManualInitTest, PackedDepthStencilRenderbufferDepth) { InitState init; init.extensions = "GL_OES_packed_depth_stencil"; init.gl_version = "opengl es 2.0"; init.bind_generates_resource = true; InitDecoder(init); DoBindRenderbuffer( GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId); DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) // for RenderbufferStoage .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) // for FramebufferRenderbuffer .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) // for GetIntegerv .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) // for GetIntegerv .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL( *gl_, RenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 100, 50)) .Times(1) .RetiresOnSaturation(); RenderbufferStorage cmd; cmd.Init(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 100, 50); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_CALL(*gl_, FramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, kServiceRenderbufferId)) .Times(1) .RetiresOnSaturation(); FramebufferRenderbuffer fbrb_cmd; fbrb_cmd.Init(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, client_renderbuffer_id_); EXPECT_EQ(error::kNoError, ExecuteCmd(fbrb_cmd)); typedef GetIntegerv::Result Result; Result* result = static_cast(shared_memory_address_); result->size = 0; GetIntegerv cmd2; cmd2.Init(GL_STENCIL_BITS, shared_memory_id_, shared_memory_offset_); EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_BITS, _)) .WillOnce(SetArgumentPointee<1>(8)) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_STENCIL_BITS), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(0, result->GetData()[0]); result->size = 0; cmd2.Init(GL_DEPTH_BITS, shared_memory_id_, shared_memory_offset_); EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_BITS, _)) .WillOnce(SetArgumentPointee<1>(24)) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_DEPTH_BITS), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(24, result->GetData()[0]); } TEST_P(GLES2DecoderManualInitTest, PackedDepthStencilRenderbufferStencil) { InitState init; init.extensions = "GL_OES_packed_depth_stencil"; init.gl_version = "opengl es 2.0"; init.bind_generates_resource = true; InitDecoder(init); DoBindRenderbuffer( GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId); DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) // for RenderbufferStoage .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) // for FramebufferRenderbuffer .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) // for GetIntegerv .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) // for GetIntegerv .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL( *gl_, RenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 100, 50)) .Times(1) .RetiresOnSaturation(); RenderbufferStorage cmd; cmd.Init(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 100, 50); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_CALL(*gl_, FramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, kServiceRenderbufferId)) .Times(1) .RetiresOnSaturation(); FramebufferRenderbuffer fbrb_cmd; fbrb_cmd.Init(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, client_renderbuffer_id_); EXPECT_EQ(error::kNoError, ExecuteCmd(fbrb_cmd)); typedef GetIntegerv::Result Result; Result* result = static_cast(shared_memory_address_); result->size = 0; GetIntegerv cmd2; cmd2.Init(GL_STENCIL_BITS, shared_memory_id_, shared_memory_offset_); EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_BITS, _)) .WillOnce(SetArgumentPointee<1>(8)) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_STENCIL_BITS), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(8, result->GetData()[0]); result->size = 0; cmd2.Init(GL_DEPTH_BITS, shared_memory_id_, shared_memory_offset_); EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_BITS, _)) .WillOnce(SetArgumentPointee<1>(24)) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_DEPTH_BITS), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(0, result->GetData()[0]); } TEST_P(GLES2DecoderTest, FramebufferRenderbufferGLError) { DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_OUT_OF_MEMORY)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, FramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, kServiceRenderbufferId)) .Times(1) .RetiresOnSaturation(); FramebufferRenderbuffer cmd; cmd.Init(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, client_renderbuffer_id_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError()); } TEST_P(GLES2DecoderTest, FramebufferTexture2DGLError) { const GLsizei kWidth = 5; const GLsizei kHeight = 3; const GLenum kFormat = GL_RGB; DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); DoTexImage2D(GL_TEXTURE_2D, 0, kFormat, kWidth, kHeight, 0, kFormat, GL_UNSIGNED_BYTE, 0, 0); DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_OUT_OF_MEMORY)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, FramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, kServiceTextureId, 0)) .Times(1) .RetiresOnSaturation(); FramebufferTexture2D fbtex_cmd; fbtex_cmd.Init(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, client_texture_id_); EXPECT_EQ(error::kNoError, ExecuteCmd(fbtex_cmd)); EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError()); } TEST_P(GLES2DecoderTest, RenderbufferStorageGLError) { DoBindRenderbuffer( GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_OUT_OF_MEMORY)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, RenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA, 100, 50)) .Times(1) .RetiresOnSaturation(); RenderbufferStorage cmd; cmd.Init(GL_RENDERBUFFER, GL_RGBA4, 100, 50); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError()); } TEST_P(GLES2DecoderTest, RenderbufferStorageBadArgs) { DoBindRenderbuffer( GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId); EXPECT_CALL(*gl_, RenderbufferStorageEXT(_, _, _, _)) .Times(0) .RetiresOnSaturation(); RenderbufferStorage cmd; cmd.Init(GL_RENDERBUFFER, GL_RGBA4, TestHelper::kMaxRenderbufferSize + 1, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); cmd.Init(GL_RENDERBUFFER, GL_RGBA4, 1, TestHelper::kMaxRenderbufferSize + 1); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); } TEST_P(GLES2DecoderManualInitTest, RenderbufferStorageMultisampleCHROMIUMGLError) { InitState init; init.extensions = "GL_EXT_framebuffer_multisample"; init.gl_version = "2.1"; init.bind_generates_resource = true; InitDecoder(init); DoBindRenderbuffer( GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_OUT_OF_MEMORY)) .RetiresOnSaturation(); EXPECT_CALL( *gl_, RenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 1, GL_RGBA, 100, 50)) .Times(1) .RetiresOnSaturation(); RenderbufferStorageMultisampleCHROMIUM cmd; cmd.Init(GL_RENDERBUFFER, 1, GL_RGBA4, 100, 50); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError()); } TEST_P(GLES2DecoderManualInitTest, RenderbufferStorageMultisampleCHROMIUMBadArgs) { InitState init; init.extensions = "GL_EXT_framebuffer_multisample"; init.gl_version = "2.1"; init.bind_generates_resource = true; InitDecoder(init); DoBindRenderbuffer( GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId); EXPECT_CALL(*gl_, RenderbufferStorageMultisampleEXT(_, _, _, _, _)) .Times(0) .RetiresOnSaturation(); RenderbufferStorageMultisampleCHROMIUM cmd; cmd.Init(GL_RENDERBUFFER, TestHelper::kMaxSamples + 1, GL_RGBA4, TestHelper::kMaxRenderbufferSize, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); cmd.Init(GL_RENDERBUFFER, TestHelper::kMaxSamples, GL_RGBA4, TestHelper::kMaxRenderbufferSize + 1, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); cmd.Init(GL_RENDERBUFFER, TestHelper::kMaxSamples, GL_RGBA4, 1, TestHelper::kMaxRenderbufferSize + 1); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); } TEST_P(GLES2DecoderManualInitTest, RenderbufferStorageMultisampleCHROMIUM) { InitState init; init.extensions = "GL_EXT_framebuffer_multisample"; init.gl_version = "2.1"; InitDecoder(init); DoBindRenderbuffer( GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId); InSequence sequence; EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL( *gl_, RenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, TestHelper::kMaxSamples, GL_RGBA, TestHelper::kMaxRenderbufferSize, 1)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); RenderbufferStorageMultisampleCHROMIUM cmd; cmd.Init(GL_RENDERBUFFER, TestHelper::kMaxSamples, GL_RGBA4, TestHelper::kMaxRenderbufferSize, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } TEST_P(GLES2DecoderManualInitTest, RenderbufferStorageMultisampleEXTNotSupported) { InitState init; init.extensions = "GL_EXT_framebuffer_multisample"; init.gl_version = "2.1"; init.bind_generates_resource = true; InitDecoder(init); DoBindRenderbuffer( GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId); InSequence sequence; // GL_EXT_framebuffer_multisample uses RenderbufferStorageMultisampleCHROMIUM. RenderbufferStorageMultisampleEXT cmd; cmd.Init(GL_RENDERBUFFER, TestHelper::kMaxSamples, GL_RGBA4, TestHelper::kMaxRenderbufferSize, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } class GLES2DecoderMultisampledRenderToTextureTest : public GLES2DecoderTestWithExtensionsOnGLES2 { public: void TestNotCompatibleWithRenderbufferStorageMultisampleCHROMIUM() { DoBindRenderbuffer( GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId); RenderbufferStorageMultisampleCHROMIUM cmd; cmd.Init(GL_RENDERBUFFER, TestHelper::kMaxSamples, GL_RGBA4, TestHelper::kMaxRenderbufferSize, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } void TestRenderbufferStorageMultisampleEXT(const char* extension) { DoBindRenderbuffer( GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId); InSequence sequence; EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); if (strstr(extension, "GL_IMG_multisampled_render_to_texture")) { EXPECT_CALL( *gl_, RenderbufferStorageMultisampleIMG(GL_RENDERBUFFER, TestHelper::kMaxSamples, GL_RGBA, TestHelper::kMaxRenderbufferSize, 1)) .Times(1) .RetiresOnSaturation(); } else { EXPECT_CALL( *gl_, RenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, TestHelper::kMaxSamples, GL_RGBA, TestHelper::kMaxRenderbufferSize, 1)) .Times(1) .RetiresOnSaturation(); } EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); RenderbufferStorageMultisampleEXT cmd; cmd.Init(GL_RENDERBUFFER, TestHelper::kMaxSamples, GL_RGBA4, TestHelper::kMaxRenderbufferSize, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } }; INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderMultisampledRenderToTextureTest, ::testing::Bool()); TEST_P(GLES2DecoderMultisampledRenderToTextureTest, NotCompatibleWithRenderbufferStorageMultisampleCHROMIUM_EXT) { Init("GL_EXT_multisampled_render_to_texture"); TestNotCompatibleWithRenderbufferStorageMultisampleCHROMIUM(); } TEST_P(GLES2DecoderMultisampledRenderToTextureTest, NotCompatibleWithRenderbufferStorageMultisampleCHROMIUM_IMG) { Init("GL_IMG_multisampled_render_to_texture"); TestNotCompatibleWithRenderbufferStorageMultisampleCHROMIUM(); } TEST_P(GLES2DecoderMultisampledRenderToTextureTest, RenderbufferStorageMultisampleEXT_EXT) { Init("GL_EXT_multisampled_render_to_texture"); TestRenderbufferStorageMultisampleEXT( "GL_EXT_multisampled_render_to_texture"); } TEST_P(GLES2DecoderMultisampledRenderToTextureTest, RenderbufferStorageMultisampleEXT_IMG) { Init("GL_IMG_multisampled_render_to_texture"); TestRenderbufferStorageMultisampleEXT( "GL_IMG_multisampled_render_to_texture"); } TEST_P(GLES2DecoderTest, ReadPixelsGLError) { GLenum kFormat = GL_RGBA; GLint x = 0; GLint y = 0; GLsizei width = 2; GLsizei height = 4; typedef ReadPixels::Result Result; Result* result = GetSharedMemoryAs(); uint32 result_shm_id = kSharedMemoryId; uint32 result_shm_offset = kSharedMemoryOffset; uint32 pixels_shm_id = kSharedMemoryId; uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(*result); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_OUT_OF_MEMORY)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, ReadPixels(x, y, width, height, kFormat, GL_UNSIGNED_BYTE, _)) .Times(1) .RetiresOnSaturation(); ReadPixels cmd; cmd.Init(x, y, width, height, kFormat, GL_UNSIGNED_BYTE, pixels_shm_id, pixels_shm_offset, result_shm_id, result_shm_offset, false); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError()); } TEST_P(GLES2DecoderWithShaderTest, UnClearedAttachmentsGetClearedOnClear) { const GLuint kFBOClientTextureId = 4100; const GLuint kFBOServiceTextureId = 4101; // Register a texture id. EXPECT_CALL(*gl_, GenTextures(_, _)) .WillOnce(SetArgumentPointee<1>(kFBOServiceTextureId)) .RetiresOnSaturation(); GenHelper(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(); SetupExpectationsForFramebufferClearing(GL_FRAMEBUFFER, // target GL_COLOR_BUFFER_BIT, // clear bits 0, 0, 0, 0, // color 0, // stencil 1.0f, // depth false); // scissor test 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_, Clear(GL_COLOR_BUFFER_BIT)).Times(1).RetiresOnSaturation(); Clear cmd; cmd.Init(GL_COLOR_BUFFER_BIT); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } TEST_P(GLES2DecoderWithShaderTest, UnClearedAttachmentsGetClearedOnReadPixels) { const GLuint kFBOClientTextureId = 4100; const GLuint kFBOServiceTextureId = 4101; // Register a texture id. EXPECT_CALL(*gl_, GenTextures(_, _)) .WillOnce(SetArgumentPointee<1>(kFBOServiceTextureId)) .RetiresOnSaturation(); GenHelper(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(); SetupExpectationsForFramebufferClearing(GL_FRAMEBUFFER, // target GL_COLOR_BUFFER_BIT, // clear bits 0, 0, 0, 0, // color 0, // stencil 1.0f, // depth false); // scissor test EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, ReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, _)) .Times(1) .RetiresOnSaturation(); typedef ReadPixels::Result Result; Result* result = GetSharedMemoryAs(); uint32 result_shm_id = kSharedMemoryId; uint32 result_shm_offset = kSharedMemoryOffset; uint32 pixels_shm_id = kSharedMemoryId; uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(*result); ReadPixels cmd; cmd.Init(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels_shm_id, pixels_shm_offset, result_shm_id, result_shm_offset, false); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } TEST_P(GLES2DecoderManualInitTest, UnClearedAttachmentsGetClearedOnReadPixelsAndDrawBufferGetsRestored) { InitState init; init.extensions = "GL_EXT_framebuffer_multisample"; init.gl_version = "2.1"; init.bind_generates_resource = true; InitDecoder(init); const GLuint kFBOClientTextureId = 4100; const GLuint kFBOServiceTextureId = 4101; // Register a texture id. EXPECT_CALL(*gl_, GenTextures(_, _)) .WillOnce(SetArgumentPointee<1>(kFBOServiceTextureId)) .RetiresOnSaturation(); GenHelper(kFBOClientTextureId); // Setup "render from" 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_READ_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); DoFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId, 0, GL_NO_ERROR); // Enable GL_SCISSOR_TEST to make sure we disable it in the clear, // then re-enable after. DoEnableDisable(GL_SCISSOR_TEST, true); SetupExpectationsForFramebufferClearingMulti( kServiceFramebufferId, // read framebuffer service id 0, // backbuffer service id GL_READ_FRAMEBUFFER, // target GL_COLOR_BUFFER_BIT, // clear bits 0, 0, 0, 0, // color 0, // stencil 1.0f, // depth true); // scissor test EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, ReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, _)) .Times(1) .RetiresOnSaturation(); typedef ReadPixels::Result Result; uint32 result_shm_id = kSharedMemoryId; uint32 result_shm_offset = kSharedMemoryOffset; uint32 pixels_shm_id = kSharedMemoryId; uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(Result); ReadPixels cmd; cmd.Init(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels_shm_id, pixels_shm_offset, result_shm_id, result_shm_offset, false); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } TEST_P(GLES2DecoderWithShaderTest, CopyTexImageWithInCompleteFBOFails) { GLenum target = GL_TEXTURE_2D; GLint level = 0; GLenum internal_format = GL_RGBA; GLsizei width = 2; GLsizei height = 4; SetupTexture(); DoBindRenderbuffer( GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId); DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); DoRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, GL_RGBA, 0, 0, GL_NO_ERROR); DoFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId, GL_NO_ERROR); EXPECT_CALL(*gl_, CopyTexImage2D(_, _, _, _, _, _, _, _)) .Times(0) .RetiresOnSaturation(); CopyTexImage2D cmd; cmd.Init(target, level, internal_format, 0, 0, width, height); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_FRAMEBUFFER_OPERATION, GetGLError()); } void GLES2DecoderWithShaderTest::CheckRenderbufferChangesMarkFBOAsNotComplete( bool bound_fbo) { FramebufferManager* framebuffer_manager = group().framebuffer_manager(); SetupTexture(); DoBindRenderbuffer( GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId); DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); DoRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, GL_RGBA, 1, 1, GL_NO_ERROR); DoFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId, GL_NO_ERROR); if (!bound_fbo) { DoBindFramebuffer(GL_FRAMEBUFFER, 0, 0); } Framebuffer* framebuffer = framebuffer_manager->GetFramebuffer(client_framebuffer_id_); ASSERT_TRUE(framebuffer != NULL); framebuffer_manager->MarkAsComplete(framebuffer); EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer)); // Test that renderbufferStorage marks fbo as not complete. DoRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, GL_RGBA, 1, 1, GL_NO_ERROR); EXPECT_FALSE(framebuffer_manager->IsComplete(framebuffer)); framebuffer_manager->MarkAsComplete(framebuffer); EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer)); // Test deleting renderbuffer marks fbo as not complete. DoDeleteRenderbuffer(client_renderbuffer_id_, kServiceRenderbufferId); if (bound_fbo) { EXPECT_FALSE(framebuffer_manager->IsComplete(framebuffer)); } else { EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer)); } // Cleanup DoDeleteFramebuffer(client_framebuffer_id_, kServiceFramebufferId, bound_fbo, GL_FRAMEBUFFER, 0, bound_fbo, GL_FRAMEBUFFER, 0); } TEST_P(GLES2DecoderWithShaderTest, RenderbufferChangesMarkFBOAsNotCompleteBoundFBO) { CheckRenderbufferChangesMarkFBOAsNotComplete(true); } TEST_P(GLES2DecoderWithShaderTest, RenderbufferChangesMarkFBOAsNotCompleteUnboundFBO) { CheckRenderbufferChangesMarkFBOAsNotComplete(false); } void GLES2DecoderWithShaderTest::CheckTextureChangesMarkFBOAsNotComplete( bool bound_fbo) { FramebufferManager* framebuffer_manager = group().framebuffer_manager(); const GLuint kFBOClientTextureId = 4100; const GLuint kFBOServiceTextureId = 4101; // Register a texture id. EXPECT_CALL(*gl_, GenTextures(_, _)) .WillOnce(SetArgumentPointee<1>(kFBOServiceTextureId)) .RetiresOnSaturation(); GenHelper(kFBOClientTextureId); SetupTexture(); // 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, GL_DEPTH_COMPONENT, 1, 1, GL_NO_ERROR); DoFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId, GL_NO_ERROR); if (!bound_fbo) { DoBindFramebuffer(GL_FRAMEBUFFER, 0, 0); } Framebuffer* framebuffer = framebuffer_manager->GetFramebuffer(client_framebuffer_id_); ASSERT_TRUE(framebuffer != NULL); framebuffer_manager->MarkAsComplete(framebuffer); EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer)); // Test TexImage2D marks fbo as not complete. DoTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, 0, 0); EXPECT_FALSE(framebuffer_manager->IsComplete(framebuffer)); framebuffer_manager->MarkAsComplete(framebuffer); EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer)); // Test CopyImage2D marks fbo as not complete. EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, CopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 1, 1, 0)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); CopyTexImage2D cmd; cmd.Init(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 1, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_FALSE(framebuffer_manager->IsComplete(framebuffer)); // Test deleting texture marks fbo as not complete. framebuffer_manager->MarkAsComplete(framebuffer); EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer)); DoDeleteTexture(kFBOClientTextureId, kFBOServiceTextureId); if (bound_fbo) { EXPECT_FALSE(framebuffer_manager->IsComplete(framebuffer)); } else { EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer)); } // Cleanup DoDeleteFramebuffer(client_framebuffer_id_, kServiceFramebufferId, bound_fbo, GL_FRAMEBUFFER, 0, bound_fbo, GL_FRAMEBUFFER, 0); } TEST_P(GLES2DecoderWithShaderTest, TextureChangesMarkFBOAsNotCompleteBoundFBO) { CheckTextureChangesMarkFBOAsNotComplete(true); } TEST_P(GLES2DecoderWithShaderTest, TextureChangesMarkFBOAsNotCompleteUnboundFBO) { CheckTextureChangesMarkFBOAsNotComplete(false); } TEST_P(GLES2DecoderTest, CanChangeSurface) { scoped_refptr other_surface(new GLSurfaceMock); EXPECT_CALL(*other_surface.get(), GetBackingFrameBufferObject()) .WillOnce(Return(7)); EXPECT_CALL(*gl_, BindFramebufferEXT(GL_FRAMEBUFFER_EXT, 7)); decoder_->SetSurface(other_surface); } TEST_P(GLES2DecoderTest, DrawBuffersEXTImmediateSuccceeds) { const GLsizei count = 1; const GLenum bufs[] = {GL_COLOR_ATTACHMENT0}; DrawBuffersEXTImmediate& cmd = *GetImmediateAs(); cmd.Init(count, bufs); DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); EXPECT_CALL(*gl_, DrawBuffersARB(count, _)).Times(1).RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(bufs))); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } TEST_P(GLES2DecoderTest, DrawBuffersEXTImmediateFails) { const GLsizei count = 1; const GLenum bufs[] = {GL_COLOR_ATTACHMENT1_EXT}; DrawBuffersEXTImmediate& cmd = *GetImmediateAs(); cmd.Init(count, bufs); DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(bufs))); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } TEST_P(GLES2DecoderTest, DrawBuffersEXTImmediateBackbuffer) { const GLsizei count = 1; const GLenum bufs[] = {GL_BACK}; DrawBuffersEXTImmediate& cmd = *GetImmediateAs(); cmd.Init(count, bufs); DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(bufs))); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); DoBindFramebuffer(GL_FRAMEBUFFER, 0, 0); // unbind EXPECT_CALL(*gl_, DrawBuffersARB(count, _)).Times(1).RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(bufs))); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } TEST_P(GLES2DecoderManualInitTest, InvalidateFramebufferBinding) { InitState init; init.gl_version = "opengl es 3.0"; InitDecoder(init); // EXPECT_EQ can't be used to compare function pointers EXPECT_TRUE( gfx::MockGLInterface::GetGLProcAddress("glInvalidateFramebuffer") == gfx::g_driver_gl.fn.glDiscardFramebufferEXTFn); EXPECT_TRUE( gfx::MockGLInterface::GetGLProcAddress("glInvalidateFramebuffer") != gfx::MockGLInterface::GetGLProcAddress("glDiscardFramebufferEXT")); } TEST_P(GLES2DecoderManualInitTest, DiscardFramebufferEXT) { InitState init; init.extensions = "GL_EXT_discard_framebuffer"; init.gl_version = "opengl es 2.0"; InitDecoder(init); // EXPECT_EQ can't be used to compare function pointers EXPECT_TRUE( gfx::MockGLInterface::GetGLProcAddress("glDiscardFramebufferEXT") == gfx::g_driver_gl.fn.glDiscardFramebufferEXTFn); const GLenum target = GL_FRAMEBUFFER; const GLsizei count = 1; const GLenum attachments[] = {GL_COLOR_ATTACHMENT0}; SetupTexture(); DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); DoFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, client_texture_id_, kServiceTextureId, 0, GL_NO_ERROR); FramebufferManager* framebuffer_manager = group().framebuffer_manager(); Framebuffer* framebuffer = framebuffer_manager->GetFramebuffer(client_framebuffer_id_); EXPECT_TRUE(framebuffer->IsCleared()); EXPECT_CALL(*gl_, DiscardFramebufferEXT(target, count, _)) .Times(1) .RetiresOnSaturation(); DiscardFramebufferEXTImmediate& cmd = *GetImmediateAs(); cmd.Init(target, count, attachments); EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(attachments))); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_FALSE(framebuffer->IsCleared()); } TEST_P(GLES2DecoderTest, DiscardFramebufferEXTUnsupported) { const GLenum target = GL_FRAMEBUFFER; const GLsizei count = 1; const GLenum attachments[] = {GL_COLOR_EXT}; DiscardFramebufferEXTImmediate& cmd = *GetImmediateAs(); cmd.Init(target, count, attachments); // Should not result into a call into GL. EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(attachments))); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } TEST_P(GLES2DecoderManualInitTest, DiscardedAttachmentsEXTMarksFramebufferIncomplete) { InitState init; init.extensions = "GL_EXT_discard_framebuffer"; init.gl_version = "opengl es 2.0"; init.has_alpha = true; init.bind_generates_resource = true; InitDecoder(init); const GLuint kFBOClientTextureId = 4100; const GLuint kFBOServiceTextureId = 4101; // Register a texture id. EXPECT_CALL(*gl_, GenTextures(_, _)) .WillOnce(SetArgumentPointee<1>(kFBOServiceTextureId)) .RetiresOnSaturation(); GenHelper(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(); SetupExpectationsForFramebufferClearing(GL_FRAMEBUFFER, // target GL_COLOR_BUFFER_BIT, // clear bits 0, 0, 0, 0, // color 0, // stencil 1.0f, // depth false); // scissor test 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_, Clear(GL_COLOR_BUFFER_BIT)).Times(1).RetiresOnSaturation(); Clear clear_cmd; clear_cmd.Init(GL_COLOR_BUFFER_BIT); EXPECT_EQ(error::kNoError, ExecuteCmd(clear_cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); // Check that framebuffer is cleared and complete. FramebufferManager* framebuffer_manager = group().framebuffer_manager(); Framebuffer* framebuffer = framebuffer_manager->GetFramebuffer(client_framebuffer_id_); EXPECT_TRUE(framebuffer->IsCleared()); EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer)); // Check that Discard GL_COLOR_ATTACHMENT0, sets the attachment as uncleared // and the framebuffer as incomplete. EXPECT_TRUE( gfx::MockGLInterface::GetGLProcAddress("glDiscardFramebufferEXT") == gfx::g_driver_gl.fn.glDiscardFramebufferEXTFn); const GLenum target = GL_FRAMEBUFFER; const GLsizei count = 1; const GLenum attachments[] = {GL_COLOR_ATTACHMENT0}; DiscardFramebufferEXTImmediate& discard_cmd = *GetImmediateAs(); discard_cmd.Init(target, count, attachments); EXPECT_CALL(*gl_, DiscardFramebufferEXT(target, count, _)) .Times(1) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(discard_cmd, sizeof(attachments))); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_FALSE(framebuffer->IsCleared()); EXPECT_FALSE(framebuffer_manager->IsComplete(framebuffer)); } TEST_P(GLES2DecoderManualInitTest, ReadFormatExtension) { InitState init; init.extensions = "GL_OES_read_format"; init.gl_version = "2.1"; init.bind_generates_resource = true; InitDecoder(init); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()).Times(6).RetiresOnSaturation(); typedef GetIntegerv::Result Result; Result* result = static_cast(shared_memory_address_); GetIntegerv cmd; const GLuint kFBOClientTextureId = 4100; const GLuint kFBOServiceTextureId = 4101; // Register a texture id. EXPECT_CALL(*gl_, GenTextures(_, _)) .WillOnce(SetArgumentPointee<1>(kFBOServiceTextureId)) .RetiresOnSaturation(); GenHelper(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); result->size = 0; EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(1).RetiresOnSaturation(); cmd.Init(GL_IMPLEMENTATION_COLOR_READ_FORMAT, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(1, result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); result->size = 0; EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(1).RetiresOnSaturation(); cmd.Init(GL_IMPLEMENTATION_COLOR_READ_TYPE, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(1, result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } TEST_P(GLES2DecoderManualInitTest, NoReadFormatExtension) { InitState init; init.gl_version = "2.1"; init.bind_generates_resource = true; InitDecoder(init); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); typedef GetIntegerv::Result Result; Result* result = static_cast(shared_memory_address_); GetIntegerv cmd; const GLuint kFBOClientTextureId = 4100; const GLuint kFBOServiceTextureId = 4101; // Register a texture id. EXPECT_CALL(*gl_, GenTextures(_, _)) .WillOnce(SetArgumentPointee<1>(kFBOServiceTextureId)) .RetiresOnSaturation(); GenHelper(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); result->size = 0; EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(0).RetiresOnSaturation(); cmd.Init(GL_IMPLEMENTATION_COLOR_READ_FORMAT, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(1, result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); result->size = 0; EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(0).RetiresOnSaturation(); cmd.Init(GL_IMPLEMENTATION_COLOR_READ_TYPE, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(1, result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } // TODO(gman): PixelStorei // TODO(gman): SwapBuffers } // namespace gles2 } // namespace gpu