Index: gpu/command_buffer/service/gles2_cmd_decoder.cc |
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc |
index ed54bce9588eb0bb5478f36659982813b13f5155..6f5fced6f3beec00a243fa7143b2d2ddab749086 100644 |
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc |
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc |
@@ -59,6 +59,7 @@ |
#include "gpu/command_buffer/service/vertex_array_manager.h" |
#include "gpu/command_buffer/service/vertex_attrib_manager.h" |
#include "ui/gl/gl_bindings.h" |
+#include "ui/gl/gl_fence.h" |
#include "ui/gl/gl_image.h" |
#include "ui/gl/gl_implementation.h" |
#include "ui/gl/gl_surface.h" |
@@ -477,6 +478,19 @@ class BackFramebuffer { |
DISALLOW_COPY_AND_ASSIGN(BackFramebuffer); |
}; |
+struct FenceCallback { |
+ explicit FenceCallback() |
+ : fence(gfx::GLFence::Create()) { |
+ DCHECK(fence); |
+ } |
+ void AddCallback(base::Closure cb) { |
+ callbacks.push_back(cb); |
+ } |
+ std::vector<base::Closure> callbacks; |
+ scoped_ptr<gfx::GLFence> fence; |
+}; |
+ |
+ |
// } // anonymous namespace. |
bool GLES2Decoder::GetServiceTextureId(uint32 client_texture_id, |
@@ -587,6 +601,8 @@ class GLES2DecoderImpl : public GLES2Decoder { |
virtual bool HasMoreIdleWork() OVERRIDE; |
virtual void PerformIdleWork() OVERRIDE; |
+ virtual void WaitForReadPixels(base::Closure callback) OVERRIDE; |
+ |
virtual void SetResizeCallback( |
const base::Callback<void(gfx::Size, float)>& callback) OVERRIDE; |
@@ -1557,6 +1573,9 @@ class GLES2DecoderImpl : public GLES2Decoder { |
surface_->DeferDraws(); |
} |
+ void ProcessPendingReadPixels(); |
+ void FinishReadPixels(const cmds::ReadPixels& c, GLuint buffer); |
+ |
void ForceCompileShaderIfPending(Shader* shader); |
// Generate a member function prototype for each command in an automated and |
@@ -1725,6 +1744,8 @@ class GLES2DecoderImpl : public GLES2Decoder { |
scoped_ptr<GPUTracer> gpu_tracer_; |
+ std::queue<linked_ptr<FenceCallback> > pending_readpixel_fences_; |
+ |
DISALLOW_COPY_AND_ASSIGN(GLES2DecoderImpl); |
}; |
@@ -2499,6 +2520,7 @@ bool GLES2DecoderImpl::Initialize( |
AsyncPixelTransferManager::Create(context.get())); |
async_pixel_transfer_manager_->Initialize(texture_manager()); |
+ |
return true; |
} |
@@ -3540,6 +3562,7 @@ bool GLES2DecoderImpl::CreateShaderHelper(GLenum type, GLuint client_id) { |
void GLES2DecoderImpl::DoFinish() { |
glFinish(); |
+ ProcessPendingReadPixels(); |
ProcessPendingQueries(); |
} |
@@ -6708,6 +6731,94 @@ error::Error GLES2DecoderImpl::HandleVertexAttribDivisorANGLE( |
return error::kNoError; |
} |
+void GLES2DecoderImpl::FinishReadPixels( |
+ const cmds::ReadPixels& c, |
+ GLuint buffer) { |
+ TRACE_EVENT0("gpu", "GLES2DecoderImpl::FinishReadPixels"); |
+ GLsizei width = c.width; |
+ GLsizei height = c.height; |
+ GLenum format = c.format; |
+ GLenum type = c.type; |
+ typedef cmds::ReadPixels::Result Result; |
+ uint32 pixels_size; |
+ GLES2Util::ComputeImageDataSizes( |
+ width, height, format, type, state_.pack_alignment, &pixels_size, |
+ NULL, NULL); |
+ void* pixels = GetSharedMemoryAs<void*>( |
+ c.pixels_shm_id, c.pixels_shm_offset, pixels_size); |
+ if (!pixels) { |
+ if (buffer != 0) { |
+ glDeleteBuffersARB(1, &buffer); |
+ return; |
+ } |
+ } |
+ |
+ if (buffer != 0) { |
+ glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, buffer); |
+ void* data = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY); |
+ memcpy(pixels, data, pixels_size); |
+ // GL_PIXEL_PACK_BUFFER_ARB is currently unused, so we don't |
+ // have to restore the state. |
+ glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB); |
+ glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); |
+ glDeleteBuffersARB(1, &buffer); |
+ } |
+ Result* result = NULL; |
+ if (c.result_shm_id != 0) { |
+ result = GetSharedMemoryAs<Result*>( |
+ c.result_shm_id, c.result_shm_offset, sizeof(*result)); |
+ if (!result) { |
+ // No way to mark failure. |
+ return; |
+ } |
+ } |
piman
2013/06/26 22:17:08
It would be good to map the result before the pixe
hubbe
2013/06/28 22:17:49
I'm not sure I understand this, if the client dest
piman
2013/06/28 23:35:46
So, for the synchronous ReadPixels, if the memory
hubbe
2013/06/29 00:13:56
This seems a little silly as async readpixels does
|
+ |
+ if (result != NULL) { |
+ *result = true; |
+ } |
+ |
+ GLenum read_format = GetBoundReadFrameBufferInternalFormat(); |
+ uint32 channels_exist = GLES2Util::GetChannelsForFormat(read_format); |
+ if ((channels_exist & 0x0008) == 0 && |
+ workarounds().clear_alpha_in_readpixels) { |
+ // Set the alpha to 255 because some drivers are buggy in this regard. |
+ uint32 temp_size; |
+ |
+ uint32 unpadded_row_size; |
+ uint32 padded_row_size; |
+ if (!GLES2Util::ComputeImageDataSizes( |
+ width, 2, format, type, state_.pack_alignment, &temp_size, |
+ &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; |
+ } |
+ 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; |
+ } |
+ break; |
+ } |
+ default: |
+ break; |
+ } |
+ } |
+} |
+ |
+ |
error::Error GLES2DecoderImpl::HandleReadPixels( |
uint32 immediate_data_size, const cmds::ReadPixels& c) { |
if (ShouldDeferReads()) |
@@ -6718,6 +6829,7 @@ error::Error GLES2DecoderImpl::HandleReadPixels( |
GLsizei height = c.height; |
GLenum format = c.format; |
GLenum type = c.type; |
+ GLboolean async = c.async; |
if (width < 0 || height < 0) { |
LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glReadPixels", "dimensions < 0"); |
return error::kNoError; |
@@ -6817,6 +6929,25 @@ error::Error GLES2DecoderImpl::HandleReadPixels( |
dst += padded_row_size; |
} |
} else { |
+ if (async && features().use_async_readpixels) { |
+ GLuint buffer; |
+ glGenBuffersARB(1, &buffer); |
+ glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, buffer); |
+ glBufferData(GL_PIXEL_PACK_BUFFER_ARB, pixels_size, NULL, GL_STREAM_READ); |
+ GLenum error = glGetError(); |
+ if (error == GL_NO_ERROR) { |
+ glReadPixels(x, y, width, height, format, type, 0); |
+ pending_readpixel_fences_.push(linked_ptr<FenceCallback>( |
+ new FenceCallback())); |
+ WaitForReadPixels(base::Bind( |
+ &GLES2DecoderImpl::FinishReadPixels, |
+ base::internal::SupportsWeakPtrBase::StaticAsWeakPtr |
+ <GLES2DecoderImpl>(this), |
+ c, buffer)); |
+ glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); |
+ return error::kNoError; |
+ } |
+ } |
glReadPixels(x, y, width, height, format, type, pixels); |
} |
GLenum error = LOCAL_PEEK_GL_ERROR("glReadPixels"); |
@@ -6824,51 +6955,7 @@ error::Error GLES2DecoderImpl::HandleReadPixels( |
if (result != NULL) { |
*result = true; |
} |
- |
- GLenum read_format = GetBoundReadFrameBufferInternalFormat(); |
- uint32 channels_exist = GLES2Util::GetChannelsForFormat(read_format); |
- if ((channels_exist & 0x0008) == 0 && |
- workarounds().clear_alpha_in_readpixels) { |
- // Set the alpha to 255 because some drivers are buggy in this regard. |
- uint32 temp_size; |
- |
- uint32 unpadded_row_size; |
- uint32 padded_row_size; |
- if (!GLES2Util::ComputeImageDataSizes( |
- width, 2, format, type, state_.pack_alignment, &temp_size, |
- &unpadded_row_size, &padded_row_size)) { |
- LOCAL_SET_GL_ERROR( |
- GL_INVALID_VALUE, "glReadPixels", "dimensions out of range"); |
- return error::kNoError; |
- } |
- // NOTE: Assumes the type is GL_UNSIGNED_BYTE which was true at the time |
- // of this implementation. |
- if (type != GL_UNSIGNED_BYTE) { |
- LOCAL_SET_GL_ERROR( |
- GL_INVALID_OPERATION, "glReadPixels", |
- "unsupported readPixel format"); |
- return error::kNoError; |
- } |
- 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; |
- } |
- break; |
- } |
- default: |
- break; |
- } |
- } |
+ FinishReadPixels(c, 0); |
} |
return error::kNoError; |
@@ -9098,11 +9185,35 @@ bool GLES2DecoderImpl::ProcessPendingQueries() { |
return query_manager_->HavePendingQueries(); |
} |
+// Note that if there are no pending readpixels right now, |
+// this function will call the callback immediately. |
+void GLES2DecoderImpl::WaitForReadPixels(base::Closure callback) { |
+ if (features().use_async_readpixels && !pending_readpixel_fences_.empty()) { |
+ pending_readpixel_fences_.back()->callbacks.push_back(callback); |
+ } else { |
+ callback.Run(); |
+ } |
+} |
+ |
+void GLES2DecoderImpl::ProcessPendingReadPixels() { |
+ while (!pending_readpixel_fences_.empty() && |
+ pending_readpixel_fences_.front()->fence->HasCompleted()) { |
+ std::vector<base::Closure> callbacks = |
+ pending_readpixel_fences_.front()->callbacks; |
+ pending_readpixel_fences_.pop(); |
+ for (size_t i = 0; i < callbacks.size(); i++) { |
+ callbacks[i].Run(); |
+ } |
+ } |
+} |
+ |
bool GLES2DecoderImpl::HasMoreIdleWork() { |
- return async_pixel_transfer_manager_->NeedsProcessMorePendingTransfers(); |
+ return !pending_readpixel_fences_.empty() || |
+ async_pixel_transfer_manager_->NeedsProcessMorePendingTransfers(); |
} |
void GLES2DecoderImpl::PerformIdleWork() { |
+ ProcessPendingReadPixels(); |
if (!async_pixel_transfer_manager_->NeedsProcessMorePendingTransfers()) |
return; |
async_pixel_transfer_manager_->ProcessMorePendingTransfers(); |