blob: 4d6f643d1c346eb3fa9f29bae12684a392f65495 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// 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/indexed_buffer_binding_host.h"
#include "gpu/command_buffer/service/buffer_manager.h"
namespace gpu {
namespace gles2 {
IndexedBufferBindingHost::IndexedBufferBinding::IndexedBufferBinding()
: type(IndexedBufferBindingType::kBindBufferNone),
offset(0),
size(0),
effective_full_buffer_size(0) {}
IndexedBufferBindingHost::IndexedBufferBinding::IndexedBufferBinding(
const IndexedBufferBindingHost::IndexedBufferBinding& other)
: type(other.type),
buffer(other.buffer.get()),
offset(other.offset),
size(other.size),
effective_full_buffer_size(other.effective_full_buffer_size) {
}
IndexedBufferBindingHost::IndexedBufferBinding::~IndexedBufferBinding() =
default;
bool IndexedBufferBindingHost::IndexedBufferBinding::operator==(
const IndexedBufferBindingHost::IndexedBufferBinding& other) const {
if (type == IndexedBufferBindingType::kBindBufferNone &&
other.type == IndexedBufferBindingType::kBindBufferNone) {
// This should be the most common case so an early out.
return true;
}
return (type == other.type &&
buffer.get() == other.buffer.get() &&
offset == other.offset &&
size == other.size &&
effective_full_buffer_size == other.effective_full_buffer_size);
}
void IndexedBufferBindingHost::IndexedBufferBinding::SetBindBufferBase(
Buffer* _buffer) {
if (!_buffer) {
Reset();
return;
}
type = IndexedBufferBindingType::kBindBufferBase;
buffer = _buffer;
offset = 0;
size = 0;
effective_full_buffer_size = 0;
}
void IndexedBufferBindingHost::IndexedBufferBinding::SetBindBufferRange(
Buffer* _buffer, GLintptr _offset, GLsizeiptr _size) {
if (!_buffer) {
Reset();
return;
}
type = IndexedBufferBindingType::kBindBufferRange;
buffer = _buffer;
offset = _offset;
size = _size;
effective_full_buffer_size = _buffer ? _buffer->size() : 0;
}
void IndexedBufferBindingHost::IndexedBufferBinding::Reset() {
type = IndexedBufferBindingType::kBindBufferNone;
buffer = nullptr;
offset = 0;
size = 0;
effective_full_buffer_size = 0;
}
IndexedBufferBindingHost::IndexedBufferBindingHost(
uint32_t max_bindings,
GLenum target,
bool needs_emulation,
bool round_down_uniform_bind_buffer_range_size)
: is_bound_(false),
needs_emulation_(needs_emulation),
round_down_uniform_bind_buffer_range_size_(
round_down_uniform_bind_buffer_range_size),
max_non_null_binding_index_plus_one_(0u),
target_(target) {
DCHECK(needs_emulation);
buffer_bindings_.resize(max_bindings);
}
IndexedBufferBindingHost::~IndexedBufferBindingHost() {
SetIsBound(false);
}
void IndexedBufferBindingHost::DoBindBufferBase(GLuint index, Buffer* buffer) {
DCHECK_LT(index, buffer_bindings_.size());
GLuint service_id = buffer ? buffer->service_id() : 0;
glBindBufferBase(target_, index, service_id);
if (buffer_bindings_[index].buffer && is_bound_) {
buffer_bindings_[index].buffer->OnUnbind(target_, true);
}
buffer_bindings_[index].SetBindBufferBase(buffer);
if (buffer && is_bound_) {
buffer->OnBind(target_, true);
}
UpdateMaxNonNullBindingIndex(index);
}
void IndexedBufferBindingHost::DoBindBufferRange(GLuint index,
Buffer* buffer,
GLintptr offset,
GLsizeiptr size) {
DCHECK_LT(index, buffer_bindings_.size());
GLuint service_id = buffer ? buffer->service_id() : 0;
if (buffer && needs_emulation_) {
DoAdjustedBindBufferRange(target_, index, service_id, offset, size,
buffer->size(),
round_down_uniform_bind_buffer_range_size_);
} else {
glBindBufferRange(target_, index, service_id, offset, size);
}
if (buffer_bindings_[index].buffer && is_bound_) {
buffer_bindings_[index].buffer->OnUnbind(target_, true);
}
buffer_bindings_[index].SetBindBufferRange(buffer, offset, size);
if (buffer && is_bound_) {
buffer->OnBind(target_, true);
}
UpdateMaxNonNullBindingIndex(index);
}
// static
void IndexedBufferBindingHost::DoAdjustedBindBufferRange(
GLenum target,
GLuint index,
GLuint service_id,
GLintptr offset,
GLsizeiptr size,
GLsizeiptr full_buffer_size,
bool round_down_uniform_bind_buffer_range_size) {
GLsizeiptr adjusted_size = size;
if (offset >= full_buffer_size) {
// Situation 1: We can't really call glBindBufferRange with reasonable
// offset/size without triggering a GL error because size == 0 isn't
// valid.
// TODO(zmo): it's ambiguous in the GL 4.1 spec whether BindBufferBase
// generates a GL error in such case. In reality, no error is generated on
// MacOSX with AMD/4.1.
glBindBufferBase(target, index, service_id);
return;
}
if (offset + size > full_buffer_size) {
adjusted_size = full_buffer_size - offset;
// size needs to be a multiple of 4.
adjusted_size = adjusted_size & ~3;
if (adjusted_size == 0) {
// Situation 2: The original size is valid, but the adjusted size
// is 0 and isn't valid. Handle it the same way as situation 1.
glBindBufferBase(target, index, service_id);
return;
}
}
if (round_down_uniform_bind_buffer_range_size) {
adjusted_size = adjusted_size & ~3;
if (adjusted_size == 0) {
// This case is invalid and we shouldn't call the driver.
// Without rounding, this would generate INVALID_OPERATION
// at draw time because the size is not enough to fill the smallest
// possible uniform block (4 bytes).
// The size of the range is set in DoBindBufferRange and validated in
// BufferManager::RequestBuffersAccess. It is fine to not bind the buffer
// because any draw call with this buffer range binding will generate
// INVALID_OPERATION.
// Clear the buffer binding because it will not be used.
glBindBufferBase(target, index, 0);
return;
}
}
glBindBufferRange(target, index, service_id, offset, adjusted_size);
}
void IndexedBufferBindingHost::OnBufferData(Buffer* buffer) {
DCHECK(buffer);
if (needs_emulation_) {
// If some bound buffers change size since last time the transformfeedback
// is bound, we might need to reset the ranges.
for (size_t ii = 0; ii < buffer_bindings_.size(); ++ii) {
if (buffer_bindings_[ii].buffer.get() != buffer)
continue;
if (buffer_bindings_[ii].type ==
IndexedBufferBindingType::kBindBufferRange &&
buffer_bindings_[ii].effective_full_buffer_size != buffer->size()) {
DoAdjustedBindBufferRange(target_, ii, buffer->service_id(),
buffer_bindings_[ii].offset,
buffer_bindings_[ii].size, buffer->size(),
round_down_uniform_bind_buffer_range_size_);
buffer_bindings_[ii].effective_full_buffer_size = buffer->size();
}
}
}
}
void IndexedBufferBindingHost::RemoveBoundBuffer(
GLenum target,
Buffer* buffer,
Buffer* target_generic_bound_buffer,
bool have_context) {
DCHECK(buffer);
bool need_to_recover_generic_binding = false;
for (size_t ii = 0; ii < buffer_bindings_.size(); ++ii) {
if (buffer_bindings_[ii].buffer.get() == buffer) {
buffer_bindings_[ii].Reset();
UpdateMaxNonNullBindingIndex(ii);
if (have_context) {
glBindBufferBase(target, ii, 0);
need_to_recover_generic_binding = true;
}
}
}
if (need_to_recover_generic_binding && target_generic_bound_buffer)
glBindBuffer(target, target_generic_bound_buffer->service_id());
}
void IndexedBufferBindingHost::SetIsBound(bool is_bound) {
if (is_bound && needs_emulation_) {
// If some bound buffers change size since last time the transformfeedback
// is bound, we might need to reset the ranges.
for (size_t ii = 0; ii < buffer_bindings_.size(); ++ii) {
Buffer* buffer = buffer_bindings_[ii].buffer.get();
if (buffer &&
buffer_bindings_[ii].type ==
IndexedBufferBindingType::kBindBufferRange &&
buffer_bindings_[ii].effective_full_buffer_size != buffer->size()) {
DoAdjustedBindBufferRange(target_, ii, buffer->service_id(),
buffer_bindings_[ii].offset,
buffer_bindings_[ii].size, buffer->size(),
round_down_uniform_bind_buffer_range_size_);
buffer_bindings_[ii].effective_full_buffer_size = buffer->size();
}
}
}
if (is_bound != is_bound_) {
is_bound_ = is_bound;
for (auto& bb : buffer_bindings_) {
if (bb.buffer) {
if (is_bound_) {
bb.buffer->OnBind(target_, true);
} else {
bb.buffer->OnUnbind(target_, true);
}
}
}
}
}
Buffer* IndexedBufferBindingHost::GetBufferBinding(GLuint index) const {
DCHECK_LT(index, buffer_bindings_.size());
return buffer_bindings_[index].buffer.get();
}
GLsizeiptr IndexedBufferBindingHost::GetBufferSize(GLuint index) const {
DCHECK_LT(index, buffer_bindings_.size());
return buffer_bindings_[index].size;
}
GLsizeiptr IndexedBufferBindingHost::GetEffectiveBufferSize(
GLuint index) const {
DCHECK_LT(index, buffer_bindings_.size());
const IndexedBufferBinding& binding = buffer_bindings_[index];
if (!binding.buffer.get())
return 0;
GLsizeiptr full_buffer_size = binding.buffer->size();
switch (binding.type) {
case IndexedBufferBindingType::kBindBufferBase:
return full_buffer_size;
case IndexedBufferBindingType::kBindBufferRange:
if (binding.offset + binding.size > full_buffer_size)
return full_buffer_size - binding.offset;
return binding.size;
case IndexedBufferBindingType::kBindBufferNone:
return 0;
}
return buffer_bindings_[index].size;
}
GLintptr IndexedBufferBindingHost::GetBufferStart(GLuint index) const {
DCHECK_LT(index, buffer_bindings_.size());
return buffer_bindings_[index].offset;
}
void IndexedBufferBindingHost::RestoreBindings(
IndexedBufferBindingHost* prev) {
// This is used only for UNIFORM_BUFFER bindings in context switching.
DCHECK(target_ == GL_UNIFORM_BUFFER && (!prev || prev->target_ == target_));
size_t limit = max_non_null_binding_index_plus_one_;
if (prev && prev->max_non_null_binding_index_plus_one_ > limit) {
limit = prev->max_non_null_binding_index_plus_one_;
}
for (size_t ii = 0; ii < limit; ++ii) {
if (prev && buffer_bindings_[ii] == prev->buffer_bindings_[ii]) {
continue;
}
switch (buffer_bindings_[ii].type) {
case IndexedBufferBindingType::kBindBufferBase:
case IndexedBufferBindingType::kBindBufferNone:
DoBindBufferBase(ii, buffer_bindings_[ii].buffer.get());
break;
case IndexedBufferBindingType::kBindBufferRange:
DoBindBufferRange(ii, buffer_bindings_[ii].buffer.get(),
buffer_bindings_[ii].offset,
buffer_bindings_[ii].size);
break;
}
}
}
void IndexedBufferBindingHost::UpdateMaxNonNullBindingIndex(
size_t changed_index) {
size_t plus_one = changed_index + 1;
DCHECK_LT(changed_index, buffer_bindings_.size());
if (buffer_bindings_[changed_index].buffer.get()) {
max_non_null_binding_index_plus_one_ =
std::max(max_non_null_binding_index_plus_one_, plus_one);
} else {
if (plus_one == max_non_null_binding_index_plus_one_) {
for (size_t ii = changed_index; ii > 0; --ii) {
if (buffer_bindings_[ii - 1].buffer.get()) {
max_non_null_binding_index_plus_one_ = ii;
break;
}
}
}
}
}
bool IndexedBufferBindingHost::UsesBuffer(
size_t used_binding_count, const Buffer* buffer) const {
DCHECK(buffer);
DCHECK_LE(used_binding_count, buffer_bindings_.size());
for (size_t ii = 0; ii < used_binding_count; ++ii) {
if (buffer == buffer_bindings_[ii].buffer)
return true;
}
return false;
}
} // namespace gles2
} // namespace gpu