summaryrefslogtreecommitdiffstats
path: root/gpu
diff options
context:
space:
mode:
authorgman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-19 01:57:56 +0000
committergman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-19 01:57:56 +0000
commit57f2238364b33696c4ee2d294c51cc317edf0ec0 (patch)
tree31a2d0b9d23bdb5d29065cb520eb09171c6f4da7 /gpu
parentaee541ebc97e1ff2c8bb43e4375913f582931d40 (diff)
downloadchromium_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.cc141
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc315
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h1
-rw-r--r--gpu/command_buffer/service/texture_manager.cc15
-rw-r--r--gpu/command_buffer/service/texture_manager.h5
-rw-r--r--gpu/command_buffer/service/texture_manager_unittest.cc17
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