diff options
author | epenner@chromium.org <epenner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-13 05:36:44 +0000 |
---|---|---|
committer | epenner@chromium.org <epenner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-13 05:36:44 +0000 |
commit | 5b3a8e0d883a0fb89faa1b23992c9dcaea9ea44d (patch) | |
tree | c52809345af859a1fbe097e635e9f551712cca70 | |
parent | e97f23a7335324fba0cc44a27c5af9e0569d0822 (diff) | |
download | chromium_src-5b3a8e0d883a0fb89faa1b23992c9dcaea9ea44d.zip chromium_src-5b3a8e0d883a0fb89faa1b23992c9dcaea9ea44d.tar.gz chromium_src-5b3a8e0d883a0fb89faa1b23992c9dcaea9ea44d.tar.bz2 |
gpu: Clean up pending async transfers.
Async transfers are currently tracked in the texture
manager. This gives less flexibility to the async
implementation, and has much higher coupling between
the decoder/async-delegate. This patch moves everything
into the delegate.
BUG=161337
Review URL: https://chromiumcodereview.appspot.com/12330118
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@187790 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h | 11 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder.cc | 88 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc | 50 | ||||
-rw-r--r-- | gpu/command_buffer/service/texture_manager.cc | 48 | ||||
-rw-r--r-- | gpu/command_buffer/service/texture_manager.h | 25 | ||||
-rw-r--r-- | ui/gl/async_pixel_transfer_delegate.h | 24 | ||||
-rw-r--r-- | ui/gl/async_pixel_transfer_delegate_android.cc | 146 | ||||
-rw-r--r-- | ui/gl/async_pixel_transfer_delegate_stub.cc | 33 | ||||
-rw-r--r-- | ui/gl/async_pixel_transfer_delegate_stub.h | 12 |
9 files changed, 210 insertions, 227 deletions
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h b/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h index 7432737..bc72269 100644 --- a/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h +++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h @@ -30,15 +30,18 @@ class MockAsyncPixelTransferDelegate : public gfx::AsyncPixelTransferDelegate { virtual ~MockAsyncPixelTransferDelegate(); // Implement AsyncPixelTransferDelegate. - MOCK_METHOD1(CreateRawPixelTransferState, - gfx::AsyncPixelTransferState*(GLuint service_id)); + MOCK_METHOD2(CreateRawPixelTransferState, + gfx::AsyncPixelTransferState*( + GLuint service_id, const AsyncTexImage2DParams& define_params)); + MOCK_METHOD0(BindCompletedAsyncTransfers, bool()); MOCK_METHOD2(AsyncNotifyCompletion, void(const AsyncMemoryParams& mem_params, const CompletionCallback& callback)); - MOCK_METHOD3(AsyncTexImage2D, + MOCK_METHOD4(AsyncTexImage2D, void(gfx::AsyncPixelTransferState*, const AsyncTexImage2DParams& tex_params, - const AsyncMemoryParams& mem_params)); + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback)); MOCK_METHOD3(AsyncTexSubImage2D, void(gfx::AsyncPixelTransferState*, const AsyncTexSubImage2DParams& tex_params, diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 94de0f5..1d642fd 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -2857,23 +2857,13 @@ void GLES2DecoderImpl::ProcessFinishedAsyncTransfers() { if (engine() && query_manager_.get()) query_manager_->ProcessPendingTransferQueries(); - // 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) + // TODO(epenner): Is there a better place to do this? + // This needs to occur before we execute any batch of commands + // from the client, as the client may have recieved an async + // completion while issuing those commands. + // "DidFlushStart" would be ideal if we had such a callback. + if (async_pixel_transfer_delegate_->BindCompletedAsyncTransfers()) 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(); - } } void GLES2DecoderImpl::ReleaseCurrent() { @@ -10197,6 +10187,15 @@ error::Error GLES2DecoderImpl::HandleAsyncTexImage2DCHROMIUM( return error::kNoError; } + // Since we don't allow async redefinition, this is the only + // time the size of this texture can change while bound + // as a frame-buffer. + if (texture->IsAttachedToFramebuffer()) { + // TODO(gman): If textures tracked which framebuffers they were attached to + // we could just mark those framebuffers as not complete. + framebuffer_manager()->IncFramebufferStateChangeCount(); + } + if (!EnsureGPUMemoryAvailable(pixels_size)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glAsyncTexImage2DCHROMIUM", "out of memory"); @@ -10211,16 +10210,7 @@ error::Error GLES2DecoderImpl::HandleAsyncTexImage2DCHROMIUM( 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 texture - // is set up lazily when the transfer completes. - DCHECK(!texture->GetAsyncTransferState()); - texture->SetAsyncTransferState( - async_pixel_transfer_delegate_-> - CreatePixelTransferState(texture->service_id())); - texture->SetImmutable(true); - - // Issue the async call and set up the texture. + // Setup the parameters. GLenum gl_internal_format = GetTexInternalFormat(internal_format, format, type); gfx::AsyncTexImage2DParams tex_params = {target, level, gl_internal_format, @@ -10228,14 +10218,27 @@ error::Error GLES2DecoderImpl::HandleAsyncTexImage2DCHROMIUM( 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 texture at the same time, - // after the the transfer is complete. - texture_manager()->AddPendingAsyncPixelTransfer( - texture->GetAsyncTransferState()->AsWeakPtr(), texture); + // 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(!texture->GetAsyncTransferState()); + texture->SetAsyncTransferState( + async_pixel_transfer_delegate_-> + CreatePixelTransferState(texture->service_id(), + tex_params)); + texture->SetImmutable(true); async_pixel_transfer_delegate_->AsyncTexImage2D( - texture->GetAsyncTransferState(), tex_params, mem_params); + texture->GetAsyncTransferState(), + tex_params, + mem_params, + base::Bind(&TextureManager::SetLevelInfoFromParams, + // The callback is only invoked if the transfer state + // still exists, which implies through manager->info->state + // ownership that both of these pointers are valid. + base::Unretained(texture_manager()), + base::Unretained(texture), + tex_params)); return error::kNoError; } @@ -10298,19 +10301,30 @@ error::Error GLES2DecoderImpl::HandleAsyncTexSubImage2DCHROMIUM( uint32 shm_data_offset = c.data_shm_offset; uint32 shm_data_size = data_size; + // Setup the parameters. + 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}; if (!texture->GetAsyncTransferState()) { + // TODO(epenner): We may want to enforce exclusive use + // of async APIs in which case this should become an error, + // (the texture should have been async defined). + gfx::AsyncTexImage2DParams define_params = {target, level, + 0, 0, 0, 0, 0, 0}; + texture->GetLevelSize(target, level, &define_params.width, + &define_params.height); + texture->GetLevelType(target, level, &define_params.type, + &define_params.internal_format); // Set up the async state if needed, and make the texture // immutable so the async state stays valid. texture->SetAsyncTransferState( async_pixel_transfer_delegate_-> - CreatePixelTransferState(texture->service_id())); + CreatePixelTransferState(texture->service_id(), + define_params)); texture->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( texture->GetAsyncTransferState(), tex_params, mem_params); return error::kNoError; diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc index e4dd9cb..a385f81 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc @@ -38,6 +38,7 @@ using ::testing::Invoke; using ::testing::MatcherCast; using ::testing::Pointee; using ::testing::Return; +using ::testing::SaveArg; using ::testing::SetArrayArgument; using ::testing::SetArgumentPointee; using ::testing::SetArgPointee; @@ -8000,20 +8001,21 @@ TEST_F(GLES2DecoderManualInitTest, AsyncPixelTransfers) { GL_UNSIGNED_BYTE, kSharedMemoryId, kSharedMemoryOffset); WaitAsyncTexImage2DCHROMIUM wait_cmd; wait_cmd.Init(GL_TEXTURE_2D); - 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(texture->GetAsyncTransferState()); + base::Closure bind_callback; + // AsyncTexImage2D { // Create transfer state since it doesn't exist. - EXPECT_CALL(*delegate, CreateRawPixelTransferState(kServiceTextureId)) + EXPECT_CALL(*delegate, CreateRawPixelTransferState(kServiceTextureId, _)) .WillOnce(Return( state = new StrictMock<gfx::MockAsyncPixelTransferState>)) .RetiresOnSaturation(); - EXPECT_CALL(*delegate, AsyncTexImage2D(state, _, _)) + EXPECT_CALL(*delegate, AsyncTexImage2D(state, _, _, _)) + .WillOnce(SaveArg<3>(&bind_callback)) .RetiresOnSaturation(); // Command succeeds. EXPECT_EQ(error::kNoError, ExecuteCmd(teximage_cmd)); @@ -8035,24 +8037,17 @@ TEST_F(GLES2DecoderManualInitTest, AsyncPixelTransfers) { EXPECT_TRUE(texture->SafeToRenderFrom()); } - // Lazy binding/defining of the async transfer + // 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 texture. - 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. + // TODO(epenner): We should check that the delegate gets the + // BindCompletedAsyncTransfers() call, which is required to + // guarantee the delegate calls the bind callback. + + // Simulate the bind callback from the delegate. + bind_callback.Run(); + + // After the bind callback is run, the texture is safe, + // and has the right size etc. EXPECT_TRUE(texture->SafeToRenderFrom()); GLsizei width, height; EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height)); @@ -8065,7 +8060,7 @@ TEST_F(GLES2DecoderManualInitTest, AsyncPixelTransfers) { texture->SetImmutable(false); { // Create transfer state since it doesn't exist. - EXPECT_CALL(*delegate, CreateRawPixelTransferState(kServiceTextureId)) + EXPECT_CALL(*delegate, CreateRawPixelTransferState(kServiceTextureId, _)) .WillOnce(Return( state = new StrictMock<gfx::MockAsyncPixelTransferState>)) .RetiresOnSaturation(); @@ -8119,11 +8114,11 @@ TEST_F(GLES2DecoderManualInitTest, AsyncPixelTransfers) { texture->SetAsyncTransferState(scoped_ptr<gfx::AsyncPixelTransferState>()); texture->SetImmutable(false); // Create transfer state since it doesn't exist. - EXPECT_CALL(*delegate, CreateRawPixelTransferState(kServiceTextureId)) + EXPECT_CALL(*delegate, CreateRawPixelTransferState(kServiceTextureId, _)) .WillOnce(Return( state = new StrictMock<gfx::MockAsyncPixelTransferState>)) .RetiresOnSaturation(); - EXPECT_CALL(*delegate, AsyncTexImage2D(state, _, _)) + EXPECT_CALL(*delegate, AsyncTexImage2D(state, _, _, _)) .RetiresOnSaturation(); // Start async transfer. EXPECT_EQ(error::kNoError, ExecuteCmd(teximage_cmd)); @@ -8132,11 +8127,8 @@ TEST_F(GLES2DecoderManualInitTest, AsyncPixelTransfers) { EXPECT_TRUE(texture->IsImmutable()); // Wait for completion. EXPECT_CALL(*delegate, WaitForTransferCompletion(state)); - EXPECT_CALL(*state, TransferIsInProgress()) - .WillOnce(Return(false)) - .RetiresOnSaturation(); - EXPECT_CALL(*state, BindTransfer(_)) - .WillOnce(SetArgPointee<0>(teximage_params)) + EXPECT_CALL(*delegate, BindCompletedAsyncTransfers()) + .WillOnce(Return(true)) .RetiresOnSaturation(); // State restoration after binding. EXPECT_CALL(*gl_, ActiveTexture(_)); diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc index 4b6fe58..8f72cf4 100644 --- a/gpu/command_buffer/service/texture_manager.cc +++ b/gpu/command_buffer/service/texture_manager.cc @@ -1230,53 +1230,5 @@ void TextureManager::AddToSignature( texture->AddToSignature(feature_info_.get(), target, level, signature); } -void TextureManager::AddPendingAsyncPixelTransfer( - base::WeakPtr<gfx::AsyncPixelTransferState> state, Texture* texture) { - pending_async_transfers_.push_back(PendingAsyncTransfer(state, texture)); -} - -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 Texture. So if the - // async state is deleted, so is the Texture. - 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 Texture, 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 18dda7a..f1d1fea 100644 --- a/gpu/command_buffer/service/texture_manager.h +++ b/gpu/command_buffer/service/texture_manager.h @@ -448,6 +448,16 @@ class GPU_EXPORT TextureManager { GLenum type, bool cleared); + // Adapter to call above function. + void SetLevelInfoFromParams(Texture* texture, + const gfx::AsyncTexImage2DParams& params) { + SetLevelInfo( + texture, params.target, params.level, params.internal_format, + params.width, params.height, 1 /* depth */, + params.border, params.format, + params.type, true /* cleared */ ); + } + // Save the texture definition and leave it undefined. TextureDefinition* Save(Texture* texture); @@ -554,13 +564,6 @@ class GPU_EXPORT TextureManager { GLint level, std::string* signature) const; - // Transfers added will get their Texture updated at the same time - // the async transfer is bound to the real texture. - void AddPendingAsyncPixelTransfer( - base::WeakPtr<gfx::AsyncPixelTransferState> state, Texture* texture); - void BindFinishedAsyncPixelTransfers(bool* texture_dirty, - bool* framebuffer_dirty); - private: friend class Texture; @@ -605,14 +608,6 @@ class GPU_EXPORT TextureManager { // The default textures for each target (texture name = 0) scoped_refptr<Texture> default_textures_[kNumDefaultTextures]; - // Async texture allocations which haven't been bound to their textures - // yet. This facilitates updating the Texture at the same time the - // real texture data is bound. - typedef std::pair<base::WeakPtr<gfx::AsyncPixelTransferState>, - Texture*> PendingAsyncTransfer; - typedef std::list<PendingAsyncTransfer> PendingAsyncTransferList; - PendingAsyncTransferList pending_async_transfers_; - DISALLOW_COPY_AND_ASSIGN(TextureManager); }; diff --git a/ui/gl/async_pixel_transfer_delegate.h b/ui/gl/async_pixel_transfer_delegate.h index e2d1d6c..44bcbcb 100644 --- a/ui/gl/async_pixel_transfer_delegate.h +++ b/ui/gl/async_pixel_transfer_delegate.h @@ -60,18 +60,10 @@ class GL_EXPORT AsyncPixelTransferState : // Returns true if there is a transfer in progress. virtual bool TransferIsInProgress() = 0; - // Perform any custom binding of the transfer (currently only - // needed for AsyncTexImage2D). The params used to define the texture - // are returned in level_params. - // - // The transfer must be complete to call this (!TransferIsInProgress). - virtual void BindTransfer(AsyncTexImage2DParams* level_params) = 0; - protected: friend class base::RefCounted<AsyncPixelTransferState>; AsyncPixelTransferState() {} - private: DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferState); }; @@ -87,19 +79,26 @@ class GL_EXPORT AsyncPixelTransferDelegate { // This wouldn't work with MOCK_METHOD, so it routes through // a virtual protected version which returns a raw pointer. scoped_ptr<AsyncPixelTransferState> - CreatePixelTransferState(GLuint texture_id) { - return make_scoped_ptr(CreateRawPixelTransferState(texture_id)); + CreatePixelTransferState(GLuint tex_id, + const AsyncTexImage2DParams& define_params) { + return make_scoped_ptr(CreateRawPixelTransferState(tex_id, define_params)); } + // Returns true iff a texture was bound to texture-unit zero. + virtual bool BindCompletedAsyncTransfers() = 0; + // There's no guarantee that callback will run on the caller thread. virtual void AsyncNotifyCompletion( const AsyncMemoryParams& mem_params, const CompletionCallback& callback) = 0; + // The callback occurs on the caller thread, once the texture is + // safe/ready to be used. virtual void AsyncTexImage2D( AsyncPixelTransferState* state, const AsyncTexImage2DParams& tex_params, - const AsyncMemoryParams& mem_params) = 0; + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback) = 0; virtual void AsyncTexSubImage2D( AsyncPixelTransferState* state, @@ -117,7 +116,8 @@ class GL_EXPORT AsyncPixelTransferDelegate { AsyncPixelTransferDelegate() {} // For testing, as returning scoped_ptr wouldn't work with MOCK_METHOD. virtual AsyncPixelTransferState* - CreateRawPixelTransferState(GLuint texture_id) = 0; + CreateRawPixelTransferState(GLuint tex_id, + const AsyncTexImage2DParams& define_params) = 0; private: DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegate); diff --git a/ui/gl/async_pixel_transfer_delegate_android.cc b/ui/gl/async_pixel_transfer_delegate_android.cc index 85a4374..6a34639 100644 --- a/ui/gl/async_pixel_transfer_delegate_android.cc +++ b/ui/gl/async_pixel_transfer_delegate_android.cc @@ -4,6 +4,7 @@ #include "ui/gl/async_pixel_transfer_delegate_android.h" +#include <list> #include <string> #include "base/bind.h" @@ -183,19 +184,18 @@ class TransferStateInternal : public base::RefCountedThreadSafe<TransferStateInternal> { public: explicit TransferStateInternal(GLuint texture_id, + const AsyncTexImage2DParams& define_params, bool wait_for_uploads, bool wait_for_creation, bool use_image_preserved) : texture_id_(texture_id), thread_texture_id_(0), - needs_late_bind_(false), transfer_completion_(true, true), egl_image_(EGL_NO_IMAGE_KHR), wait_for_uploads_(wait_for_uploads), wait_for_creation_(wait_for_creation), use_image_preserved_(use_image_preserved) { - static const AsyncTexImage2DParams zero_params = {0, 0, 0, 0, 0, 0, 0, 0}; - late_bind_define_params_ = zero_params; + define_params_ = define_params; } // Implement AsyncPixelTransferState: @@ -203,15 +203,11 @@ class TransferStateInternal return !transfer_completion_.IsSignaled(); } - void BindTransfer(AsyncTexImage2DParams* bound_params) { + void BindTransfer() { TRACE_EVENT2("gpu", "BindAsyncTransfer glEGLImageTargetTexture2DOES", - "width", late_bind_define_params_.width, - "height", late_bind_define_params_.height); - DCHECK(bound_params); + "width", define_params_.width, + "height", define_params_.height); DCHECK(texture_id_); - *bound_params = late_bind_define_params_; - if (!needs_late_bind_) - return; DCHECK_NE(EGL_NO_IMAGE_KHR, egl_image_); // We can only change the active texture and unit 0, @@ -219,7 +215,7 @@ class TransferStateInternal glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture_id_); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_); - needs_late_bind_ = false; + bind_callback_.Run(); DCHECK(CHECK_GL()); } @@ -313,12 +309,8 @@ class TransferStateInternal // The EGLImage sibling on the upload thread. GLuint thread_texture_id_; - // Indicates there is a new EGLImage and the 'real' - // texture needs to be bound to it as an EGLImage target. - bool needs_late_bind_; - // Definition params for texture that needs binding. - AsyncTexImage2DParams late_bind_define_params_; + AsyncTexImage2DParams define_params_; // Indicates that an async transfer is in progress. base::WaitableEvent transfer_completion_; @@ -328,6 +320,11 @@ class TransferStateInternal // one for the lifetime of the texture. EGLImageKHR egl_image_; + // Callback to invoke when AsyncTexImage2D is complete + // and the client can safely use the texture. This occurs + // during BindCompletedAsyncTransfers(). + base::Closure bind_callback_; + // Customize when we block on fences (these are work-arounds). bool wait_for_uploads_; bool wait_for_creation_; @@ -338,11 +335,14 @@ class TransferStateInternal // an internal thread-safe ref-counted state object. class AsyncTransferStateAndroid : public AsyncPixelTransferState { public: - explicit AsyncTransferStateAndroid(GLuint texture_id, - bool wait_for_uploads, - bool wait_for_creation, - bool use_image_preserved) + explicit AsyncTransferStateAndroid( + GLuint texture_id, + const AsyncTexImage2DParams& define_params, + bool wait_for_uploads, + bool wait_for_creation, + bool use_image_preserved) : internal_(new TransferStateInternal(texture_id, + define_params, wait_for_uploads, wait_for_creation, use_image_preserved)) { @@ -351,8 +351,8 @@ class AsyncTransferStateAndroid : public AsyncPixelTransferState { virtual bool TransferIsInProgress() OVERRIDE { return internal_->TransferIsInProgress(); } - virtual void BindTransfer(AsyncTexImage2DParams* bound_params) OVERRIDE { - internal_->BindTransfer(bound_params); + void BindTransfer() { + internal_->BindTransfer(); } scoped_refptr<TransferStateInternal> internal_; }; @@ -367,13 +367,15 @@ class AsyncPixelTransferDelegateAndroid virtual ~AsyncPixelTransferDelegateAndroid(); // implement AsyncPixelTransferDelegate: + virtual bool BindCompletedAsyncTransfers() OVERRIDE; virtual void AsyncNotifyCompletion( const AsyncMemoryParams& mem_params, const CompletionCallback& callback) OVERRIDE; virtual void AsyncTexImage2D( AsyncPixelTransferState* state, const AsyncTexImage2DParams& tex_params, - const AsyncMemoryParams& mem_params) OVERRIDE; + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback) OVERRIDE; virtual void AsyncTexSubImage2D( AsyncPixelTransferState* state, const AsyncTexSubImage2DParams& tex_params, @@ -386,7 +388,8 @@ class AsyncPixelTransferDelegateAndroid private: // implement AsyncPixelTransferDelegate: virtual AsyncPixelTransferState* - CreateRawPixelTransferState(GLuint texture_id) OVERRIDE; + CreateRawPixelTransferState(GLuint texture_id, + const AsyncTexImage2DParams& define_params) OVERRIDE; static void PerformNotifyCompletion( AsyncMemoryParams mem_params, @@ -406,14 +409,18 @@ class AsyncPixelTransferDelegateAndroid // Returns true if a work-around was used. bool WorkAroundAsyncTexImage2D( - TransferStateInternal* state, + AsyncPixelTransferState* state, const AsyncTexImage2DParams& tex_params, - const AsyncMemoryParams& mem_params); + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback); bool WorkAroundAsyncTexSubImage2D( - TransferStateInternal* state, + AsyncPixelTransferState* state, const AsyncTexSubImage2DParams& tex_params, const AsyncMemoryParams& mem_params); + typedef std::list<base::WeakPtr<AsyncPixelTransferState> > TransferQueue; + TransferQueue pending_allocations_; + scoped_refptr<TextureUploadStats> texture_upload_stats_; bool is_imagination_; bool is_qualcomm_; @@ -458,8 +465,8 @@ AsyncPixelTransferDelegateAndroid::~AsyncPixelTransferDelegateAndroid() { AsyncPixelTransferState* AsyncPixelTransferDelegateAndroid:: - CreateRawPixelTransferState(GLuint texture_id) { - + CreateRawPixelTransferState(GLuint texture_id, + const AsyncTexImage2DParams& define_params) { // We can't wait on uploads on imagination (it can take 200ms+). // In practice, they are complete when the CPU glTexSubImage2D completes. bool wait_for_uploads = !is_imagination_; @@ -478,11 +485,34 @@ AsyncPixelTransferState* return static_cast<AsyncPixelTransferState*>( new AsyncTransferStateAndroid(texture_id, + define_params, wait_for_uploads, wait_for_creation, use_image_preserved)); } +bool AsyncPixelTransferDelegateAndroid::BindCompletedAsyncTransfers() { + bool texture_dirty = false; + while(!pending_allocations_.empty()) { + if (!pending_allocations_.front().get()) { + pending_allocations_.pop_front(); + continue; + } + scoped_refptr<TransferStateInternal> state = + static_cast<AsyncTransferStateAndroid*> + (pending_allocations_.front().get())->internal_.get(); + // Terminate early, as all transfers finish in order, currently. + if (state->TransferIsInProgress()) + break; + // If the transfer is finished, bind it to the texture + // and remove it from pending list. + texture_dirty = true; + state->BindTransfer(); + pending_allocations_.pop_front(); + } + return texture_dirty; +} + void AsyncPixelTransferDelegateAndroid::AsyncNotifyCompletion( const AsyncMemoryParams& mem_params, const CompletionCallback& callback) { @@ -531,7 +561,12 @@ void AsyncPixelTransferDelegateAndroid::WaitForTransferCompletion( void AsyncPixelTransferDelegateAndroid::AsyncTexImage2D( AsyncPixelTransferState* transfer_state, const AsyncTexImage2DParams& tex_params, - const AsyncMemoryParams& mem_params) { + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback) { + if (WorkAroundAsyncTexImage2D(transfer_state, tex_params, + mem_params, bind_callback)) + return; + scoped_refptr<TransferStateInternal> state = static_cast<AsyncTransferStateAndroid*>(transfer_state)->internal_.get(); DCHECK(mem_params.shared_memory); @@ -539,23 +574,20 @@ void AsyncPixelTransferDelegateAndroid::AsyncTexImage2D( mem_params.shm_size); DCHECK(state); DCHECK(state->texture_id_); - DCHECK(!state->needs_late_bind_); DCHECK(!state->TransferIsInProgress()); DCHECK_EQ(state->egl_image_, EGL_NO_IMAGE_KHR); DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target); DCHECK_EQ(tex_params.level, 0); - if (WorkAroundAsyncTexImage2D(state, tex_params, mem_params)) - return; - - // Mark the transfer in progress and save define params for lazy binding. - state->needs_late_bind_ = true; - state->late_bind_define_params_ = tex_params; + // Mark the transfer in progress and save the late bind + // callback, so we can notify the client when it is bound. + pending_allocations_.push_back(transfer_state->AsWeakPtr()); + state->bind_callback_ = bind_callback; // Mark the transfer in progress. state->MarkAsTransferIsInProgress(); - // Duplicate the shared memory so there are no way we can get + // Duplicate the shared memory so there is no way we can get // a use-after-free of the raw pixels. transfer_message_loop_proxy()->PostTask(FROM_HERE, base::Bind( @@ -567,6 +599,7 @@ void AsyncPixelTransferDelegateAndroid::AsyncTexImage2D( mem_params.shared_memory, mem_params.shm_size)))); + DCHECK(CHECK_GL()); } @@ -577,8 +610,11 @@ void AsyncPixelTransferDelegateAndroid::AsyncTexSubImage2D( TRACE_EVENT2("gpu", "AsyncTexSubImage2D", "width", tex_params.width, "height", tex_params.height); + if (WorkAroundAsyncTexSubImage2D(transfer_state, tex_params, mem_params)) + return; scoped_refptr<TransferStateInternal> state = static_cast<AsyncTransferStateAndroid*>(transfer_state)->internal_.get(); + DCHECK(state->texture_id_); DCHECK(!state->TransferIsInProgress()); DCHECK(mem_params.shared_memory); @@ -587,9 +623,6 @@ void AsyncPixelTransferDelegateAndroid::AsyncTexSubImage2D( DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target); DCHECK_EQ(tex_params.level, 0); - if (WorkAroundAsyncTexSubImage2D(state, tex_params, mem_params)) - return; - // Mark the transfer in progress. state->MarkAsTransferIsInProgress(); @@ -773,11 +806,14 @@ bool DimensionsSupportImgFastPath(int width, int height) { // on purely synchronous allocation/upload on the main thread. bool AsyncPixelTransferDelegateAndroid::WorkAroundAsyncTexImage2D( - TransferStateInternal* state, + AsyncPixelTransferState* transfer_state, const AsyncTexImage2DParams& tex_params, - const AsyncMemoryParams& mem_params) { + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback) { if (!is_imagination_) return false; + scoped_refptr<TransferStateInternal> state = + static_cast<AsyncTransferStateAndroid*>(transfer_state)->internal_.get(); // On imagination we allocate synchronously all the time, even // if the dimensions support fast uploads. This is for part a.) @@ -793,8 +829,6 @@ bool AsyncPixelTransferDelegateAndroid::WorkAroundAsyncTexImage2D( // The allocation has already occured, so mark it as finished // and ready for binding. - state->needs_late_bind_ = false; - state->late_bind_define_params_ = tex_params; CHECK(!state->TransferIsInProgress()); // If the dimensions support fast async uploads, create the @@ -803,7 +837,8 @@ bool AsyncPixelTransferDelegateAndroid::WorkAroundAsyncTexImage2D( // texture, but this is required to prevent an imagination driver crash. if (DimensionsSupportImgFastPath(tex_params.width, tex_params.height)) { state->CreateEglImageOnMainThreadIfNeeded(); - state->needs_late_bind_ = true; + pending_allocations_.push_back(transfer_state->AsWeakPtr()); + state->bind_callback_ = bind_callback; } DCHECK(CHECK_GL()); @@ -811,7 +846,7 @@ bool AsyncPixelTransferDelegateAndroid::WorkAroundAsyncTexImage2D( } bool AsyncPixelTransferDelegateAndroid::WorkAroundAsyncTexSubImage2D( - TransferStateInternal* state, + AsyncPixelTransferState* transfer_state, const AsyncTexSubImage2DParams& tex_params, const AsyncMemoryParams& mem_params) { if (!is_imagination_) @@ -822,6 +857,9 @@ bool AsyncPixelTransferDelegateAndroid::WorkAroundAsyncTexSubImage2D( if (DimensionsSupportImgFastPath(tex_params.width, tex_params.height)) return false; + scoped_refptr<TransferStateInternal> state = + static_cast<AsyncTransferStateAndroid*>(transfer_state)->internal_.get(); + // Fall back on a synchronous stub as we don't have a known fast path. // Also, older ICS drivers crash when we do any glTexSubImage2D on the // same thread. To work around this we do glTexImage2D instead. Since @@ -830,11 +868,11 @@ bool AsyncPixelTransferDelegateAndroid::WorkAroundAsyncTexSubImage2D( DCHECK(!state->egl_image_); DCHECK_EQ(tex_params.xoffset, 0); DCHECK_EQ(tex_params.yoffset, 0); - DCHECK_EQ(state->late_bind_define_params_.width, tex_params.width); - DCHECK_EQ(state->late_bind_define_params_.height, tex_params.height); - DCHECK_EQ(state->late_bind_define_params_.level, tex_params.level); - DCHECK_EQ(state->late_bind_define_params_.format, tex_params.format); - DCHECK_EQ(state->late_bind_define_params_.type, tex_params.type); + DCHECK_EQ(state->define_params_.width, tex_params.width); + DCHECK_EQ(state->define_params_.height, tex_params.height); + DCHECK_EQ(state->define_params_.level, tex_params.level); + DCHECK_EQ(state->define_params_.format, tex_params.format); + DCHECK_EQ(state->define_params_.type, tex_params.type); void* data = GetAddress(mem_params.shared_memory, mem_params.shm_data_offset); @@ -843,9 +881,9 @@ bool AsyncPixelTransferDelegateAndroid::WorkAroundAsyncTexSubImage2D( begin_time = base::TimeTicks::HighResNow(); { TRACE_EVENT0("gpu", "glTexSubImage2D"); - // Note we use late_bind_define_params_ instead of tex_params. + // Note we use define_params_ instead of tex_params. // The DCHECKs above verify this is always the same. - DoTexImage2D(state->late_bind_define_params_, data); + DoTexImage2D(state->define_params_, data); } if (texture_upload_stats_) { texture_upload_stats_->AddUpload( diff --git a/ui/gl/async_pixel_transfer_delegate_stub.cc b/ui/gl/async_pixel_transfer_delegate_stub.cc index 093cb11..2ec16bb 100644 --- a/ui/gl/async_pixel_transfer_delegate_stub.cc +++ b/ui/gl/async_pixel_transfer_delegate_stub.cc @@ -43,9 +43,6 @@ scoped_ptr<AsyncPixelTransferDelegate> } AsyncTransferStateStub::AsyncTransferStateStub(GLuint texture_id) { - static const AsyncTexImage2DParams zero_params = {0, 0, 0, 0, 0, 0, 0, 0}; - late_bind_define_params_ = zero_params; - needs_late_bind_ = false; } AsyncTransferStateStub::~AsyncTransferStateStub() { @@ -55,13 +52,6 @@ bool AsyncTransferStateStub::TransferIsInProgress() { return false; } -void AsyncTransferStateStub::BindTransfer(AsyncTexImage2DParams* out_params) { - DCHECK(out_params); - DCHECK(needs_late_bind_); - *out_params = late_bind_define_params_; - needs_late_bind_ = false; -} - AsyncPixelTransferDelegateStub::AsyncPixelTransferDelegateStub() : texture_upload_count_(0) { } @@ -71,11 +61,17 @@ AsyncPixelTransferDelegateStub::~AsyncPixelTransferDelegateStub() { AsyncPixelTransferState* AsyncPixelTransferDelegateStub::CreateRawPixelTransferState( - GLuint texture_id) { + GLuint texture_id, + const AsyncTexImage2DParams& define_params) { return static_cast<AsyncPixelTransferState*>( new AsyncTransferStateStub(texture_id)); } +bool AsyncPixelTransferDelegateStub::BindCompletedAsyncTransfers() { + // Everything is already bound. + return false; +} + void AsyncPixelTransferDelegateStub::AsyncNotifyCompletion( const AsyncMemoryParams& mem_params, const CompletionCallback& callback) { @@ -85,17 +81,11 @@ void AsyncPixelTransferDelegateStub::AsyncNotifyCompletion( void AsyncPixelTransferDelegateStub::AsyncTexImage2D( AsyncPixelTransferState* transfer_state, const AsyncTexImage2DParams& tex_params, - const AsyncMemoryParams& mem_params) { + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback) { // Save the define params to return later during deferred // binding of the transfer texture. DCHECK(transfer_state); - AsyncTransferStateStub* state = - static_cast<AsyncTransferStateStub*>(transfer_state); - // We don't actually need a late bind since this stub does - // everything synchronously, but this tries to be similar - // as an async implementation. - state->needs_late_bind_ = true; - state->late_bind_define_params_ = tex_params; void* data = GetAddress(mem_params.shared_memory, mem_params.shm_size, mem_params.shm_data_offset, @@ -110,6 +100,8 @@ void AsyncPixelTransferDelegateStub::AsyncTexImage2D( tex_params.format, tex_params.type, data); + // The texture is already fully bound so just call it now. + bind_callback.Run(); } void AsyncPixelTransferDelegateStub::AsyncTexSubImage2D( @@ -121,9 +113,6 @@ void AsyncPixelTransferDelegateStub::AsyncTexSubImage2D( mem_params.shm_data_offset, mem_params.shm_data_size); DCHECK(transfer_state); - AsyncTransferStateStub* state = - static_cast<AsyncTransferStateStub*>(transfer_state); - DCHECK(!state->needs_late_bind_); base::TimeTicks begin_time(base::TimeTicks::HighResNow()); glTexSubImage2D( tex_params.target, diff --git a/ui/gl/async_pixel_transfer_delegate_stub.h b/ui/gl/async_pixel_transfer_delegate_stub.h index cb21d8c..ae90342 100644 --- a/ui/gl/async_pixel_transfer_delegate_stub.h +++ b/ui/gl/async_pixel_transfer_delegate_stub.h @@ -13,12 +13,9 @@ class AsyncTransferStateStub : public AsyncPixelTransferState { public: // implement AsyncPixelTransferState: virtual bool TransferIsInProgress() OVERRIDE; - virtual void BindTransfer(AsyncTexImage2DParams* bound_params) OVERRIDE; private: friend class AsyncPixelTransferDelegateStub; - bool needs_late_bind_; - AsyncTexImage2DParams late_bind_define_params_ ; explicit AsyncTransferStateStub(GLuint texture_id); virtual ~AsyncTransferStateStub(); @@ -36,13 +33,15 @@ class AsyncPixelTransferDelegateStub : public AsyncPixelTransferDelegate { virtual ~AsyncPixelTransferDelegateStub(); // implement AsyncPixelTransferDelegate: + virtual bool BindCompletedAsyncTransfers() OVERRIDE; virtual void AsyncNotifyCompletion( const AsyncMemoryParams& mem_params, const CompletionCallback& callback) OVERRIDE; virtual void AsyncTexImage2D( - AsyncPixelTransferState* transfer_state, + AsyncPixelTransferState* state, const AsyncTexImage2DParams& tex_params, - const AsyncMemoryParams& mem_params) OVERRIDE; + const AsyncMemoryParams& mem_params, + const base::Closure& bind_callback) OVERRIDE; virtual void AsyncTexSubImage2D( AsyncPixelTransferState* transfer_state, const AsyncTexSubImage2DParams& tex_params, @@ -54,7 +53,8 @@ class AsyncPixelTransferDelegateStub : public AsyncPixelTransferDelegate { private: // implement AsyncPixelTransferDelegate: virtual AsyncPixelTransferState* - CreateRawPixelTransferState(GLuint texture_id) OVERRIDE; + CreateRawPixelTransferState(GLuint texture_id, + const AsyncTexImage2DParams& define_params) OVERRIDE; int texture_upload_count_; base::TimeDelta total_texture_upload_time_; |