diff options
author | geofflang@chromium.org <geofflang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-11 01:27:59 +0000 |
---|---|---|
committer | geofflang@chromium.org <geofflang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-11 01:27:59 +0000 |
commit | 685863794f0212c52238384fa033ff5b28d5ea7c (patch) | |
tree | e09fb17557448f83caac19944f3c6a3f81d6ad9e /gpu | |
parent | e97887cbd71dcc943b834c051f2c7719f76d7e53 (diff) | |
download | chromium_src-685863794f0212c52238384fa033ff5b28d5ea7c.zip chromium_src-685863794f0212c52238384fa033ff5b28d5ea7c.tar.gz chromium_src-685863794f0212c52238384fa033ff5b28d5ea7c.tar.bz2 |
Support GL_FLOAT and GL_HALF_FLOAT_OES as glReadPixels types when the gl context allows it.
BUG=314141
Review URL: https://codereview.chromium.org/89103002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@239932 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu')
-rw-r--r-- | gpu/command_buffer/common/gles2_cmd_utils.cc | 37 | ||||
-rw-r--r-- | gpu/command_buffer/common/gles2_cmd_utils.h | 5 | ||||
-rw-r--r-- | gpu/command_buffer/service/framebuffer_manager.cc | 21 | ||||
-rw-r--r-- | gpu/command_buffer/service/framebuffer_manager.h | 4 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder.cc | 105 | ||||
-rw-r--r-- | gpu/command_buffer/tests/gl_readback_unittests.cc | 233 |
6 files changed, 384 insertions, 21 deletions
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.cc b/gpu/command_buffer/common/gles2_cmd_utils.cc index abc3544..b7d5e52 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils.cc +++ b/gpu/command_buffer/common/gles2_cmd_utils.cc @@ -590,6 +590,43 @@ uint32 GLES2Util::IndexToGLFaceTarget(int index) { return faces[index]; } +uint32 GLES2Util::GetPreferredGLReadPixelsFormat(uint32 internal_format) { + switch (internal_format) { + case GL_RGB16F_EXT: + case GL_RGB32F_EXT: + return GL_RGB; + case GL_RGBA16F_EXT: + case GL_RGBA32F_EXT: + return GL_RGBA; + default: + return GL_RGBA; + } +} + +uint32 GLES2Util::GetPreferredGLReadPixelsType( + uint32 internal_format, uint32 texture_type) { + switch (internal_format) { + case GL_RGBA32F_EXT: + case GL_RGB32F_EXT: + return GL_FLOAT; + case GL_RGBA16F_EXT: + case GL_RGB16F_EXT: + return GL_HALF_FLOAT_OES; + case GL_RGBA: + case GL_RGB: + // Unsized internal format, check the type + switch (texture_type) { + case GL_FLOAT: + case GL_HALF_FLOAT_OES: + return GL_FLOAT; + default: + return GL_UNSIGNED_BYTE; + } + default: + return GL_UNSIGNED_BYTE; + } +} + uint32 GLES2Util::GetChannelsForFormat(int format) { switch (format) { case GL_ALPHA: diff --git a/gpu/command_buffer/common/gles2_cmd_utils.h b/gpu/command_buffer/common/gles2_cmd_utils.h index bc34304..59b5d96 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils.h +++ b/gpu/command_buffer/common/gles2_cmd_utils.h @@ -132,6 +132,11 @@ class GLES2_UTILS_EXPORT GLES2Util { static uint32 IndexToGLFaceTarget(int index); + static uint32 GetPreferredGLReadPixelsFormat(uint32 internal_format); + + static uint32 GetPreferredGLReadPixelsType( + uint32 internal_format, uint32 texture_type); + // Returns a bitmask for the channels the given format supports. // See ChannelBits. static uint32 GetChannelsForFormat(int format); diff --git a/gpu/command_buffer/service/framebuffer_manager.cc b/gpu/command_buffer/service/framebuffer_manager.cc index 7dd1d82..48e503c 100644 --- a/gpu/command_buffer/service/framebuffer_manager.cc +++ b/gpu/command_buffer/service/framebuffer_manager.cc @@ -60,6 +60,10 @@ class RenderbufferAttachment return renderbuffer_->internal_format(); } + virtual GLenum texture_type() const OVERRIDE { + return 0; + } + virtual GLsizei samples() const OVERRIDE { return renderbuffer_->samples(); } @@ -159,6 +163,14 @@ class TextureAttachment return temp_internal_format; } + virtual GLenum texture_type() const OVERRIDE { + GLenum temp_type = 0; + GLenum temp_internal_format = 0; + texture_ref_->texture()->GetLevelType( + target_, level_, &temp_type, &temp_internal_format); + return temp_type; + } + virtual GLsizei samples() const OVERRIDE { return samples_; } @@ -385,6 +397,15 @@ GLenum Framebuffer::GetColorAttachmentFormat() const { return attachment->internal_format(); } +GLenum Framebuffer::GetColorAttachmentTextureType() const { + AttachmentMap::const_iterator it = attachments_.find(GL_COLOR_ATTACHMENT0); + if (it == attachments_.end()) { + return 0; + } + const Attachment* attachment = it->second.get(); + return attachment->texture_type(); +} + GLenum Framebuffer::IsPossiblyComplete() const { if (attachments_.empty()) { return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; diff --git a/gpu/command_buffer/service/framebuffer_manager.h b/gpu/command_buffer/service/framebuffer_manager.h index 23fd1ba..c469bf87 100644 --- a/gpu/command_buffer/service/framebuffer_manager.h +++ b/gpu/command_buffer/service/framebuffer_manager.h @@ -32,6 +32,7 @@ class GPU_EXPORT Framebuffer : public base::RefCounted<Framebuffer> { virtual GLsizei width() const = 0; virtual GLsizei height() const = 0; virtual GLenum internal_format() const = 0; + virtual GLenum texture_type() const = 0; virtual GLsizei samples() const = 0; virtual GLuint object_name() const = 0; virtual bool cleared() const = 0; @@ -103,6 +104,9 @@ class GPU_EXPORT Framebuffer : public base::RefCounted<Framebuffer> { bool HasDepthAttachment() const; bool HasStencilAttachment() const; GLenum GetColorAttachmentFormat() const; + // If the color attachment is a texture, returns its type; otherwise, + // returns 0. + GLenum GetColorAttachmentTextureType() const; // Verify all the rules in OpenGL ES 2.0.25 4.4.5 are followed. // Returns GL_FRAMEBUFFER_COMPLETE if there are no reasons we know we can't diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index fd9c78e..575a1b5 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -763,6 +763,7 @@ class GLES2DecoderImpl : public GLES2Decoder, // Get the format of the currently bound frame buffer (either FBO or regular // back buffer) + GLenum GetBoundReadFrameBufferTextureType(); GLenum GetBoundReadFrameBufferInternalFormat(); GLenum GetBoundDrawFrameBufferInternalFormat(); @@ -3025,6 +3026,16 @@ gfx::Size GLES2DecoderImpl::GetBoundReadFrameBufferSize() { } } +GLenum GLES2DecoderImpl::GetBoundReadFrameBufferTextureType() { + Framebuffer* framebuffer = + GetFramebufferInfoForTarget(GL_READ_FRAMEBUFFER_EXT); + if (framebuffer != NULL) { + return framebuffer->GetColorAttachmentTextureType(); + } else { + return GL_UNSIGNED_BYTE; + } +} + GLenum GLES2DecoderImpl::GetBoundReadFrameBufferInternalFormat() { Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_READ_FRAMEBUFFER_EXT); @@ -4094,13 +4105,16 @@ bool GLES2DecoderImpl::GetHelper( case GL_IMPLEMENTATION_COLOR_READ_FORMAT: *num_written = 1; if (params) { - *params = GL_RGBA; // We don't support other formats. + *params = GLES2Util::GetPreferredGLReadPixelsFormat( + GetBoundReadFrameBufferInternalFormat()); } return true; case GL_IMPLEMENTATION_COLOR_READ_TYPE: *num_written = 1; if (params) { - *params = GL_UNSIGNED_BYTE; // We don't support other types. + *params = GLES2Util::GetPreferredGLReadPixelsType( + GetBoundReadFrameBufferInternalFormat(), + GetBoundReadFrameBufferTextureType()); } return true; case GL_MAX_FRAGMENT_UNIFORM_VECTORS: @@ -7078,6 +7092,29 @@ error::Error GLES2DecoderImpl::HandleVertexAttribDivisorANGLE( return error::kNoError; } +template <typename pixel_data_type> +static void WriteAlphaData( + void *pixels, uint32 row_count, uint32 channel_count, + uint32 alpha_channel_index, uint32 unpadded_row_size, + uint32 padded_row_size, pixel_data_type alpha_value) { + DCHECK_GT(channel_count, 0U); + DCHECK_EQ(unpadded_row_size % sizeof(pixel_data_type), 0U); + uint32 unpadded_row_size_in_elements = + unpadded_row_size / sizeof(pixel_data_type); + DCHECK_EQ(padded_row_size % sizeof(pixel_data_type), 0U); + uint32 padded_row_size_in_elements = + padded_row_size / sizeof(pixel_data_type); + pixel_data_type* dst = + static_cast<pixel_data_type*>(pixels) + alpha_channel_index; + for (uint32 yy = 0; yy < row_count; ++yy) { + pixel_data_type* end = dst + unpadded_row_size_in_elements; + for (pixel_data_type* d = dst; d < end; d += channel_count) { + *d = alpha_value; + } + dst += padded_row_size_in_elements; + } +} + void GLES2DecoderImpl::FinishReadPixels( const cmds::ReadPixels& c, GLuint buffer) { @@ -7146,30 +7183,40 @@ void GLES2DecoderImpl::FinishReadPixels( &unpadded_row_size, &padded_row_size)) { return; } - // NOTE: Assumes the type is GL_UNSIGNED_BYTE which was true at the time - // of this implementation. - if (type != GL_UNSIGNED_BYTE) { - return; - } + + uint32 channel_count = 0; + uint32 alpha_channel = 0; switch (format) { case GL_RGBA: case GL_BGRA_EXT: - case GL_ALPHA: { - int offset = (format == GL_ALPHA) ? 0 : 3; - int step = (format == GL_ALPHA) ? 1 : 4; - uint8* dst = static_cast<uint8*>(pixels) + offset; - for (GLint yy = 0; yy < height; ++yy) { - uint8* end = dst + unpadded_row_size; - for (uint8* d = dst; d < end; d += step) { - *d = 255; - } - dst += padded_row_size; - } + channel_count = 4; + alpha_channel = 3; break; - } - default: + case GL_ALPHA: + channel_count = 1; + alpha_channel = 0; break; } + + if (channel_count > 0) { + switch (type) { + case GL_UNSIGNED_BYTE: + WriteAlphaData<uint8>( + pixels, height, channel_count, alpha_channel, unpadded_row_size, + padded_row_size, 0xFF); + break; + case GL_FLOAT: + WriteAlphaData<float>( + pixels, height, channel_count, alpha_channel, unpadded_row_size, + padded_row_size, 1.0f); + break; + case GL_HALF_FLOAT: + WriteAlphaData<uint16>( + pixels, height, channel_count, alpha_channel, unpadded_row_size, + padded_row_size, 0x3C00); + break; + } + } } } @@ -7214,10 +7261,26 @@ error::Error GLES2DecoderImpl::HandleReadPixels( LOCAL_SET_GL_ERROR_INVALID_ENUM("glReadPixels", format, "format"); return error::kNoError; } - if (!validators_->pixel_type.IsValid(type)) { + if (!validators_->read_pixel_type.IsValid(type)) { LOCAL_SET_GL_ERROR_INVALID_ENUM("glReadPixels", type, "type"); return error::kNoError; } + if ((format != GL_RGBA && format != GL_BGRA_EXT && format != GL_RGB && + format != GL_ALPHA) || type != GL_UNSIGNED_BYTE) { + // format and type are acceptable enums but not guaranteed to be supported + // for this framebuffer. Have to ask gl if they are valid. + GLint preferred_format = 0; + DoGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &preferred_format); + GLint preferred_type = 0; + DoGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &preferred_type); + if (format != static_cast<GLenum>(preferred_format) || + type != static_cast<GLenum>(preferred_type)) { + LOCAL_SET_GL_ERROR( + GL_INVALID_OPERATION, "glReadPixels", "format and type incompatible " + "with the current read framebuffer"); + return error::kNoError; + } + } if (width == 0 || height == 0) { return error::kNoError; } diff --git a/gpu/command_buffer/tests/gl_readback_unittests.cc b/gpu/command_buffer/tests/gl_readback_unittests.cc index d4b5d3d..7f81220 100644 --- a/gpu/command_buffer/tests/gl_readback_unittests.cc +++ b/gpu/command_buffer/tests/gl_readback_unittests.cc @@ -6,6 +6,9 @@ #include <GLES2/gl2ext.h> #include <GLES2/gl2extchromium.h> +#include <cmath> + +#include "base/basictypes.h" #include "base/bind.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" @@ -86,4 +89,234 @@ TEST_F(GLReadbackTest, ReadPixelsWithPBOAndQuery) { GLTestHelper::CheckGLError("no errors", __LINE__); } +static float HalfToFloat32(uint16 value) { + int32 s = (value >> 15) & 0x00000001; + int32 e = (value >> 10) & 0x0000001f; + int32 m = value & 0x000003ff; + + if (e == 0) { + if (m == 0) { + uint32 result = s << 31; + return bit_cast<float>(result); + } else { + while (!(m & 0x00000400)) { + m <<= 1; + e -= 1; + } + + e += 1; + m &= ~0x00000400; + } + } else if (e == 31) { + if (m == 0) { + uint32 result = (s << 31) | 0x7f800000; + return bit_cast<float>(result); + } else { + uint32 result = (s << 31) | 0x7f800000 | (m << 13); + return bit_cast<float>(result); + } + } + + e = e + (127 - 15); + m = m << 13; + + uint32 result = (s << 31) | (e << 23) | m; + return bit_cast<float>(result); +} + +static GLuint CompileShader(GLenum type, const char *data) { + const char *shaderStrings[1] = { data }; + + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, shaderStrings, NULL); + glCompileShader(shader); + + GLint compile_status = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); + if (compile_status != GL_TRUE) { + glDeleteShader(shader); + shader = 0; + } + + return shader; +} + +TEST_F(GLReadbackTest, ReadPixelsFloat) { + const GLsizei kTextureSize = 4; + const GLfloat kDrawColor[4] = { -10.9f, 0.5f, 10.5f, 100.12f }; + const GLfloat kEpsilon = 0.01f; + + struct TestFormat { + GLint format; + GLint type; + uint32 comp_count; + }; + TestFormat test_formats[4]; + size_t test_count = 0; + const char *extensions = reinterpret_cast<const char*>( + glGetString(GL_EXTENSIONS)); + if (strstr(extensions, "GL_OES_texture_half_float") != NULL) { + TestFormat rgb16f = { GL_RGB, GL_HALF_FLOAT_OES, 3 }; + test_formats[test_count++] = rgb16f; + + TestFormat rgba16f = { GL_RGBA, GL_HALF_FLOAT_OES, 4 }; + test_formats[test_count++] = rgba16f; + } + if (strstr(extensions, "GL_OES_texture_float") != NULL) { + TestFormat rgb32f = { GL_RGB, GL_FLOAT, 3 }; + test_formats[test_count++] = rgb32f; + + TestFormat rgba32f = { GL_RGBA, GL_FLOAT, 4 }; + test_formats[test_count++] = rgba32f; + } + + const char *vs_source = + "precision mediump float;\n" + "attribute vec4 a_position;\n" + "void main() {\n" + " gl_Position = a_position;\n" + "}\n"; + + GLuint vertex_shader = CompileShader(GL_VERTEX_SHADER, vs_source); + ASSERT_NE(vertex_shader, GLuint(0)); + + const char *fs_source = + "precision mediump float;\n" + "uniform vec4 u_color;\n" + "void main() {\n" + " gl_FragColor = u_color;\n" + "}\n"; + + GLuint fragment_shader = CompileShader(GL_FRAGMENT_SHADER, fs_source); + ASSERT_NE(fragment_shader, GLuint(0)); + + GLuint program = glCreateProgram(); + glAttachShader(program, vertex_shader); + glDeleteShader(vertex_shader); + glAttachShader(program, fragment_shader); + glDeleteShader(fragment_shader); + glLinkProgram(program); + + GLint link_status = 0; + glGetProgramiv(program, GL_LINK_STATUS, &link_status); + if (link_status != GL_TRUE) { + glDeleteProgram(program); + program = 0; + } + ASSERT_NE(program, GLuint(0)); + + EXPECT_EQ(glGetError(), GLenum(GL_NO_ERROR)); + + float quad_vertices[] = { + -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + }; + + GLuint vertex_buffer; + glGenBuffers(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glBufferData( + GL_ARRAY_BUFFER, sizeof(quad_vertices), + reinterpret_cast<void*>(quad_vertices), GL_STATIC_DRAW); + + GLint position_location = glGetAttribLocation(program, "a_position"); + glVertexAttribPointer( + position_location, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), NULL); + glEnableVertexAttribArray(position_location); + + glUseProgram(program); + glUniform4fv(glGetUniformLocation(program, "u_color"), 1, kDrawColor); + + EXPECT_EQ(glGetError(), GLenum(GL_NO_ERROR)); + + for (size_t ii = 0; ii < test_count; ++ii) { + GLuint texture_id = 0; + glGenTextures(1, &texture_id); + glBindTexture(GL_TEXTURE_2D, texture_id); + glTexImage2D( + GL_TEXTURE_2D, 0, test_formats[ii].format, kTextureSize, kTextureSize, + 0, test_formats[ii].format, test_formats[ii].type, NULL); + + GLuint framebuffer = 0; + glGenFramebuffers(1, &framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0); + + EXPECT_EQ(glGetError(), GLenum(GL_NO_ERROR)); + + // Make sure this floating point framebuffer is supported + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { + // Check if this implementation supports reading floats back from this + // framebuffer + GLint read_format = 0; + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &read_format); + GLint read_type = 0; + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &read_type); + + EXPECT_EQ(glGetError(), GLenum(GL_NO_ERROR)); + + if ((read_format == GL_RGB || read_format == GL_RGBA) && + read_type == test_formats[ii].type) { + glClear(GL_COLOR_BUFFER_BIT); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + uint32 read_comp_count = 0; + switch (read_format) { + case GL_RGB: + read_comp_count = 3; + break; + case GL_RGBA: + read_comp_count = 4; + break; + } + + switch (read_type) { + case GL_HALF_FLOAT_OES: { + scoped_ptr<GLushort[]> buf( + new GLushort[kTextureSize * kTextureSize * read_comp_count]); + glReadPixels( + 0, 0, kTextureSize, kTextureSize, read_format, read_type, + buf.get()); + EXPECT_EQ(glGetError(), GLenum(GL_NO_ERROR)); + for (uint32 jj = 0; jj < kTextureSize * kTextureSize; ++jj) { + for (uint32 kk = 0; kk < test_formats[ii].comp_count; ++kk) { + EXPECT_LE( + std::abs(HalfToFloat32(buf[jj * read_comp_count + kk]) - + kDrawColor[kk]), + std::abs(kDrawColor[kk] * kEpsilon)); + } + } + break; + } + case GL_FLOAT: { + scoped_ptr<GLfloat[]> buf( + new GLfloat[kTextureSize * kTextureSize * read_comp_count]); + glReadPixels( + 0, 0, kTextureSize, kTextureSize, read_format, read_type, + buf.get()); + EXPECT_EQ(glGetError(), GLenum(GL_NO_ERROR)); + for (uint32 jj = 0; jj < kTextureSize * kTextureSize; ++jj) { + for (uint32 kk = 0; kk < test_formats[ii].comp_count; ++kk) { + EXPECT_LE( + std::abs(buf[jj * read_comp_count + kk] - kDrawColor[kk]), + std::abs(kDrawColor[kk] * kEpsilon)); + } + } + break; + } + } + } + } + + glDeleteFramebuffers(1, &framebuffer); + glDeleteTextures(1, &texture_id); + } + + glDeleteBuffers(1, &vertex_buffer); + glDeleteProgram(program); +} + } // namespace gpu |