Chromium Code Reviews| Index: gpu/command_buffer/client/gles2_implementation.cc |
| diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc |
| index 2f3194258cb7f92ce51e625cf9472f1e5b61a017..d485c98a24fb359bde4fff54f6b9db0c1d221343 100644 |
| --- a/gpu/command_buffer/client/gles2_implementation.cc |
| +++ b/gpu/command_buffer/client/gles2_implementation.cc |
| @@ -15,6 +15,7 @@ |
| #include <string.h> |
| #include <GLES2/gl2ext.h> |
| #include <GLES2/gl2extchromium.h> |
| +#include "base/bind.h" |
| #include "gpu/command_buffer/client/buffer_tracker.h" |
| #include "gpu/command_buffer/client/gpu_memory_buffer_tracker.h" |
| #include "gpu/command_buffer/client/program_info_manager.h" |
| @@ -107,6 +108,7 @@ GLES2Implementation::GLES2Implementation( |
| bound_array_buffer_id_(0), |
| bound_pixel_pack_transfer_buffer_id_(0), |
| bound_pixel_unpack_transfer_buffer_id_(0), |
| + async_upload_sync_(NULL), |
|
piman
2014/02/07 22:58:20
Also need initializers for async_upload_token_, a
jadahl
2014/02/19 14:06:20
async_upload_token_ is needed indeed, but shm_id a
piman
2014/02/20 01:52:37
Per style guide ( http://google-styleguide.googlec
|
| error_bits_(0), |
| debug_(false), |
| use_count_(0), |
| @@ -154,7 +156,12 @@ bool GLES2Implementation::Initialize( |
| return false; |
| } |
| - mapped_memory_.reset(new MappedMemoryManager(helper_, mapped_memory_limit)); |
| + mapped_memory_.reset( |
| + new MappedMemoryManager(helper_, |
| + base::Bind(&GLES2Implementation::PollAsyncUploads, |
| + base::Unretained(this), |
|
epennerAtGoogle
2014/02/07 20:43:58
Nit: Maybe overkill in this case, but it never hur
|
| + false), |
| + mapped_memory_limit)); |
| unsigned chunk_size = 2 * 1024 * 1024; |
| if (mapped_memory_limit != kNoLimit) { |
| @@ -277,6 +284,14 @@ GLES2Implementation::~GLES2Implementation() { |
| #endif |
| buffer_tracker_.reset(); |
| + // Wait for all async uploads to finish so we can free their buffers. |
| + PollAsyncUploads(true); |
| + |
| + if (async_upload_sync_) { |
| + mapped_memory_->Free(static_cast<void*>(async_upload_sync_)); |
|
piman
2014/02/07 22:58:20
nit: static_cast isn't needed.
|
| + async_upload_sync_ = NULL; |
| + } |
| + |
| // Make sure the commands make it the service. |
| WaitForCmd(); |
| } |
| @@ -1378,13 +1393,8 @@ void GLES2Implementation::BufferDataHelper( |
| } |
| BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffer_id); |
| - if (buffer) { |
| - // Free buffer memory, pending the passage of a token. |
| - buffer_tracker_->FreePendingToken(buffer, helper_->InsertToken()); |
| - |
| - // Remove old buffer. |
| - buffer_tracker_->RemoveBuffer(buffer_id); |
| - } |
| + if (buffer) |
| + RemoveTransferBuffer(buffer); |
| // Create new buffer. |
| buffer = buffer_tracker_->CreateBuffer(buffer_id, size); |
| @@ -1512,6 +1522,32 @@ void GLES2Implementation::BufferSubData( |
| CheckGLError(); |
| } |
| +void GLES2Implementation::RemoveTransferBuffer(BufferTracker::Buffer* buffer) { |
| + int32 token = buffer->last_usage_token(); |
| + uint32 async_token = buffer->async_token(); |
| + |
| + base::subtle::MemoryBarrier(); |
|
epennerAtGoogle
2014/02/07 20:43:58
See nit on other MemoryBarrier. This one also seem
|
| + |
| + if (async_token) { |
| + if (HasAsyncUploadTokenPassed(async_token)) { |
| + buffer_tracker_->Free(buffer); |
| + } else { |
| + detached_async_upload_memory_.push_back( |
| + std::make_pair(buffer->address(), async_token)); |
| + buffer_tracker_->Unmanage(buffer); |
| + } |
| + } else if (token) { |
| + if (helper_->HasTokenPassed(token)) |
| + buffer_tracker_->Free(buffer); |
| + else |
| + buffer_tracker_->FreePendingToken(buffer, token); |
| + } else { |
| + buffer_tracker_->Free(buffer); |
| + } |
| + |
| + buffer_tracker_->RemoveBuffer(buffer->id()); |
| +} |
| + |
| bool GLES2Implementation::GetBoundPixelTransferBuffer( |
| GLenum target, |
| const char* function_name, |
| @@ -1587,7 +1623,7 @@ void GLES2Implementation::CompressedTexImage2D( |
| helper_->CompressedTexImage2D( |
| target, level, internalformat, width, height, border, image_size, |
| buffer->shm_id(), buffer->shm_offset() + offset); |
| - buffer->set_transfer_ready_token(helper_->InsertToken()); |
| + buffer->set_last_usage_token(helper_->InsertToken()); |
| } |
| return; |
| } |
| @@ -1628,7 +1664,7 @@ void GLES2Implementation::CompressedTexSubImage2D( |
| helper_->CompressedTexSubImage2D( |
| target, level, xoffset, yoffset, width, height, format, image_size, |
| buffer->shm_id(), buffer->shm_offset() + offset); |
| - buffer->set_transfer_ready_token(helper_->InsertToken()); |
| + buffer->set_last_usage_token(helper_->InsertToken()); |
| CheckGLError(); |
| } |
| return; |
| @@ -1715,7 +1751,7 @@ void GLES2Implementation::TexImage2D( |
| helper_->TexImage2D( |
| target, level, internalformat, width, height, border, format, type, |
| buffer->shm_id(), buffer->shm_offset() + offset); |
| - buffer->set_transfer_ready_token(helper_->InsertToken()); |
| + buffer->set_last_usage_token(helper_->InsertToken()); |
| CheckGLError(); |
| } |
| return; |
| @@ -1821,7 +1857,7 @@ void GLES2Implementation::TexSubImage2D( |
| helper_->TexSubImage2D( |
| target, level, xoffset, yoffset, width, height, format, type, |
| buffer->shm_id(), buffer->shm_offset() + offset, false); |
| - buffer->set_transfer_ready_token(helper_->InsertToken()); |
| + buffer->set_last_usage_token(helper_->InsertToken()); |
| CheckGLError(); |
| } |
| return; |
| @@ -2404,24 +2440,24 @@ void GLES2Implementation::GenQueriesEXTHelper( |
| // deleted the resource. |
| bool GLES2Implementation::BindBufferHelper( |
| - GLenum target, GLuint buffer) { |
| + GLenum target, GLuint buffer_id) { |
| // TODO(gman): See note #1 above. |
| bool changed = false; |
| switch (target) { |
| case GL_ARRAY_BUFFER: |
| - if (bound_array_buffer_id_ != buffer) { |
| - bound_array_buffer_id_ = buffer; |
| + if (bound_array_buffer_id_ != buffer_id) { |
| + bound_array_buffer_id_ = buffer_id; |
| changed = true; |
| } |
| break; |
| case GL_ELEMENT_ARRAY_BUFFER: |
| - changed = vertex_array_object_manager_->BindElementArray(buffer); |
| + changed = vertex_array_object_manager_->BindElementArray(buffer_id); |
| break; |
| case GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM: |
| - bound_pixel_pack_transfer_buffer_id_ = buffer; |
| + bound_pixel_pack_transfer_buffer_id_ = buffer_id; |
| break; |
| case GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM: |
| - bound_pixel_unpack_transfer_buffer_id_ = buffer; |
| + bound_pixel_unpack_transfer_buffer_id_ = buffer_id; |
| break; |
| default: |
| changed = true; |
| @@ -2429,7 +2465,7 @@ bool GLES2Implementation::BindBufferHelper( |
| } |
| // TODO(gman): There's a bug here. If the target is invalid the ID will not be |
| // used even though it's marked it as used here. |
| - GetIdHandler(id_namespaces::kBuffers)->MarkAsUsedForBind(buffer); |
| + GetIdHandler(id_namespaces::kBuffers)->MarkAsUsedForBind(buffer_id); |
| return changed; |
| } |
| @@ -2563,13 +2599,11 @@ void GLES2Implementation::DeleteBuffersHelper( |
| bound_array_buffer_id_ = 0; |
| } |
| vertex_array_object_manager_->UnbindBuffer(buffers[ii]); |
| + |
| BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffers[ii]); |
| - if (buffer) { |
| - // Free buffer memory, pending the passage of a token. |
| - buffer_tracker_->FreePendingToken(buffer, helper_->InsertToken()); |
| - // Remove buffer. |
| - buffer_tracker_->RemoveBuffer(buffers[ii]); |
| - } |
| + if (buffer) |
| + RemoveTransferBuffer(buffer); |
| + |
| if (buffers[ii] == bound_pixel_unpack_transfer_buffer_id_) { |
| bound_pixel_unpack_transfer_buffer_id_ = 0; |
| } |
| @@ -3591,9 +3625,9 @@ void* GLES2Implementation::MapBufferCHROMIUM(GLuint target, GLenum access) { |
| // with this method of synchronization. Until this is fixed, |
| // MapBufferCHROMIUM will not block even if the transfer is not ready |
| // for these calls. |
| - if (buffer->transfer_ready_token()) { |
| - helper_->WaitForToken(buffer->transfer_ready_token()); |
| - buffer->set_transfer_ready_token(0); |
| + if (buffer->last_usage_token()) { |
| + helper_->WaitForToken(buffer->last_usage_token()); |
| + buffer->set_last_usage_token(0); |
| } |
| buffer->set_mapped(true); |
| @@ -3627,6 +3661,54 @@ GLboolean GLES2Implementation::UnmapBufferCHROMIUM(GLuint target) { |
| return true; |
| } |
| +bool GLES2Implementation::EnsureAsyncUploadSync() { |
| + if (async_upload_sync_) |
| + return true; |
| + |
| + int32 shm_id; |
| + unsigned int shm_offset; |
| + void* mem = mapped_memory_->Alloc(sizeof(AsyncUploadSync), |
| + &shm_id, |
| + &shm_offset); |
| + if (!mem) |
| + return false; |
| + |
| + async_upload_sync_shm_id_ = shm_id; |
| + async_upload_sync_shm_offset_ = shm_offset; |
| + async_upload_sync_ = static_cast<AsyncUploadSync*>(mem); |
| + async_upload_sync_->Reset(); |
| + |
| + return true; |
| +} |
| + |
| +uint32 GLES2Implementation::NextAsyncUploadToken() { |
| + async_upload_token_++; |
| + if (async_upload_token_ == 0) |
| + async_upload_token_++; |
| + return async_upload_token_; |
| +} |
| + |
| +void GLES2Implementation::PollAsyncUploads(bool wait) { |
| + if (!async_upload_sync_) |
| + return; |
| + |
| + do { |
| + base::subtle::MemoryBarrier(); |
|
epennerAtGoogle
2014/02/07 20:43:58
It wouldn't hurt to document each of these base::s
piman
2014/02/07 22:58:20
TBH, I'd rather make the access to the AsyncUpload
jadahl
2014/02/08 09:18:25
To be honest, I added the barrier here mostly beca
piman
2014/02/11 00:09:09
Some useful background: http://software.intel.com/
|
| + DetachedAsyncUploadMemoryList::iterator it = |
| + detached_async_upload_memory_.begin(); |
| + while (it != detached_async_upload_memory_.end()) { |
| + if (HasAsyncUploadTokenPassed(it->second)) { |
|
piman
2014/02/07 22:58:20
Similar to what I mentioned in FencedAllocator, we
|
| + mapped_memory_->Free(it->first); |
| + it = detached_async_upload_memory_.erase(it); |
| + } else { |
| + break; |
| + } |
| + } |
| + if (wait) |
| + WaitForCmd(); |
|
piman
2014/02/07 22:58:20
We need more than this, otherwise we'll spin loop
|
| + } while (!detached_async_upload_memory_.empty() && wait); |
| +} |
| + |
| void GLES2Implementation::AsyncTexImage2DCHROMIUM( |
| GLenum target, GLint level, GLint internalformat, GLsizei width, |
| GLsizei height, GLint border, GLenum format, GLenum type, |
| @@ -3658,7 +3740,12 @@ void GLES2Implementation::AsyncTexImage2DCHROMIUM( |
| if (!pixels && !bound_pixel_unpack_transfer_buffer_id_) { |
| helper_->AsyncTexImage2DCHROMIUM( |
| target, level, internalformat, width, height, border, format, type, |
| - 0, 0); |
| + 0, 0, 0, 0, 0); |
| + return; |
| + } |
| + |
| + if (!EnsureAsyncUploadSync()) { |
| + SetGLError(GL_OUT_OF_MEMORY, "glTexImage2D", "out of memory"); |
| return; |
| } |
| @@ -3671,9 +3758,13 @@ void GLES2Implementation::AsyncTexImage2DCHROMIUM( |
| bound_pixel_unpack_transfer_buffer_id_, |
| "glAsyncTexImage2DCHROMIUM", offset, size); |
| if (buffer && buffer->shm_id() != -1) { |
| + uint32 async_token = NextAsyncUploadToken(); |
| + buffer->set_async_token(async_token); |
| helper_->AsyncTexImage2DCHROMIUM( |
| target, level, internalformat, width, height, border, format, type, |
| - buffer->shm_id(), buffer->shm_offset() + offset); |
| + buffer->shm_id(), buffer->shm_offset() + offset, |
| + async_token, |
| + async_upload_sync_shm_id_, async_upload_sync_shm_offset_); |
| } |
| } |
| @@ -3706,6 +3797,11 @@ void GLES2Implementation::AsyncTexSubImage2DCHROMIUM( |
| return; |
| } |
| + if (!EnsureAsyncUploadSync()) { |
| + SetGLError(GL_OUT_OF_MEMORY, "glTexImage2D", "out of memory"); |
| + return; |
| + } |
| + |
| // Async uploads require a transfer buffer to be bound. |
| // TODO(hubbe): Make MapBufferCHROMIUM block if someone tries to re-use |
| // the buffer before the transfer is finished. (Currently such |
| @@ -3715,9 +3811,13 @@ void GLES2Implementation::AsyncTexSubImage2DCHROMIUM( |
| bound_pixel_unpack_transfer_buffer_id_, |
| "glAsyncTexSubImage2DCHROMIUM", offset, size); |
| if (buffer && buffer->shm_id() != -1) { |
| + uint32 async_token = NextAsyncUploadToken(); |
| + buffer->set_async_token(async_token); |
| helper_->AsyncTexSubImage2DCHROMIUM( |
| target, level, xoffset, yoffset, width, height, format, type, |
| - buffer->shm_id(), buffer->shm_offset() + offset); |
| + buffer->shm_id(), buffer->shm_offset() + offset, |
| + async_token, |
| + async_upload_sync_shm_id_, async_upload_sync_shm_offset_); |
| } |
| } |