|  | // 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/client/share_group.h" | 
|  |  | 
|  | #include <stack> | 
|  | #include <vector> | 
|  | #include "base/basictypes.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/synchronization/lock.h" | 
|  | #include "gpu/command_buffer/client/gles2_cmd_helper.h" | 
|  | #include "gpu/command_buffer/client/gles2_implementation.h" | 
|  | #include "gpu/command_buffer/client/program_info_manager.h" | 
|  | #include "gpu/command_buffer/common/id_allocator.h" | 
|  |  | 
|  | namespace gpu { | 
|  | namespace gles2 { | 
|  |  | 
|  | ShareGroupContextData::IdHandlerData::IdHandlerData() : flush_generation_(0) {} | 
|  | ShareGroupContextData::IdHandlerData::~IdHandlerData() {} | 
|  |  | 
|  | static_assert(gpu::kInvalidResource == 0, | 
|  | "GL expects kInvalidResource to be 0"); | 
|  |  | 
|  | // The standard id handler. | 
|  | class IdHandler : public IdHandlerInterface { | 
|  | public: | 
|  | IdHandler() { } | 
|  | ~IdHandler() override {} | 
|  |  | 
|  | // Overridden from IdHandlerInterface. | 
|  | void MakeIds(GLES2Implementation* /* gl_impl */, | 
|  | GLuint id_offset, | 
|  | GLsizei n, | 
|  | GLuint* ids) override { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | if (id_offset == 0) { | 
|  | for (GLsizei ii = 0; ii < n; ++ii) { | 
|  | ids[ii] = id_allocator_.AllocateID(); | 
|  | } | 
|  | } else { | 
|  | for (GLsizei ii = 0; ii < n; ++ii) { | 
|  | ids[ii] = id_allocator_.AllocateIDAtOrAbove(id_offset); | 
|  | id_offset = ids[ii] + 1; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Overridden from IdHandlerInterface. | 
|  | bool FreeIds(GLES2Implementation* gl_impl, | 
|  | GLsizei n, | 
|  | const GLuint* ids, | 
|  | DeleteFn delete_fn) override { | 
|  | base::AutoLock auto_lock(lock_); | 
|  |  | 
|  | for (GLsizei ii = 0; ii < n; ++ii) { | 
|  | id_allocator_.FreeID(ids[ii]); | 
|  | } | 
|  |  | 
|  | (gl_impl->*delete_fn)(n, ids); | 
|  | // We need to ensure that the delete call is evaluated on the service side | 
|  | // before any other contexts issue commands using these client ids. | 
|  | gl_impl->helper()->CommandBufferHelper::OrderingBarrier(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Overridden from IdHandlerInterface. | 
|  | bool MarkAsUsedForBind(GLES2Implementation* gl_impl, | 
|  | GLenum target, | 
|  | GLuint id, | 
|  | BindFn bind_fn) override { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | bool result = id ? id_allocator_.MarkAsUsed(id) : true; | 
|  | (gl_impl->*bind_fn)(target, id); | 
|  | return result; | 
|  | } | 
|  | bool MarkAsUsedForBind(GLES2Implementation* gl_impl, | 
|  | GLenum target, | 
|  | GLuint index, | 
|  | GLuint id, | 
|  | BindIndexedFn bind_fn) override { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | bool result = id ? id_allocator_.MarkAsUsed(id) : true; | 
|  | (gl_impl->*bind_fn)(target, index, id); | 
|  | return result; | 
|  | } | 
|  | bool MarkAsUsedForBind(GLES2Implementation* gl_impl, | 
|  | GLenum target, | 
|  | GLuint index, | 
|  | GLuint id, | 
|  | GLintptr offset, | 
|  | GLsizeiptr size, | 
|  | BindIndexedRangeFn bind_fn) override { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | bool result = id ? id_allocator_.MarkAsUsed(id) : true; | 
|  | (gl_impl->*bind_fn)(target, index, id, offset, size); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | void FreeContext(GLES2Implementation* gl_impl) override {} | 
|  |  | 
|  | private: | 
|  | base::Lock lock_; | 
|  | IdAllocator id_allocator_; | 
|  | }; | 
|  |  | 
|  | // An id handler that requires Gen before Bind. | 
|  | class StrictIdHandler : public IdHandlerInterface { | 
|  | public: | 
|  | explicit StrictIdHandler(int id_namespace) : id_namespace_(id_namespace) {} | 
|  | ~StrictIdHandler() override {} | 
|  |  | 
|  | // Overridden from IdHandler. | 
|  | void MakeIds(GLES2Implementation* gl_impl, | 
|  | GLuint /* id_offset */, | 
|  | GLsizei n, | 
|  | GLuint* ids) override { | 
|  | base::AutoLock auto_lock(lock_); | 
|  |  | 
|  | // Collect pending FreeIds from other flush_generation. | 
|  | CollectPendingFreeIds(gl_impl); | 
|  |  | 
|  | for (GLsizei ii = 0; ii < n; ++ii) { | 
|  | if (!free_ids_.empty()) { | 
|  | // Allocate a previously freed Id. | 
|  | ids[ii] = free_ids_.top(); | 
|  | free_ids_.pop(); | 
|  |  | 
|  | // Record kIdInUse state. | 
|  | DCHECK(id_states_[ids[ii] - 1] == kIdFree); | 
|  | id_states_[ids[ii] - 1] = kIdInUse; | 
|  | } else { | 
|  | // Allocate a new Id. | 
|  | id_states_.push_back(kIdInUse); | 
|  | ids[ii] = id_states_.size(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Overridden from IdHandler. | 
|  | bool FreeIds(GLES2Implementation* gl_impl, | 
|  | GLsizei n, | 
|  | const GLuint* ids, | 
|  | DeleteFn delete_fn) override { | 
|  | // Delete stub must run before CollectPendingFreeIds. | 
|  | (gl_impl->*delete_fn)(n, ids); | 
|  |  | 
|  | { | 
|  | base::AutoLock auto_lock(lock_); | 
|  |  | 
|  | // Collect pending FreeIds from other flush_generation. | 
|  | CollectPendingFreeIds(gl_impl); | 
|  |  | 
|  | // Save Ids to free in a later flush_generation. | 
|  | ShareGroupContextData::IdHandlerData* ctxt_data = | 
|  | gl_impl->share_group_context_data()->id_handler_data(id_namespace_); | 
|  |  | 
|  | for (GLsizei ii = 0; ii < n; ++ii) { | 
|  | GLuint id = ids[ii]; | 
|  | if (id != 0) { | 
|  | // Save freed Id for later. | 
|  | DCHECK(id_states_[id - 1] == kIdInUse); | 
|  | id_states_[id - 1] = kIdPendingFree; | 
|  | ctxt_data->freed_ids_.push_back(id); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Overridden from IdHandler. | 
|  | bool MarkAsUsedForBind(GLES2Implementation* gl_impl, | 
|  | GLenum target, | 
|  | GLuint id, | 
|  | BindFn bind_fn) override { | 
|  | #ifndef NDEBUG | 
|  | if (id != 0) { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | DCHECK(id_states_[id - 1] == kIdInUse); | 
|  | } | 
|  | #endif | 
|  | // StrictIdHandler is used if |bind_generates_resource| is false. In that | 
|  | // case, |bind_fn| will not use Flush() after helper->Bind*(), so it is OK | 
|  | // to call |bind_fn| without holding the lock. | 
|  | (gl_impl->*bind_fn)(target, id); | 
|  | return true; | 
|  | } | 
|  | bool MarkAsUsedForBind(GLES2Implementation* gl_impl, | 
|  | GLenum target, | 
|  | GLuint index, | 
|  | GLuint id, | 
|  | BindIndexedFn bind_fn) override { | 
|  | #ifndef NDEBUG | 
|  | if (id != 0) { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | DCHECK(id_states_[id - 1] == kIdInUse); | 
|  | } | 
|  | #endif | 
|  | // StrictIdHandler is used if |bind_generates_resource| is false. In that | 
|  | // case, |bind_fn| will not use Flush() after helper->Bind*(), so it is OK | 
|  | // to call |bind_fn| without holding the lock. | 
|  | (gl_impl->*bind_fn)(target, index, id); | 
|  | return true; | 
|  | } | 
|  | bool MarkAsUsedForBind(GLES2Implementation* gl_impl, | 
|  | GLenum target, | 
|  | GLuint index, | 
|  | GLuint id, | 
|  | GLintptr offset, | 
|  | GLsizeiptr size, | 
|  | BindIndexedRangeFn bind_fn) override { | 
|  | #ifndef NDEBUG | 
|  | if (id != 0) { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | DCHECK(id_states_[id - 1] == kIdInUse); | 
|  | } | 
|  | #endif | 
|  | // StrictIdHandler is used if |bind_generates_resource| is false. In that | 
|  | // case, |bind_fn| will not use Flush() after helper->Bind*(), so it is OK | 
|  | // to call |bind_fn| without holding the lock. | 
|  | (gl_impl->*bind_fn)(target, index, id, offset, size); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Overridden from IdHandlerInterface. | 
|  | void FreeContext(GLES2Implementation* gl_impl) override { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | CollectPendingFreeIds(gl_impl); | 
|  | } | 
|  |  | 
|  | private: | 
|  | enum IdState { kIdFree, kIdPendingFree, kIdInUse }; | 
|  |  | 
|  | void CollectPendingFreeIds(GLES2Implementation* gl_impl) { | 
|  | uint32 flush_generation = gl_impl->helper()->flush_generation(); | 
|  | ShareGroupContextData::IdHandlerData* ctxt_data = | 
|  | gl_impl->share_group_context_data()->id_handler_data(id_namespace_); | 
|  |  | 
|  | if (ctxt_data->flush_generation_ != flush_generation) { | 
|  | ctxt_data->flush_generation_ = flush_generation; | 
|  | for (uint32 ii = 0; ii < ctxt_data->freed_ids_.size(); ++ii) { | 
|  | const GLuint id = ctxt_data->freed_ids_[ii]; | 
|  | DCHECK(id_states_[id - 1] == kIdPendingFree); | 
|  | id_states_[id - 1] = kIdFree; | 
|  | free_ids_.push(id); | 
|  | } | 
|  | ctxt_data->freed_ids_.clear(); | 
|  | } | 
|  | } | 
|  |  | 
|  | int id_namespace_; | 
|  |  | 
|  | base::Lock lock_; | 
|  | std::vector<uint8> id_states_; | 
|  | std::stack<uint32> free_ids_; | 
|  | }; | 
|  |  | 
|  | // An id handler for ids that are never reused. | 
|  | class NonReusedIdHandler : public IdHandlerInterface { | 
|  | public: | 
|  | NonReusedIdHandler() : last_id_(0) {} | 
|  | ~NonReusedIdHandler() override {} | 
|  |  | 
|  | // Overridden from IdHandlerInterface. | 
|  | void MakeIds(GLES2Implementation* /* gl_impl */, | 
|  | GLuint id_offset, | 
|  | GLsizei n, | 
|  | GLuint* ids) override { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | for (GLsizei ii = 0; ii < n; ++ii) { | 
|  | ids[ii] = ++last_id_ + id_offset; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Overridden from IdHandlerInterface. | 
|  | bool FreeIds(GLES2Implementation* gl_impl, | 
|  | GLsizei n, | 
|  | const GLuint* ids, | 
|  | DeleteFn delete_fn) override { | 
|  | // Ids are never freed. | 
|  | (gl_impl->*delete_fn)(n, ids); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Overridden from IdHandlerInterface. | 
|  | bool MarkAsUsedForBind(GLES2Implementation* /* gl_impl */, | 
|  | GLenum /* target */, | 
|  | GLuint /* id */, | 
|  | BindFn /* bind_fn */) override { | 
|  | // This is only used for Shaders and Programs which have no bind. | 
|  | return false; | 
|  | } | 
|  | bool MarkAsUsedForBind(GLES2Implementation* /* gl_impl */, | 
|  | GLenum /* target */, | 
|  | GLuint /* index */, | 
|  | GLuint /* id */, | 
|  | BindIndexedFn /* bind_fn */) override { | 
|  | // This is only used for Shaders and Programs which have no bind. | 
|  | return false; | 
|  | } | 
|  | bool MarkAsUsedForBind(GLES2Implementation* /* gl_impl */, | 
|  | GLenum /* target */, | 
|  | GLuint /* index */, | 
|  | GLuint /* id */, | 
|  | GLintptr /* offset */, | 
|  | GLsizeiptr /* size */, | 
|  | BindIndexedRangeFn /* bind_fn */) override { | 
|  | // This is only used for Shaders and Programs which have no bind. | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void FreeContext(GLES2Implementation* gl_impl) override {} | 
|  |  | 
|  | private: | 
|  | base::Lock lock_; | 
|  | GLuint last_id_; | 
|  | }; | 
|  |  | 
|  | class RangeIdHandler : public RangeIdHandlerInterface { | 
|  | public: | 
|  | RangeIdHandler() {} | 
|  |  | 
|  | void MakeIdRange(GLES2Implementation* /*gl_impl*/, | 
|  | GLsizei n, | 
|  | GLuint* first_id) override { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | *first_id = id_allocator_.AllocateIDRange(n); | 
|  | } | 
|  |  | 
|  | void FreeIdRange(GLES2Implementation* gl_impl, | 
|  | const GLuint first_id, | 
|  | GLsizei range, | 
|  | DeleteRangeFn delete_fn) override { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | DCHECK(range > 0); | 
|  | id_allocator_.FreeIDRange(first_id, range); | 
|  | (gl_impl->*delete_fn)(first_id, range); | 
|  | gl_impl->helper()->CommandBufferHelper::OrderingBarrier(); | 
|  | } | 
|  |  | 
|  | void FreeContext(GLES2Implementation* gl_impl) override {} | 
|  |  | 
|  | private: | 
|  | base::Lock lock_; | 
|  | IdAllocator id_allocator_; | 
|  | }; | 
|  |  | 
|  | ShareGroup::ShareGroup(bool bind_generates_resource, uint64_t tracing_guid) | 
|  | : bind_generates_resource_(bind_generates_resource), | 
|  | tracing_guid_(tracing_guid) { | 
|  | if (bind_generates_resource) { | 
|  | for (int i = 0; i < id_namespaces::kNumIdNamespaces; ++i) { | 
|  | if (i == id_namespaces::kProgramsAndShaders) { | 
|  | id_handlers_[i].reset(new NonReusedIdHandler()); | 
|  | } else { | 
|  | id_handlers_[i].reset(new IdHandler()); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | for (int i = 0; i < id_namespaces::kNumIdNamespaces; ++i) { | 
|  | if (i == id_namespaces::kProgramsAndShaders) { | 
|  | id_handlers_[i].reset(new NonReusedIdHandler()); | 
|  | } else { | 
|  | id_handlers_[i].reset(new StrictIdHandler(i)); | 
|  | } | 
|  | } | 
|  | } | 
|  | program_info_manager_.reset(new ProgramInfoManager); | 
|  | for (auto& range_id_handler : range_id_handlers_) { | 
|  | range_id_handler.reset(new RangeIdHandler()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ShareGroup::set_program_info_manager(ProgramInfoManager* manager) { | 
|  | program_info_manager_.reset(manager); | 
|  | } | 
|  |  | 
|  | ShareGroup::~ShareGroup() {} | 
|  |  | 
|  | }  // namespace gles2 | 
|  | }  // namespace gpu |