diff options
author | gman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-19 01:57:56 +0000 |
---|---|---|
committer | gman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-19 01:57:56 +0000 |
commit | 57f2238364b33696c4ee2d294c51cc317edf0ec0 (patch) | |
tree | 31a2d0b9d23bdb5d29065cb520eb09171c6f4da7 /gpu | |
parent | aee541ebc97e1ff2c8bb43e4375913f582931d40 (diff) | |
download | chromium_src-57f2238364b33696c4ee2d294c51cc317edf0ec0.zip chromium_src-57f2238364b33696c4ee2d294c51cc317edf0ec0.tar.gz chromium_src-57f2238364b33696c4ee2d294c51cc317edf0ec0.tar.bz2 |
Made glReadPixels handle out of range areas.
TEST=unit tests
BUG=none
Review URL: http://codereview.chromium.org/1081006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42065 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu')
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder.cc | 141 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc | 315 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h | 1 | ||||
-rw-r--r-- | gpu/command_buffer/service/texture_manager.cc | 15 | ||||
-rw-r--r-- | gpu/command_buffer/service/texture_manager.h | 5 | ||||
-rw-r--r-- | gpu/command_buffer/service/texture_manager_unittest.cc | 17 |
6 files changed, 486 insertions, 8 deletions
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 47f6609..441ffa1 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -1972,7 +1972,10 @@ error::Error GLES2DecoderImpl::HandleReadPixels( GLsizei height = c.height; GLenum format = c.format; GLenum type = c.type; - // TODO(gman): Handle out of range rectangles. + if (width < 0 || height < 0) { + SetGLError(GL_INVALID_VALUE); + return error::kNoError; + } typedef gles2::ReadPixels::Result Result; uint32 pixels_size; if (!GLES2Util::ComputeImageDataSize( @@ -1992,12 +1995,142 @@ error::Error GLES2DecoderImpl::HandleReadPixels( SetGLError(GL_INVALID_ENUM); return error::kNoError; } - if (width < 0 || height < 0) { - SetGLError(GL_INVALID_VALUE); + if (width == 0 || height == 0) { return error::kNoError; } + CopyRealGLErrorsToWrapper(); - glReadPixels(x, y, width, height, format, type, pixels); + + // Get the size of the current fbo or backbuffer. + GLsizei max_width = 0; + GLsizei max_height = 0; + if (bound_framebuffer_ != 0) { + // Assume we have to have COLOR_ATTACHMENT0. Should we check for depth and + // stencil. + GLint fb_type = 0; + glGetFramebufferAttachmentParameterivEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, + &fb_type); + switch (fb_type) { + case GL_RENDERBUFFER: + { + GLint renderbuffer_id = 0; + glGetFramebufferAttachmentParameterivEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + &renderbuffer_id); + if (renderbuffer_id != 0) { + glGetRenderbufferParameterivEXT( + GL_RENDERBUFFER, + GL_RENDERBUFFER_WIDTH, + &max_width); + glGetRenderbufferParameterivEXT( + GL_RENDERBUFFER, + GL_RENDERBUFFER_HEIGHT, + &max_height); + } + break; + } + case GL_TEXTURE: + { + GLint texture_id = 0; + glGetFramebufferAttachmentParameterivEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + &texture_id); + if (texture_id != 0) { + TextureManager::TextureInfo* texture_info = + GetTextureInfo(texture_id); + if (texture_info) { + GLint level = 0; + GLint face = 0; + glGetFramebufferAttachmentParameterivEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, + &level); + glGetFramebufferAttachmentParameterivEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE, + &face); + texture_info->GetLevelSize( + face ? face : GL_TEXTURE_2D, level, &max_width, &max_height); + } + } + break; + } + default: + // unknown so assume max_width = 0. + break; + } + } else { + // TODO(gman): Get these values from the proper place. + max_width = 300; + max_height = 150; + } + + GLint max_x; + GLint max_y; + if (!SafeAdd(x, width, &max_x) || !SafeAdd(y, height, &max_y)) { + SetGLError(GL_INVALID_VALUE); + return error::kNoError; + } + + if (x < 0 || y < 0 || max_x > max_width || max_y > max_height) { + // The user requested an out of range area. Get the results 1 line + // at a time. + uint32 temp_size; + if (!GLES2Util::ComputeImageDataSize( + width, 1, format, type, pack_alignment_, &temp_size)) { + SetGLError(GL_INVALID_VALUE); + return error::kNoError; + } + GLsizei unpadded_row_size = temp_size; + if (!GLES2Util::ComputeImageDataSize( + width, 2, format, type, pack_alignment_, &temp_size)) { + SetGLError(GL_INVALID_VALUE); + return error::kNoError; + } + GLsizei padded_row_size = temp_size - unpadded_row_size; + if (padded_row_size < 0 || unpadded_row_size < 0) { + SetGLError(GL_INVALID_VALUE); + return error::kNoError; + } + + GLint dest_x_offset = std::max(-x, 0); + uint32 dest_row_offset; + if (!GLES2Util::ComputeImageDataSize( + dest_x_offset, 1, format, type, pack_alignment_, &dest_row_offset)) { + SetGLError(GL_INVALID_VALUE); + return error::kNoError; + } + + // Copy each row into the larger dest rect. + int8* dst = static_cast<int8*>(pixels); + GLint read_x = std::max(0, x); + GLint read_end_x = std::max(0, std::min(max_width, max_x)); + GLint read_width = read_end_x - read_x; + for (GLint yy = 0; yy < height; ++yy) { + GLint ry = y + yy; + + // Clear the row. + memset(dst, 0, unpadded_row_size); + + // If the row is in range, copy it. + if (ry >= 0 && ry < max_height && read_width > 0) { + glReadPixels( + read_x, ry, read_width, 1, format, type, dst + dest_row_offset); + } + dst += padded_row_size; + } + } else { + glReadPixels(x, y, width, height, format, type, pixels); + } GLenum error = glGetError(); if (error == GL_NO_ERROR) { *result = true; diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc index 5653e57..dc68d4e 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc @@ -16,6 +16,7 @@ using ::gles2::MockGLInterface; using ::testing::_; using ::testing::DoAll; using ::testing::InSequence; +using ::testing::Invoke; using ::testing::MatcherCast; using ::testing::Pointee; using ::testing::Return; @@ -30,6 +31,12 @@ namespace gles2 { class GLES2DecoderTest : public GLES2DecoderTestBase { public: GLES2DecoderTest() { } + + protected: + void CheckReadPixelsOutOfRange( + GLint in_read_x, GLint in_read_y, + GLsizei in_read_width, GLsizei in_read_height, + bool init); }; class GLES2DecoderWithShaderTest : public GLES2DecoderWithShaderTestBase { @@ -1109,6 +1116,312 @@ TEST_F(GLES2DecoderTest, RenderbufferStorageWithNoBoundTarget) { 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* pixels, GLint pack_alignment) + : width_(width), + height_(height), + pack_alignment_(pack_alignment), + bytes_per_pixel_(bytes_per_pixel), + pixels_(reinterpret_cast<const int8*>(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(x, y + yy); + const void* dst = ComputePackAlignmentAddress(0, yy, width, pixels); + memcpy(const_cast<void*>(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(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<const int8*>(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_; + 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(GLint x, GLint y) const { + return pixels_ + (width_ * y + x) * bytes_per_pixel_; + } + + GLint pack_alignment_; + GLint bytes_per_pixel_; + GLsizei width_; + GLsizei height_; + const int8* 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, 0, 0); + DoBindFramebuffer(GL_FRAMEBUFFER, client_framebuffer_id_, + kServiceFramebufferId); + } + + // We need to tell our mock GL to return the info about our FBO. + EXPECT_CALL(*gl_, GetFramebufferAttachmentParameterivEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, + _)) + .WillOnce(SetArgumentPointee<3>(GL_TEXTURE)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, GetFramebufferAttachmentParameterivEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + _)) + .WillOnce(SetArgumentPointee<3>(kServiceTextureId)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, GetFramebufferAttachmentParameterivEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, + _)) + .WillOnce(SetArgumentPointee<3>(0)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, GetFramebufferAttachmentParameterivEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE, + _)) + .WillOnce(SetArgumentPointee<3>(0)) + .RetiresOnSaturation(); + + ReadPixelsEmulator emu( + kWidth, kHeight, kBytesPerPixel, kSrcPixels, kPackAlignment); + typedef ReadPixels::Result Result; + Result* result = GetSharedMemoryAs<Result*>(); + 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); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + + GLint unpadded_row_size = emu.ComputeImageDataSize(in_read_width, 1); + scoped_array<int8> zero(new int8[unpadded_row_size]); + scoped_array<int8> 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<const int8*>( + 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_F(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, + }; + + ReadPixelsEmulator emu( + kWidth, kHeight, kBytesPerPixel, kSrcPixels, kPackAlignment); + typedef ReadPixels::Result Result; + Result* result = GetSharedMemoryAs<Result*>(); + 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); + 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_F(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_F(GLES2DecoderTest, ReadPixelsInvalidArgs) { + typedef ReadPixels::Result Result; + Result* result = GetSharedMemoryAs<Result*>(); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); +} + // TODO(gman): BindAttribLocation // TODO(gman): BindAttribLocationImmediate @@ -1143,8 +1456,6 @@ TEST_F(GLES2DecoderTest, RenderbufferStorageWithNoBoundTarget) { // TODO(gman): PixelStorei -// TODO(gman): ReadPixels - // TODO(gman): TexImage2D // TODO(gman): TexImage2DImmediate diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h index 4408e64..96dc2d9 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h @@ -134,7 +134,6 @@ class GLES2DecoderTestBase : public testing::Test { void DoBindFramebuffer(GLenum target, GLuint client_id, GLuint service_id); void DoBindRenderbuffer(GLenum target, GLuint client_id, GLuint service_id); void DoBindTexture(GLenum target, GLuint client_id, GLuint service_id); - void DoTexImage2D(GLenum target, GLint level, GLenum internal_format, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc index a2f72bb..d6a1dce 100644 --- a/gpu/command_buffer/service/texture_manager.cc +++ b/gpu/command_buffer/service/texture_manager.cc @@ -108,7 +108,7 @@ bool TextureManager::TextureInfo::MarkMipmapsGenerated() { } bool TextureManager::TextureInfo::CanGenerateMipmaps() const { - if (npot() || level_infos_.empty()) { + if (npot() || level_infos_.empty() || IsDeleted()) { return false; } const TextureInfo::LevelInfo& first = level_infos_[0][0]; @@ -160,6 +160,19 @@ void TextureManager::TextureInfo::SetLevelInfo( Update(); } +bool TextureManager::TextureInfo::GetLevelSize( + GLint face, GLint level, GLsizei* width, GLsizei* height) const { + size_t face_index = GLTargetToFaceIndex(face); + if (!IsDeleted() && level >= 0 && face_index < level_infos_.size() && + static_cast<size_t>(level) < level_infos_[face_index].size()) { + const LevelInfo& info = level_infos_[GLTargetToFaceIndex(face)][level]; + *width = info.width; + *height = info.height; + return true; + } + return false; +} + void TextureManager::TextureInfo::SetParameter(GLenum pname, GLint param) { switch (pname) { case GL_TEXTURE_MIN_FILTER: diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h index 6407721..358cbbc 100644 --- a/gpu/command_buffer/service/texture_manager.h +++ b/gpu/command_buffer/service/texture_manager.h @@ -92,6 +92,11 @@ class TextureManager { GLenum format, GLenum type); + // Get the width and height for a particular level. Returns false if level + // does not exist. + bool GetLevelSize( + GLint face, GLint level, GLsizei* width, GLsizei* height) const; + // Sets a texture parameter. // TODO(gman): Expand to SetParameteri,f,iv,fv void SetParameter(GLenum pname, GLint param); diff --git a/gpu/command_buffer/service/texture_manager_unittest.cc b/gpu/command_buffer/service/texture_manager_unittest.cc index dc74605..d5c393c 100644 --- a/gpu/command_buffer/service/texture_manager_unittest.cc +++ b/gpu/command_buffer/service/texture_manager_unittest.cc @@ -274,6 +274,23 @@ TEST_F(TextureInfoTest, POTCubeMap) { EXPECT_TRUE(info_->cube_complete()); } +TEST_F(TextureInfoTest, GetLevelSize) { + manager_.SetInfoTarget(info_, GL_TEXTURE_2D); + info_->SetLevelInfo( + GL_TEXTURE_2D, 1, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + GLsizei width = -1; + GLsizei height = -1; + EXPECT_FALSE(info_->GetLevelSize(GL_TEXTURE_2D, -1, &width, &height)); + EXPECT_FALSE(info_->GetLevelSize(GL_TEXTURE_2D, 1000, &width, &height)); + EXPECT_TRUE(info_->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height)); + EXPECT_EQ(0, width); + EXPECT_EQ(0, height); + EXPECT_TRUE(info_->GetLevelSize(GL_TEXTURE_2D, 1, &width, &height)); + EXPECT_EQ(4, width); + EXPECT_EQ(5, height); + manager_.RemoveTextureInfo(info_->texture_id()); + EXPECT_FALSE(info_->GetLevelSize(GL_TEXTURE_2D, 1, &width, &height)); +} } // namespace gles2 } // namespace gpu |