blob: a92338745788fdc238435c5f94ecf924a7b93837 [file] [log] [blame]
// 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