| // 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 <stdint.h> | |
| #include <vector> | |
| #include "base/containers/stack.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() = default; | |
| static_assert(gpu::kInvalidResource == 0, | |
| "GL expects kInvalidResource to be 0"); | |
| // The standard id handler. | |
| class IdHandler : public IdHandlerInterface { | |
| public: | |
| IdHandler() = default; | |
| ~IdHandler() override = default; | |
| // 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 = default; | |
| // 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); | |
| bool return_value = true; | |
| { | |
| 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_); | |
| GLuint max_valid_id = id_states_.size(); | |
| for (GLsizei ii = 0; ii < n; ++ii) { | |
| GLuint id = ids[ii]; | |
| if (id != 0) { | |
| if (id > max_valid_id) { | |
| // Caller will generate an error. | |
| return_value = false; | |
| continue; | |
| } | |
| // Save freed Id for later. | |
| if (id_states_[id - 1] != kIdInUse) { | |
| DVLOG(1) << "Already freed id " << id; | |
| return_value = false; | |
| continue; | |
| } | |
| id_states_[id - 1] = kIdPendingFree; | |
| ctxt_data->freed_ids_.push_back(id); | |
| } | |
| } | |
| } | |
| return return_value; | |
| } | |
| // 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_t 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_t 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_t> id_states_; | |
| base::stack<uint32_t> free_ids_; | |
| }; | |
| // An id handler for ids that are never reused. | |
| class NonReusedIdHandler : public IdHandlerInterface { | |
| public: | |
| NonReusedIdHandler() : last_id_(0) {} | |
| ~NonReusedIdHandler() override = default; | |
| // 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() = default; | |
| 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 < static_cast<int>(SharedIdNamespaces::kNumSharedIdNamespaces); | |
| ++i) { | |
| if (i == static_cast<int>(SharedIdNamespaces::kProgramsAndShaders)) { | |
| id_handlers_[i].reset(new NonReusedIdHandler()); | |
| } else { | |
| id_handlers_[i].reset(new IdHandler()); | |
| } | |
| } | |
| } else { | |
| for (int i = 0; | |
| i < static_cast<int>(SharedIdNamespaces::kNumSharedIdNamespaces); | |
| ++i) { | |
| if (i == static_cast<int>(SharedIdNamespaces::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::Lose() { | |
| base::AutoLock hold(lost_lock_); | |
| lost_ = true; | |
| } | |
| bool ShareGroup::IsLost() const { | |
| base::AutoLock hold(lost_lock_); | |
| return lost_; | |
| } | |
| void ShareGroup::SetProgramInfoManagerForTesting(ProgramInfoManager* manager) { | |
| program_info_manager_.reset(manager); | |
| } | |
| ShareGroup::~ShareGroup() = default; | |
| } // namespace gles2 | |
| } // namespace gpu |