summaryrefslogtreecommitdiffstats
path: root/gpu
diff options
context:
space:
mode:
authorgeofflang@chromium.org <geofflang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-11 01:27:59 +0000
committergeofflang@chromium.org <geofflang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-11 01:27:59 +0000
commit685863794f0212c52238384fa033ff5b28d5ea7c (patch)
treee09fb17557448f83caac19944f3c6a3f81d6ad9e /gpu
parente97887cbd71dcc943b834c051f2c7719f76d7e53 (diff)
downloadchromium_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.cc37
-rw-r--r--gpu/command_buffer/common/gles2_cmd_utils.h5
-rw-r--r--gpu/command_buffer/service/framebuffer_manager.cc21
-rw-r--r--gpu/command_buffer/service/framebuffer_manager.h4
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc105
-rw-r--r--gpu/command_buffer/tests/gl_readback_unittests.cc233
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