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 3fc39c6dda1c29f787f7a8648d00baa58d85d481..dfb29e30f62dd75ddbd025595cb526959493b66a 100644 |
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc |
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc |
@@ -56,6 +56,7 @@ |
#include "gpu/command_buffer/service/texture_manager.h" |
#include "gpu/command_buffer/service/vertex_attrib_manager.h" |
#include "gpu/command_buffer/service/vertex_array_manager.h" |
+#include "ui/gl/async_pixel_transfer_delegate.h" |
#include "ui/gl/gl_image.h" |
#include "ui/gl/gl_implementation.h" |
#include "ui/gl/gl_surface.h" |
@@ -193,6 +194,61 @@ static inline GLenum GetTexInternalFormat(GLenum internal_format) { |
return internal_format; |
} |
+// TODO(epenner): Could the above function be merged into this and removed? |
+static inline GLenum GetTexInternalFormat(GLenum internal_format, |
+ GLenum format, |
+ GLenum type) { |
+ GLenum gl_internal_format = GetTexInternalFormat(internal_format); |
+ |
+ if (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2) |
+ return gl_internal_format; |
+ |
+ if (type == GL_FLOAT) { |
+ switch (format) { |
+ case GL_RGBA: |
+ gl_internal_format = GL_RGBA32F_ARB; |
+ break; |
+ case GL_RGB: |
+ gl_internal_format = GL_RGB32F_ARB; |
+ break; |
+ case GL_LUMINANCE_ALPHA: |
+ gl_internal_format = GL_LUMINANCE_ALPHA32F_ARB; |
+ break; |
+ case GL_LUMINANCE: |
+ gl_internal_format = GL_LUMINANCE32F_ARB; |
+ break; |
+ case GL_ALPHA: |
+ gl_internal_format = GL_ALPHA32F_ARB; |
+ break; |
+ default: |
+ NOTREACHED(); |
+ break; |
+ } |
+ } else if (type == GL_HALF_FLOAT_OES) { |
+ switch (format) { |
+ case GL_RGBA: |
+ gl_internal_format = GL_RGBA16F_ARB; |
+ break; |
+ case GL_RGB: |
+ gl_internal_format = GL_RGB16F_ARB; |
+ break; |
+ case GL_LUMINANCE_ALPHA: |
+ gl_internal_format = GL_LUMINANCE_ALPHA16F_ARB; |
+ break; |
+ case GL_LUMINANCE: |
+ gl_internal_format = GL_LUMINANCE16F_ARB; |
+ break; |
+ case GL_ALPHA: |
+ gl_internal_format = GL_ALPHA16F_ARB; |
+ break; |
+ default: |
+ NOTREACHED(); |
+ break; |
+ } |
+ } |
+ return gl_internal_format; |
+} |
+ |
static void WrappedTexImage2D( |
GLenum target, |
GLint level, |
@@ -203,55 +259,9 @@ static void WrappedTexImage2D( |
GLenum format, |
GLenum type, |
const void* pixels) { |
- GLenum gl_internal_format = GetTexInternalFormat(internal_format); |
- if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) { |
- if (type == GL_FLOAT) { |
- switch (format) { |
- case GL_RGBA: |
- gl_internal_format = GL_RGBA32F_ARB; |
- break; |
- case GL_RGB: |
- gl_internal_format = GL_RGB32F_ARB; |
- break; |
- case GL_LUMINANCE_ALPHA: |
- gl_internal_format = GL_LUMINANCE_ALPHA32F_ARB; |
- break; |
- case GL_LUMINANCE: |
- gl_internal_format = GL_LUMINANCE32F_ARB; |
- break; |
- case GL_ALPHA: |
- gl_internal_format = GL_ALPHA32F_ARB; |
- break; |
- default: |
- NOTREACHED(); |
- break; |
- } |
- } else if (type == GL_HALF_FLOAT_OES) { |
- switch (format) { |
- case GL_RGBA: |
- gl_internal_format = GL_RGBA16F_ARB; |
- break; |
- case GL_RGB: |
- gl_internal_format = GL_RGB16F_ARB; |
- break; |
- case GL_LUMINANCE_ALPHA: |
- gl_internal_format = GL_LUMINANCE_ALPHA16F_ARB; |
- break; |
- case GL_LUMINANCE: |
- gl_internal_format = GL_LUMINANCE16F_ARB; |
- break; |
- case GL_ALPHA: |
- gl_internal_format = GL_ALPHA16F_ARB; |
- break; |
- default: |
- NOTREACHED(); |
- break; |
- } |
- } |
- } |
glTexImage2D( |
- target, level, gl_internal_format, width, height, border, format, type, |
- pixels); |
+ target, level, GetTexInternalFormat(internal_format, format, type), |
+ width, height, border, format, type, pixels); |
} |
// Wrapper for glEnable/glDisable that doesn't suck. |
@@ -552,6 +562,12 @@ class GLES2DecoderImpl : public GLES2Decoder { |
virtual void SetMsgCallback(const MsgCallback& callback) OVERRIDE; |
virtual void SetStreamTextureManager(StreamTextureManager* manager) OVERRIDE; |
+ |
+ virtual gfx::AsyncPixelTransferDelegate* |
+ GetAsyncPixelTransferDelegate() OVERRIDE; |
+ virtual void SetAsyncPixelTransferDelegate( |
+ gfx::AsyncPixelTransferDelegate* delegate) OVERRIDE; |
+ |
virtual bool GetServiceTextureId(uint32 client_texture_id, |
uint32* service_texture_id) OVERRIDE; |
@@ -774,6 +790,14 @@ class GLES2DecoderImpl : public GLES2Decoder { |
GLenum type, |
const void * data); |
+ // Extra validation for async tex(Sub)Image2D. |
+ bool ValidateAsyncTransfer( |
+ const char* function_name, |
+ TextureManager::TextureInfo* info, |
+ GLenum target, |
+ GLint level, |
+ const void * data); |
+ |
// Wrapper for TexImageIOSurface2DCHROMIUM. |
void DoTexImageIOSurface2DCHROMIUM( |
GLenum target, |
@@ -1598,6 +1622,7 @@ class GLES2DecoderImpl : public GLES2Decoder { |
MsgCallback msg_callback_; |
StreamTextureManager* stream_texture_manager_; |
+ scoped_ptr<gfx::AsyncPixelTransferDelegate> async_pixel_transfer_delegate_; |
// The format of the back buffer_ |
GLenum back_buffer_color_format_; |
@@ -2390,6 +2415,10 @@ bool GLES2DecoderImpl::Initialize( |
glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT); |
} |
+ // Create a delegate to perform async pixel transfers. |
+ async_pixel_transfer_delegate_ = |
+ gfx::AsyncPixelTransferDelegate::Create(context.get()); |
+ |
return true; |
} |
@@ -2721,6 +2750,27 @@ void GLES2DecoderImpl::RestoreCurrentTexture2DBindings() { |
bool GLES2DecoderImpl::CheckFramebufferValid( |
FramebufferManager::FramebufferInfo* framebuffer, |
GLenum target, const char* func_name) { |
+ |
+ // As this occurs before every draw, take this opportunity to |
+ // bind any unbound async pixel transfers to their textures. |
+ // TODO(epenner): Is there a better place to do this? Transfers |
greggman
2012/12/14 06:08:06
This seems like it really needs to be in DoCommand
epenner
2012/12/14 21:01:34
Does DoCommand happen at every GL command? It only
|
+ // are marked as complete on the main thread, so we only need |
+ // to process them once in a given main-thread task. |
+ bool frame_buffer_dirty = false; |
+ bool texture_dirty = false; |
+ GetContextGroup()->texture_manager()->BindFinishedAsyncPixelTransfers( |
greggman
2012/12/14 06:08:06
you can just use
texture_manager()->BindFinished.
epenner
2012/12/14 21:01:34
Done.
|
+ &texture_dirty, &frame_buffer_dirty); |
+ // Texture unit zero might be stomped. |
+ if (texture_dirty) |
+ RestoreCurrentTexture2DBindings(); |
+ // A texture attached to frame-buffer might have changed size. |
+ if (frame_buffer_dirty) { |
+ clear_state_dirty_ = true; |
+ // TODO(gman): If textures tracked which framebuffers they were attached to |
+ // we could just mark those framebuffers as not complete. |
+ framebuffer_manager()->IncFramebufferStateChangeCount(); |
+ } |
+ |
if (!framebuffer) { |
if (backbuffer_needs_clear_bits_) { |
glClearColor(0, 0, 0, (GLES2Util::GetChannelsForFormat( |
@@ -2887,6 +2937,16 @@ void GLES2DecoderImpl::SetStreamTextureManager(StreamTextureManager* manager) { |
stream_texture_manager_ = manager; |
} |
+gfx::AsyncPixelTransferDelegate* |
+ GLES2DecoderImpl::GetAsyncPixelTransferDelegate() { |
+ return async_pixel_transfer_delegate_.get(); |
+} |
+ |
+void GLES2DecoderImpl::SetAsyncPixelTransferDelegate( |
+ gfx::AsyncPixelTransferDelegate* delegate) { |
+ async_pixel_transfer_delegate_ = make_scoped_ptr(delegate); |
+} |
+ |
bool GLES2DecoderImpl::GetServiceTextureId(uint32 client_texture_id, |
uint32* service_texture_id) { |
TextureManager::TextureInfo* texture = |
@@ -3629,6 +3689,7 @@ void GLES2DecoderImpl::DoBindTexture(GLenum target, GLuint client_id) { |
texture_manager()->SetInfoTarget(info, target); |
} |
glBindTexture(target, info->service_id()); |
+ |
TextureUnit& unit = state_.texture_units[state_.active_texture_unit]; |
unit.bind_target = target; |
switch (target) { |
@@ -9666,13 +9727,39 @@ void GLES2DecoderImpl::DoTraceEndCHROMIUM() { |
gpu_trace_stack_.pop(); |
} |
+bool GLES2DecoderImpl::ValidateAsyncTransfer( |
+ const char* function_name, |
+ TextureManager::TextureInfo* info, |
+ GLenum target, |
+ GLint level, |
+ const void * data) { |
+ // We only support async uploads to 2D textures for now. |
+ if (GL_TEXTURE_2D != target) { |
+ SetGLErrorInvalidEnum(function_name, target, "target"); |
+ return false; |
+ } |
+ // We only support uploads to level zero for now. |
+ if (level != 0) { |
+ SetGLError(GL_INVALID_VALUE, function_name, "level != 0"); |
+ return false; |
+ } |
+ // A transfer buffer must be bound, even for asyncTexImage2D. |
+ if (data == NULL) { |
+ SetGLError(GL_INVALID_OPERATION, function_name, "buffer == 0"); |
+ return false; |
+ } |
+ // We only support one async transfer in progress. |
+ if (!info || info->AsyncTransferIsInProgress()) { |
+ SetGLError(GL_INVALID_OPERATION, |
+ function_name, "transfer already in progress"); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
error::Error GLES2DecoderImpl::HandleAsyncTexImage2DCHROMIUM( |
uint32 immediate_data_size, const gles2::AsyncTexImage2DCHROMIUM& c) { |
TRACE_EVENT0("gpu", "GLES2DecoderImpl::HandleAsyncTexImage2DCHROMIUM"); |
- |
- // TODO: This is a copy of HandleTexImage2D validation. Merge |
- // as much of it as possible. |
- tex_image_2d_failed_ = true; |
GLenum target = static_cast<GLenum>(c.target); |
GLint level = static_cast<GLint>(c.level); |
GLint internal_format = static_cast<GLint>(c.internalformat); |
@@ -9684,6 +9771,9 @@ error::Error GLES2DecoderImpl::HandleAsyncTexImage2DCHROMIUM( |
uint32 pixels_shm_id = static_cast<uint32>(c.pixels_shm_id); |
uint32 pixels_shm_offset = static_cast<uint32>(c.pixels_shm_offset); |
uint32 pixels_size; |
+ |
+ // TODO(epenner): Move this and copies of this memory validation |
+ // into ValidateTexImage2D step. |
if (!GLES2Util::ComputeImageDataSizes( |
width, height, format, type, state_.unpack_alignment, &pixels_size, NULL, |
NULL)) { |
@@ -9698,19 +9788,62 @@ error::Error GLES2DecoderImpl::HandleAsyncTexImage2DCHROMIUM( |
} |
} |
- // TODO(epenner): Do this via an async task. |
- DoTexImage2D( |
- target, level, internal_format, width, height, border, format, type, |
- pixels, pixels_size); |
+ // All the normal glTexSubImage2D validation. |
+ if (!ValidateTexImage2D("glAsyncTexImage2D", target, level, internal_format, |
greggman
2012/12/14 06:08:06
s/glAsyncTexImage2D/glAsyncTexImage2DCHROMIUM/
s/g
epenner
2012/12/14 21:01:34
Done.
|
+ width, height, border, format, type, pixels, pixels_size)) { |
+ return error::kNoError; |
+ } |
+ |
+ // Extra async validation. |
+ TextureManager::TextureInfo* info = GetTextureInfoForTarget(target); |
+ if (!ValidateAsyncTransfer("glAsyncTexImage2D", info, target, level, pixels)) |
+ return error::kNoError; |
+ |
+ // Don't allow async redefinition of a textures. |
+ if (info->IsDefined()) { |
+ SetGLError(GL_INVALID_OPERATION, "glAsyncTexImage2D", "already defined"); |
+ return error::kNoError; |
+ } |
+ |
+ // We know the memory/size is safe, so get the real shared memory since |
+ // it might need to be duped to prevent use-after-free of the memory. |
+ Buffer buffer = GetSharedMemoryBuffer(c.pixels_shm_id); |
+ base::SharedMemory* shared_memory = buffer.shared_memory; |
+ uint32 shm_size = buffer.size; |
+ uint32 shm_data_offset = c.pixels_shm_offset; |
+ uint32 shm_data_size = pixels_size; |
+ |
+ // Set up the async state if needed, and make the texture |
+ // immutable so the async state stays valid. The level info |
+ // is set up lazily when the transfer completes. |
+ DCHECK(!info->GetAsyncTransferState()); |
+ info->SetAsyncTransferState( |
+ async_pixel_transfer_delegate_-> |
+ CreatePixelTransferState(info->service_id())); |
+ info->SetImmutable(true); |
+ |
+ // Issue the async call and set up the texture. |
+ GLenum gl_internal_format = |
+ GetTexInternalFormat(internal_format, format, type); |
+ gfx::AsyncTexImage2DParams tex_params = {target, level, gl_internal_format, |
+ width, height, border, format, type}; |
+ gfx::AsyncMemoryParams mem_params = {shared_memory, shm_size, |
+ shm_data_offset, shm_data_size}; |
+ |
+ // Add a pending transfer to the texture manager, which will bind the |
+ // transfer data to the texture and set the level info at the same time, |
+ // after the the transfer is complete. |
+ GetContextGroup()->texture_manager()->AddPendingAsyncPixelTransfer( |
greggman
2012/12/14 06:08:06
GetConextGroup()-> not needed?
epenner
2012/12/14 21:01:34
Done.
|
+ info->GetAsyncTransferState()->AsWeakPtr(), info); |
+ |
+ async_pixel_transfer_delegate_->AsyncTexImage2D( |
+ info->GetAsyncTransferState(), tex_params, mem_params); |
return error::kNoError; |
} |
error::Error GLES2DecoderImpl::HandleAsyncTexSubImage2DCHROMIUM( |
uint32 immediate_data_size, const gles2::AsyncTexSubImage2DCHROMIUM& c) { |
TRACE_EVENT0("gpu", "GLES2DecoderImpl::HandleAsyncTexSubImage2DCHROMIUM"); |
- |
- // TODO: This is a copy of HandleTexSubImage2D validation. Merge |
- // as much of it as possible. |
GLenum target = static_cast<GLenum>(c.target); |
GLint level = static_cast<GLint>(c.level); |
GLint xoffset = static_cast<GLint>(c.xoffset); |
@@ -9719,6 +9852,9 @@ error::Error GLES2DecoderImpl::HandleAsyncTexSubImage2DCHROMIUM( |
GLsizei height = static_cast<GLsizei>(c.height); |
GLenum format = static_cast<GLenum>(c.format); |
GLenum type = static_cast<GLenum>(c.type); |
+ |
+ // TODO(epenner): Move this and copies of this memory validation |
+ // into ValidateTexSubImage2D step. |
uint32 data_size; |
if (!GLES2Util::ComputeImageDataSizes( |
width, height, format, type, state_.unpack_alignment, &data_size, |
@@ -9727,13 +9863,58 @@ error::Error GLES2DecoderImpl::HandleAsyncTexSubImage2DCHROMIUM( |
} |
const void* pixels = GetSharedMemoryAs<const void*>( |
c.data_shm_id, c.data_shm_offset, data_size); |
- if (pixels == NULL) { |
- return error::kOutOfBounds; |
+ |
+ // All the normal glTexSubImage2D validation. |
+ error::Error error = error::kNoError; |
+ if (!ValidateTexSubImage2D(&error, "glAsyncTexSubImage2D", target, level, |
+ xoffset, yoffset, width, height, format, type, pixels)) { |
+ return error; |
} |
- // TODO(epenner): Do this via an async task. |
- return DoTexSubImage2D( |
- target, level, xoffset, yoffset, width, height, format, type, pixels); |
+ // Extra async validation. |
+ TextureManager::TextureInfo* info = GetTextureInfoForTarget(target); |
+ if (!ValidateAsyncTransfer( |
+ "glAsyncTexSubImage2D", info, target, level, pixels)) |
+ return error::kNoError; |
+ |
+ // Guarantee async textures are always 'cleared' as follows: |
+ // - AsyncTexImage2D can not redefine an existing texture |
+ // - AsyncTexImage2D must initialize the entire image via non-null buffer. |
+ // - AsyncTexSubImage2D clears synchronously if not already cleared. |
+ // - Textures become immutable after an async call. |
+ // This way we know in all cases that an async texture is always clear. |
+ if (!info->SafeToRenderFrom()) { |
+ if (!texture_manager()->ClearTextureLevel(this, info, target, level)) { |
+ SetGLError( |
+ GL_OUT_OF_MEMORY, "glAsyncTexSubImage2D", "dimensions too big"); |
+ return error::kNoError; |
+ } |
+ } |
+ |
+ // We know the memory/size is safe, so get the real shared memory since |
+ // it might need to be duped to prevent use-after-free of the memory. |
+ Buffer buffer = GetSharedMemoryBuffer(c.data_shm_id); |
+ base::SharedMemory* shared_memory = buffer.shared_memory; |
+ uint32 shm_size = buffer.size; |
+ uint32 shm_data_offset = c.data_shm_offset; |
+ uint32 shm_data_size = data_size; |
+ |
+ if (!info->GetAsyncTransferState()) { |
+ // Set up the async state if needed, and make the texture |
+ // immutable so the async state stays valid. |
+ info->SetAsyncTransferState( |
+ async_pixel_transfer_delegate_-> |
+ CreatePixelTransferState(info->service_id())); |
+ info->SetImmutable(true); |
+ } |
+ |
+ gfx::AsyncTexSubImage2DParams tex_params = {target, level, xoffset, yoffset, |
+ width, height, format, type}; |
+ gfx::AsyncMemoryParams mem_params = {shared_memory, shm_size, |
+ shm_data_offset, shm_data_size}; |
+ async_pixel_transfer_delegate_->AsyncTexSubImage2D( |
+ info->GetAsyncTransferState(), tex_params, mem_params); |
+ return error::kNoError; |
} |
// Include the auto-generated part of this file. We split this because it means |