diff options
author | epenner@chromium.org <epenner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-17 09:01:59 +0000 |
---|---|---|
committer | epenner@chromium.org <epenner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-17 09:01:59 +0000 |
commit | 32145a9eab73bf02ab820f03ec79dcacdfb43932 (patch) | |
tree | 95be4c92661eed312b44e299e54e8076798c1c9e /gpu | |
parent | 3abf5ba76821e9aff27e2bbde17d913c132ff7b0 (diff) | |
download | chromium_src-32145a9eab73bf02ab820f03ec79dcacdfb43932.zip chromium_src-32145a9eab73bf02ab820f03ec79dcacdfb43932.tar.gz chromium_src-32145a9eab73bf02ab820f03ec79dcacdfb43932.tar.bz2 |
gpu: Add async pixel transfers.
This adds a generic async pixel transfer interface, a
stub/fallback implementation and mocks/tests.
NOTRY=true
BUG=161337
Review URL: https://chromiumcodereview.appspot.com/11428140
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@173430 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu')
-rw-r--r-- | gpu/command_buffer/service/async_pixel_transfer_delegate_mock.cc | 22 | ||||
-rw-r--r-- | gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h | 53 | ||||
-rw-r--r-- | gpu/command_buffer/service/common_decoder.cc | 4 | ||||
-rw-r--r-- | gpu/command_buffer/service/common_decoder.h | 4 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder.cc | 319 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder.h | 6 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_mock.h | 4 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc | 137 | ||||
-rw-r--r-- | gpu/command_buffer/service/query_manager.cc | 30 | ||||
-rw-r--r-- | gpu/command_buffer/service/texture_manager.cc | 48 | ||||
-rw-r--r-- | gpu/command_buffer/service/texture_manager.h | 36 | ||||
-rw-r--r-- | gpu/gpu.gyp | 2 |
12 files changed, 595 insertions, 70 deletions
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.cc new file mode 100644 index 0000000..1937842 --- /dev/null +++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.cc @@ -0,0 +1,22 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h" + +namespace gfx { + +MockAsyncPixelTransferState::MockAsyncPixelTransferState() { +} + +MockAsyncPixelTransferState::~MockAsyncPixelTransferState() { +} + +MockAsyncPixelTransferDelegate::MockAsyncPixelTransferDelegate() { +} + +MockAsyncPixelTransferDelegate::~MockAsyncPixelTransferDelegate() { +} + +} // namespace gfx + diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h b/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h new file mode 100644 index 0000000..dcd65c9 --- /dev/null +++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h @@ -0,0 +1,53 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GL_ASYNC_TASK_DELEGATE_MOCK_H_ +#define UI_GL_ASYNC_TASK_DELEGATE_MOCK_H_ + +#include "base/basictypes.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "ui/gl/async_pixel_transfer_delegate.h" + +namespace gfx { + +class MockAsyncPixelTransferState : public gfx::AsyncPixelTransferState { + public: + MockAsyncPixelTransferState(); + + // Implement AsyncPixelTransferState. + MOCK_METHOD0(TransferIsInProgress, bool()); + MOCK_METHOD1(BindTransfer, void(AsyncTexImage2DParams* level_params)); + + protected: + virtual ~MockAsyncPixelTransferState(); + DISALLOW_COPY_AND_ASSIGN(MockAsyncPixelTransferState); +}; + +class MockAsyncPixelTransferDelegate : public gfx::AsyncPixelTransferDelegate { + public: + MockAsyncPixelTransferDelegate(); + virtual ~MockAsyncPixelTransferDelegate(); + + // Implement AsyncPixelTransferDelegate. + MOCK_METHOD1(CreateRawPixelTransferState, + gfx::AsyncPixelTransferState*(GLuint service_id)); + MOCK_METHOD1(AsyncNotifyCompletion, + void(const base::Closure& task)); + MOCK_METHOD3(AsyncTexImage2D, + void(gfx::AsyncPixelTransferState*, + const AsyncTexImage2DParams& tex_params, + const AsyncMemoryParams& mem_params)); + MOCK_METHOD3(AsyncTexSubImage2D, + void(gfx::AsyncPixelTransferState*, + const AsyncTexSubImage2DParams& tex_params, + const AsyncMemoryParams& mem_params)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockAsyncPixelTransferDelegate); +}; + +} // namespace gfx + +#endif // UI_GL_ASYNC_TASK_DELEGATE_MOCK_H_ + diff --git a/gpu/command_buffer/service/common_decoder.cc b/gpu/command_buffer/service/common_decoder.cc index 74adcea..5a73cbd 100644 --- a/gpu/command_buffer/service/common_decoder.cc +++ b/gpu/command_buffer/service/common_decoder.cc @@ -73,6 +73,10 @@ void* CommonDecoder::GetAddressAndCheckSize(unsigned int shm_id, return static_cast<int8*>(buffer.ptr) + offset; } +Buffer CommonDecoder::GetSharedMemoryBuffer(unsigned int shm_id) { + return engine_->GetSharedMemoryBuffer(shm_id); +} + bool CommonDecoder::PushAddress(uint32 offset) { if (call_stack_.size() < kMaxStackDepth) { CommandAddress return_address(engine_->GetGetOffset()); diff --git a/gpu/command_buffer/service/common_decoder.h b/gpu/command_buffer/service/common_decoder.h index 94dec16..2848360 100644 --- a/gpu/command_buffer/service/common_decoder.h +++ b/gpu/command_buffer/service/common_decoder.h @@ -10,6 +10,7 @@ #include <string> #include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" +#include "gpu/command_buffer/common/buffer.h" #include "gpu/command_buffer/service/cmd_parser.h" #include "gpu/gpu_export.h" @@ -129,6 +130,9 @@ class GPU_EXPORT CommonDecoder : NON_EXPORTED_BASE(public AsyncAPIInterface) { return static_cast<T>(GetAddressAndCheckSize(shm_id, offset, size)); } + // Get the actual shared memory buffer. + Buffer GetSharedMemoryBuffer(unsigned int shm_id); + protected: // Executes a common command. // Parameters: diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index cb48d3b..a6a3055 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_; @@ -2389,6 +2414,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; } @@ -2656,6 +2685,24 @@ bool GLES2DecoderImpl::MakeCurrent() { return false; } + // TODO(epenner): Is there a better place to do this? Transfers + // can complete any time we yield the main thread. So we *must* + // process transfers after any such yield, before resuming. + bool frame_buffer_dirty = false; + bool texture_dirty = false; + texture_manager()->BindFinishedAsyncPixelTransfers( + &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(); + } + return true; } @@ -2886,6 +2933,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 = @@ -3628,6 +3685,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) { @@ -7915,6 +7973,11 @@ void GLES2DecoderImpl::DoCopyTexSubImage2D( "glCopyTexSubImage2D", "bad dimensions."); return; } + if (info->AsyncTransferIsInProgress()) { + SetGLError(GL_INVALID_OPERATION, + "glCopyTexSubImage2D", "async upload pending for texture"); + return; + } // Check we have compatible formats. GLenum read_format = GetBoundReadFrameBufferInternalFormat(); @@ -8040,6 +8103,11 @@ bool GLES2DecoderImpl::ValidateTexSubImage2D( function_name, "type does not match type of texture."); return false; } + if (info->AsyncTransferIsInProgress()) { + SetGLError(GL_INVALID_OPERATION, + function_name, "async upload pending for texture"); + return false; + } if (!info->ValidForTexture( target, level, xoffset, yoffset, width, height, format, type)) { SetGLError(GL_INVALID_VALUE, function_name, "bad dimensions."); @@ -9681,13 +9749,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); @@ -9699,6 +9793,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)) { @@ -9713,19 +9810,65 @@ 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( + "glAsyncTexImage2DCHROMIUM", target, level, internal_format, + width, height, border, format, type, pixels, pixels_size)) { + return error::kNoError; + } + + // Extra async validation. + TextureManager::TextureInfo* info = GetTextureInfoForTarget(target); + if (!ValidateAsyncTransfer( + "glAsyncTexImage2DCHROMIUM", info, target, level, pixels)) + return error::kNoError; + + // Don't allow async redefinition of a textures. + if (info->IsDefined()) { + SetGLError(GL_INVALID_OPERATION, + "glAsyncTexImage2DCHROMIUM", "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. + texture_manager()->AddPendingAsyncPixelTransfer( + 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); @@ -9734,6 +9877,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, @@ -9742,13 +9888,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, "glAsyncTexSubImage2DCHROMIUM", + 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( + "glAsyncTexSubImage2DCHROMIUM", 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, + "glAsyncTexSubImage2DCHROMIUM","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 diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h index 9b089e3..4311198 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder.h @@ -20,6 +20,7 @@ namespace gfx { class GLContext; class GLSurface; +class AsyncPixelTransferDelegate; } namespace gpu { @@ -157,6 +158,11 @@ class GPU_EXPORT GLES2Decoder : public base::SupportsWeakPtr<GLES2Decoder>, virtual void SetStreamTextureManager(StreamTextureManager* manager) = 0; + // Interface to performing async pixel transfers. + virtual gfx::AsyncPixelTransferDelegate* GetAsyncPixelTransferDelegate() = 0; + virtual void SetAsyncPixelTransferDelegate( + gfx::AsyncPixelTransferDelegate* delegate) = 0; + // Get the service texture ID corresponding to a client texture ID. // If no such record is found then return false. virtual bool GetServiceTextureId(uint32 client_texture_id, diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h index 427c44e..e92ca94 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h @@ -57,6 +57,10 @@ class MockGLES2Decoder : public GLES2Decoder { MOCK_METHOD0(GetVertexArrayManager, gpu::gles2::VertexArrayManager*()); MOCK_METHOD1(SetResizeCallback, void(const base::Callback<void(gfx::Size)>&)); MOCK_METHOD1(SetStreamTextureManager, void(StreamTextureManager*)); + MOCK_METHOD0(GetAsyncPixelTransferDelegate, + gfx::AsyncPixelTransferDelegate*()); + MOCK_METHOD1(SetAsyncPixelTransferDelegate, + void(gfx::AsyncPixelTransferDelegate*)); MOCK_METHOD3(DoCommand, error::Error(unsigned int command, unsigned int arg_count, const void* cmd_data)); diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc index df2a4f1..b60c917 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc @@ -8,6 +8,7 @@ #include "gpu/command_buffer/common/gles2_cmd_format.h" #include "gpu/command_buffer/common/gles2_cmd_utils.h" #include "gpu/command_buffer/common/id_allocator.h" +#include "gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h" #include "gpu/command_buffer/service/cmd_buffer_engine.h" #include "gpu/command_buffer/service/context_group.h" #include "gpu/command_buffer/service/gl_surface_mock.h" @@ -38,6 +39,7 @@ using ::testing::Pointee; using ::testing::Return; using ::testing::SetArrayArgument; using ::testing::SetArgumentPointee; +using ::testing::SetArgPointee; using ::testing::StrEq; using ::testing::StrictMock; @@ -7975,6 +7977,141 @@ TEST_F(GLES2DecoderManualInitTest, GpuMemoryManagerCHROMIUM) { EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); } +TEST_F(GLES2DecoderManualInitTest, AsyncPixelTransfers) { + InitDecoder( + "GL_CHROMIUM_async_pixel_transfers", // extensions + false, false, false, // has alpha/depth/stencil + false, false, false, // request alpha/depth/stencil + true); // bind generates resource + + // Set up the texture. + DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); + TextureManager::TextureInfo* info = GetTextureInfo(client_texture_id_); + + // Set a mock Async delegate + // Async state is returned as a scoped_ptr, but we keep a raw copy. + StrictMock<gfx::MockAsyncPixelTransferDelegate>* delegate = + new StrictMock<gfx::MockAsyncPixelTransferDelegate>; + decoder_->SetAsyncPixelTransferDelegate(delegate); + StrictMock<gfx::MockAsyncPixelTransferState>* state = NULL; + + // Tex(Sub)Image2D upload commands. + AsyncTexImage2DCHROMIUM teximage_cmd; + teximage_cmd.Init(GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 0, GL_RGBA, + GL_UNSIGNED_BYTE, kSharedMemoryId, kSharedMemoryOffset); + AsyncTexSubImage2DCHROMIUM texsubimage_cmd; + texsubimage_cmd.Init(GL_TEXTURE_2D, 0, 0, 0, 8, 8, GL_RGBA, + GL_UNSIGNED_BYTE, kSharedMemoryId, kSharedMemoryOffset); + gfx::AsyncTexImage2DParams teximage_params = + {GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE}; + + // No transfer state exists initially. + EXPECT_FALSE(info->GetAsyncTransferState()); + + // AsyncTexImage2D + { + // Create transfer state since it doesn't exist. + EXPECT_CALL(*delegate, CreateRawPixelTransferState(kServiceTextureId)) + .WillOnce(Return( + state = new StrictMock<gfx::MockAsyncPixelTransferState>)) + .RetiresOnSaturation(); + EXPECT_CALL(*delegate, AsyncTexImage2D(state, _, _)) + .RetiresOnSaturation(); + // Command succeeds. + EXPECT_EQ(error::kNoError, ExecuteCmd(teximage_cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + EXPECT_TRUE(info->GetAsyncTransferState()); + EXPECT_TRUE(info->IsImmutable()); + // The texture is safe but the level has not been defined yet. + EXPECT_TRUE(info->SafeToRenderFrom()); + GLsizei width, height; + EXPECT_FALSE(info->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height)); + } + { + // Async redefinitions are not allowed! + // Command fails. + EXPECT_EQ(error::kNoError, ExecuteCmd(teximage_cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + EXPECT_TRUE(info->GetAsyncTransferState()); + EXPECT_TRUE(info->IsImmutable()); + EXPECT_TRUE(info->SafeToRenderFrom()); + } + + // Lazy binding/defining of the async transfer + { + // We the code should check that the transfer is done, + // call bind transfer on it, and update the texture info. + InSequence scoped_in_sequence; + EXPECT_CALL(*state, TransferIsInProgress()) + .WillOnce(Return(false)) + .RetiresOnSaturation(); + EXPECT_CALL(*state, BindTransfer(_)) + .WillOnce(SetArgPointee<0>(teximage_params)) + .RetiresOnSaturation(); + TextureManager* manager = decoder_->GetContextGroup()->texture_manager(); + bool texture_dirty, framebuffer_dirty; + manager->BindFinishedAsyncPixelTransfers(&texture_dirty, + &framebuffer_dirty); + EXPECT_TRUE(texture_dirty); + EXPECT_FALSE(framebuffer_dirty); + // Texture is safe, and has the right size etc. + EXPECT_TRUE(info->SafeToRenderFrom()); + GLsizei width, height; + EXPECT_TRUE(info->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height)); + EXPECT_EQ(width, 8); + EXPECT_EQ(height, 8); + } + + // AsyncTexSubImage2D + info->SetAsyncTransferState(scoped_ptr<gfx::AsyncPixelTransferState>()); + info->SetImmutable(false); + { + // Create transfer state since it doesn't exist. + EXPECT_CALL(*delegate, CreateRawPixelTransferState(kServiceTextureId)) + .WillOnce(Return( + state = new StrictMock<gfx::MockAsyncPixelTransferState>)) + .RetiresOnSaturation(); + EXPECT_CALL(*delegate, AsyncTexSubImage2D(state, _, _)) + .RetiresOnSaturation(); + // Command succeeds. + EXPECT_EQ(error::kNoError, ExecuteCmd(texsubimage_cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + EXPECT_TRUE(info->GetAsyncTransferState()); + EXPECT_TRUE(info->IsImmutable()); + EXPECT_TRUE(info->SafeToRenderFrom()); + } + { + // No transfer is in progress. + EXPECT_CALL(*state, TransferIsInProgress()) + .WillOnce(Return(false)) // texSubImage validation + .WillOnce(Return(false)) // async validation + .RetiresOnSaturation(); + EXPECT_CALL(*delegate, AsyncTexSubImage2D(state, _, _)) + .RetiresOnSaturation(); + // Command succeeds. + EXPECT_EQ(error::kNoError, ExecuteCmd(texsubimage_cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + EXPECT_TRUE(info->GetAsyncTransferState()); + EXPECT_TRUE(info->IsImmutable()); + EXPECT_TRUE(info->SafeToRenderFrom()); + } + { + // A transfer is still in progress! + EXPECT_CALL(*state, TransferIsInProgress()) + .WillOnce(Return(true)) + .RetiresOnSaturation(); + // No async call, command fails. + EXPECT_EQ(error::kNoError, ExecuteCmd(texsubimage_cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + EXPECT_TRUE(info->GetAsyncTransferState()); + EXPECT_TRUE(info->IsImmutable()); + EXPECT_TRUE(info->SafeToRenderFrom()); + } + + decoder_->SetAsyncPixelTransferDelegate(NULL); + info->SetAsyncTransferState(scoped_ptr<gfx::AsyncPixelTransferState>()); +} + // TODO(gman): Complete this test. // TEST_F(GLES2DecoderTest, CompressedTexImage2DGLError) { // } diff --git a/gpu/command_buffer/service/query_manager.cc b/gpu/command_buffer/service/query_manager.cc index 61a8d7f..51055e2 100644 --- a/gpu/command_buffer/service/query_manager.cc +++ b/gpu/command_buffer/service/query_manager.cc @@ -4,11 +4,13 @@ #include "gpu/command_buffer/service/query_manager.h" #include "base/atomicops.h" +#include "base/bind.h" #include "base/logging.h" #include "base/time.h" #include "gpu/command_buffer/common/gles2_cmd_format.h" #include "gpu/command_buffer/service/gles2_cmd_decoder.h" #include "gpu/command_buffer/service/feature_info.h" +#include "ui/gl/async_pixel_transfer_delegate.h" namespace gpu { namespace gles2 { @@ -162,7 +164,9 @@ void CommandLatencyQuery::Destroy(bool /* have_context */) { CommandLatencyQuery::~CommandLatencyQuery() { } -class AsyncPixelTransfersCompletedQuery : public QueryManager::Query { +class AsyncPixelTransfersCompletedQuery + : public QueryManager::Query + , public base::SupportsWeakPtr<AsyncPixelTransfersCompletedQuery> { public: AsyncPixelTransfersCompletedQuery( QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset); @@ -172,6 +176,8 @@ class AsyncPixelTransfersCompletedQuery : public QueryManager::Query { virtual bool Process() OVERRIDE; virtual void Destroy(bool have_context) OVERRIDE; + void MarkAsCompletedCallback() { MarkAsCompleted(1); } + protected: virtual ~AsyncPixelTransfersCompletedQuery(); }; @@ -186,12 +192,24 @@ bool AsyncPixelTransfersCompletedQuery::Begin() { } bool AsyncPixelTransfersCompletedQuery::End(uint32 submit_count) { - // TODO(epenner): Mark completion via an async task. - // TODO(epenner): This will be a boolean to start, indicating - // completion of all tasks in the query. We could change this - // to return a count of tasks completed instead. MarkAsPending(submit_count); - return MarkAsCompleted(1); + + // This will call MarkAsCompleted(1) as a reply to a task on + // the async upload thread, such that it occurs after all previous + // async transfers have completed. + manager()->decoder()->GetAsyncPixelTransferDelegate()->AsyncNotifyCompletion( + base::Bind( + &AsyncPixelTransfersCompletedQuery::MarkAsCompletedCallback, + AsWeakPtr())); + + // TODO(epenner): The async task occurs outside the normal + // flow, via a callback on this thread. Is there anything + // missing or wrong with that? + + // TODO(epenner): Could we possibly trigger the completion on + // the upload thread by writing to the query shared memory + // directly? + return true; } bool AsyncPixelTransfersCompletedQuery::Process() { diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc index 6dc4b84..fc02b92 100644 --- a/gpu/command_buffer/service/texture_manager.cc +++ b/gpu/command_buffer/service/texture_manager.cc @@ -1210,5 +1210,53 @@ void TextureManager::AddToSignature( info->AddToSignature(feature_info_.get(), target, level, signature); } +void TextureManager::AddPendingAsyncPixelTransfer( + base::WeakPtr<gfx::AsyncPixelTransferState> state, TextureInfo* info) { + pending_async_transfers_.push_back(PendingAsyncTransfer(state,info)); +} + +void TextureManager::BindFinishedAsyncPixelTransfers( + bool* texture_dirty, bool* framebuffer_dirty) { + DCHECK(texture_dirty); + DCHECK(framebuffer_dirty); + *texture_dirty = false; + *framebuffer_dirty = false; + + // Remove finished transfers from the list, while + // marking whether texture unit 0 or frame_buffer status is dirty. + while(!pending_async_transfers_.empty()) { + PendingAsyncTransfer state_info = pending_async_transfers_.front(); + if (!state_info.first.get()) { + // The AsyncState is owned by the TextureInfo. So if the + // async state is deleted, so is the TextureInfo. + pending_async_transfers_.pop_front(); + continue; + } + // Terminate early, as all transfers finish in order. + if (state_info.first->TransferIsInProgress()) + break; + // If the transfer is finished, bind it to the texture, + // update the TextureInfo, and remove it from pending list. + *texture_dirty = true; + *framebuffer_dirty |= state_info.second->IsAttachedToFramebuffer(); + gfx::AsyncTexImage2DParams tex_define_params; + state_info.second-> + GetAsyncTransferState()->BindTransfer(&tex_define_params); + SetLevelInfo( + state_info.second, + tex_define_params.target, + tex_define_params.level, + tex_define_params.internal_format, + tex_define_params.width, + tex_define_params.height, + 1, // depth + tex_define_params.border, + tex_define_params.format, + tex_define_params.type, + true); // cleared + pending_async_transfers_.pop_front(); + } +} + } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h index dc1cd7f..f0f5b6a 100644 --- a/gpu/command_buffer/service/texture_manager.h +++ b/gpu/command_buffer/service/texture_manager.h @@ -5,6 +5,7 @@ #ifndef GPU_COMMAND_BUFFER_SERVICE_TEXTURE_MANAGER_H_ #define GPU_COMMAND_BUFFER_SERVICE_TEXTURE_MANAGER_H_ +#include <list> #include <string> #include <vector> #include "base/basictypes.h" @@ -15,6 +16,7 @@ #include "gpu/command_buffer/service/gl_utils.h" #include "gpu/command_buffer/service/memory_tracking.h" #include "gpu/gpu_export.h" +#include "ui/gl/async_pixel_transfer_delegate.h" #include "ui/gl/gl_image.h" namespace gpu { @@ -178,6 +180,17 @@ class GPU_EXPORT TextureManager { return stream_texture_; } + gfx::AsyncPixelTransferState* GetAsyncTransferState() const { + return async_transfer_state_.get(); + } + void SetAsyncTransferState(scoped_ptr<gfx::AsyncPixelTransferState> state) { + async_transfer_state_ = state.Pass(); + } + bool AsyncTransferIsInProgress() { + return async_transfer_state_ && + async_transfer_state_->TransferIsInProgress(); + } + void SetImmutable(bool immutable) { immutable_ = immutable; } @@ -189,6 +202,11 @@ class GPU_EXPORT TextureManager { // Whether a particular level/face is cleared. bool IsLevelCleared(GLenum target, GLint level); + // Whether the texture has been defined + bool IsDefined() { + return estimated_size() > 0; + } + private: friend class TextureManager; friend class base::RefCounted<TextureInfo>; @@ -341,6 +359,9 @@ class GPU_EXPORT TextureManager { // Whether this is a special streaming texture. bool stream_texture_; + // State to facilitate async transfers on this texture. + scoped_ptr<gfx::AsyncPixelTransferState> async_transfer_state_; + // Whether the texture is immutable and no further changes to the format // or dimensions of the texture object can be made. bool immutable_; @@ -530,6 +551,13 @@ class GPU_EXPORT TextureManager { GLint level, std::string* signature) const; + // Transfers added will get their TextureInfo updated at the same time + // the async transfer is bound to the real texture. + void AddPendingAsyncPixelTransfer( + base::WeakPtr<gfx::AsyncPixelTransferState> state, TextureInfo* info); + void BindFinishedAsyncPixelTransfers(bool* texture_dirty, + bool* framebuffer_dirty); + private: // Helper for Initialize(). TextureInfo::Ref CreateDefaultAndBlackTextures( @@ -572,6 +600,14 @@ class GPU_EXPORT TextureManager { // The default textures for each target (texture name = 0) TextureInfo::Ref default_textures_[kNumDefaultTextures]; + // Async texture allocations which haven't been bound to their textures + // yet. This facilitates updating the TextureInfo at the same time the + // real texture data is bound. + typedef std::pair<base::WeakPtr<gfx::AsyncPixelTransferState>, + TextureInfo*> PendingAsyncTransfer; + typedef std::list<PendingAsyncTransfer> PendingAsyncTransferList; + PendingAsyncTransferList pending_async_transfers_; + DISALLOW_COPY_AND_ASSIGN(TextureManager); }; diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp index 3c12f3c..e7bd419 100644 --- a/gpu/gpu.gyp +++ b/gpu/gpu.gyp @@ -155,6 +155,8 @@ 'command_buffer/common/id_allocator_test.cc', 'command_buffer/common/trace_event.h', 'command_buffer/common/unittest_main.cc', + 'command_buffer/service/async_pixel_transfer_delegate_mock.h', + 'command_buffer/service/async_pixel_transfer_delegate_mock.cc', 'command_buffer/service/buffer_manager_unittest.cc', 'command_buffer/service/cmd_parser_test.cc', 'command_buffer/service/command_buffer_service_unittest.cc', |