| // Copyright (c) 2016 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/service/indexed_buffer_binding_host.h" | |
| #include "gpu/command_buffer/service/buffer_manager.h" | |
| namespace gpu { | |
| namespace gles2 { | |
| IndexedBufferBindingHost::IndexedBufferBinding::IndexedBufferBinding() | |
| : type(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() { | |
| } | |
| bool IndexedBufferBindingHost::IndexedBufferBinding::operator==( | |
| const IndexedBufferBindingHost::IndexedBufferBinding& other) const { | |
| if (type == kBindBufferNone && other.type == 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 = 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 = kBindBufferRange; | |
| buffer = _buffer; | |
| offset = _offset; | |
| size = _size; | |
| effective_full_buffer_size = _buffer ? _buffer->size() : 0; | |
| } | |
| void IndexedBufferBindingHost::IndexedBufferBinding::Reset() { | |
| type = kBindBufferNone; | |
| buffer = nullptr; | |
| offset = 0; | |
| size = 0; | |
| effective_full_buffer_size = 0; | |
| } | |
| IndexedBufferBindingHost::IndexedBufferBindingHost( | |
| uint32_t max_bindings, bool needs_emulation) | |
| : needs_emulation_(needs_emulation), | |
| max_non_null_binding_index_plus_one_(0u) { | |
| DCHECK(needs_emulation); | |
| buffer_bindings_.resize(max_bindings); | |
| } | |
| IndexedBufferBindingHost::~IndexedBufferBindingHost() { | |
| } | |
| void IndexedBufferBindingHost::DoBindBufferBase( | |
| GLenum target, GLuint index, Buffer* buffer) { | |
| DCHECK_LT(index, buffer_bindings_.size()); | |
| GLuint service_id = buffer ? buffer->service_id() : 0; | |
| glBindBufferBase(target, index, service_id); | |
| buffer_bindings_[index].SetBindBufferBase(buffer); | |
| UpdateMaxNonNullBindingIndex(index); | |
| } | |
| void IndexedBufferBindingHost::DoBindBufferRange( | |
| GLenum target, 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()); | |
| } else { | |
| glBindBufferRange(target, index, service_id, offset, size); | |
| } | |
| buffer_bindings_[index].SetBindBufferRange(buffer, offset, size); | |
| UpdateMaxNonNullBindingIndex(index); | |
| } | |
| // static | |
| void IndexedBufferBindingHost::DoAdjustedBindBufferRange( | |
| GLenum target, GLuint index, GLuint service_id, GLintptr offset, | |
| GLsizeiptr size, GLsizeiptr full_buffer_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; | |
| } else 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; | |
| } | |
| } | |
| glBindBufferRange(target, index, service_id, offset, adjusted_size); | |
| } | |
| void IndexedBufferBindingHost::OnBindHost(GLenum target) { | |
| 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) { | |
| Buffer* buffer = buffer_bindings_[ii].buffer.get(); | |
| if (buffer && buffer_bindings_[ii].type == 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()); | |
| buffer_bindings_[ii].effective_full_buffer_size = buffer->size(); | |
| } | |
| } | |
| } | |
| } | |
| void IndexedBufferBindingHost::OnBufferData(GLenum target, 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 == 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()); | |
| buffer_bindings_[ii].effective_full_buffer_size = buffer->size(); | |
| } | |
| } | |
| } | |
| } | |
| void IndexedBufferBindingHost::RemoveBoundBuffer(Buffer* buffer) { | |
| for (size_t ii = 0; ii < buffer_bindings_.size(); ++ii) { | |
| if (buffer_bindings_[ii].buffer.get() == buffer) { | |
| buffer_bindings_[ii].Reset(); | |
| UpdateMaxNonNullBindingIndex(ii); | |
| } | |
| } | |
| } | |
| 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 kBindBufferBase: | |
| return full_buffer_size; | |
| case kBindBufferRange: | |
| if (binding.offset + binding.size > full_buffer_size) | |
| return full_buffer_size - binding.offset; | |
| return binding.size; | |
| case 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) { | |
| 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 kBindBufferBase: | |
| case kBindBufferNone: | |
| DoBindBufferBase( | |
| GL_UNIFORM_BUFFER, ii, buffer_bindings_[ii].buffer.get()); | |
| break; | |
| case kBindBufferRange: | |
| DoBindBufferRange( | |
| GL_UNIFORM_BUFFER, 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 |