summaryrefslogtreecommitdiffstats
path: root/gpu
diff options
context:
space:
mode:
authorepenner@chromium.org <epenner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-17 09:01:59 +0000
committerepenner@chromium.org <epenner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-17 09:01:59 +0000
commit32145a9eab73bf02ab820f03ec79dcacdfb43932 (patch)
tree95be4c92661eed312b44e299e54e8076798c1c9e /gpu
parent3abf5ba76821e9aff27e2bbde17d913c132ff7b0 (diff)
downloadchromium_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.cc22
-rw-r--r--gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h53
-rw-r--r--gpu/command_buffer/service/common_decoder.cc4
-rw-r--r--gpu/command_buffer/service/common_decoder.h4
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc319
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.h6
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_mock.h4
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc137
-rw-r--r--gpu/command_buffer/service/query_manager.cc30
-rw-r--r--gpu/command_buffer/service/texture_manager.cc48
-rw-r--r--gpu/command_buffer/service/texture_manager.h36
-rw-r--r--gpu/gpu.gyp2
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',