| // Copyright 2018 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/multi_draw_manager.h" |
| |
| #include <algorithm> |
| #include <ranges> |
| |
| #include "base/check.h" |
| #include "base/compiler_specific.h" |
| #include "base/notreached.h" |
| #include "base/numerics/checked_math.h" |
| |
| namespace gpu { |
| namespace gles2 { |
| |
| MultiDrawManager::ResultData::ResultData() = default; |
| MultiDrawManager::ResultData::~ResultData() = default; |
| |
| MultiDrawManager::ResultData::ResultData(ResultData&& rhs) |
| : draw_function(rhs.draw_function), |
| drawcount(rhs.drawcount), |
| mode(rhs.mode), |
| type(rhs.type), |
| firsts(std::move(rhs.firsts)), |
| counts(std::move(rhs.counts)), |
| offsets(std::move(rhs.offsets)), |
| indices(std::move(rhs.indices)), |
| instance_counts(std::move(rhs.instance_counts)), |
| basevertices(std::move(rhs.basevertices)), |
| baseinstances(std::move(rhs.baseinstances)) {} |
| |
| MultiDrawManager::ResultData& MultiDrawManager::ResultData::operator=( |
| ResultData&& rhs) { |
| if (&rhs == this) { |
| return *this; |
| } |
| draw_function = rhs.draw_function; |
| drawcount = rhs.drawcount; |
| mode = rhs.mode; |
| type = rhs.type; |
| std::swap(firsts, rhs.firsts); |
| std::swap(counts, rhs.counts); |
| std::swap(offsets, rhs.offsets); |
| std::swap(indices, rhs.indices); |
| std::swap(instance_counts, rhs.instance_counts); |
| std::swap(basevertices, rhs.basevertices); |
| std::swap(baseinstances, rhs.baseinstances); |
| return *this; |
| } |
| |
| MultiDrawManager::MultiDrawManager(IndexStorageType index_type) |
| : draw_state_(DrawState::End), |
| current_draw_offset_(0), |
| index_type_(index_type), |
| result_() {} |
| |
| bool MultiDrawManager::Begin(GLsizei drawcount) { |
| if (draw_state_ != DrawState::End) { |
| return false; |
| } |
| result_.drawcount = drawcount; |
| current_draw_offset_ = 0; |
| draw_state_ = DrawState::Begin; |
| return true; |
| } |
| |
| bool MultiDrawManager::End(ResultData* result) { |
| DCHECK(result); |
| if (draw_state_ != DrawState::Draw || |
| current_draw_offset_ != result_.drawcount) { |
| return false; |
| } |
| draw_state_ = DrawState::End; |
| *result = std::move(result_); |
| return true; |
| } |
| |
| bool MultiDrawManager::MultiDrawArrays(GLenum mode, |
| base::span<const GLint> firsts, |
| base::span<const GLsizei> counts) { |
| CHECK(firsts.size() == counts.size()); |
| GLsizei drawcount; |
| if (!base::CheckedNumeric<GLsizei>(firsts.size()).AssignIfValid(&drawcount)) { |
| return false; |
| } |
| if (!EnsureDrawArraysFunction(DrawFunction::DrawArrays, mode, drawcount)) { |
| return false; |
| } |
| CopyArraysHelper(drawcount, firsts, counts, {}, {}, {}, {}); |
| return true; |
| } |
| |
| bool MultiDrawManager::MultiDrawArraysInstanced( |
| GLenum mode, |
| base::span<const GLint> firsts, |
| base::span<const GLsizei> counts, |
| base::span<const GLsizei> instance_counts) { |
| CHECK(firsts.size() == counts.size()); |
| CHECK(firsts.size() == instance_counts.size()); |
| GLsizei drawcount; |
| if (!base::CheckedNumeric<GLsizei>(firsts.size()).AssignIfValid(&drawcount)) { |
| return false; |
| } |
| if (!EnsureDrawArraysFunction(DrawFunction::DrawArraysInstanced, mode, |
| drawcount)) { |
| return false; |
| } |
| CopyArraysHelper(drawcount, firsts, counts, {}, instance_counts, {}, {}); |
| return true; |
| } |
| |
| bool MultiDrawManager::MultiDrawArraysInstancedBaseInstance( |
| GLenum mode, |
| base::span<const GLint> firsts, |
| base::span<const GLsizei> counts, |
| base::span<const GLsizei> instance_counts, |
| base::span<const GLuint> baseinstances) { |
| CHECK(firsts.size() == counts.size()); |
| CHECK(firsts.size() == instance_counts.size()); |
| CHECK(firsts.size() == baseinstances.size()); |
| GLsizei drawcount; |
| if (!base::CheckedNumeric<GLsizei>(firsts.size()).AssignIfValid(&drawcount)) { |
| return false; |
| } |
| if (!EnsureDrawArraysFunction(DrawFunction::DrawArraysInstancedBaseInstance, |
| mode, drawcount)) { |
| return false; |
| } |
| CopyArraysHelper(drawcount, firsts, counts, {}, instance_counts, {}, |
| baseinstances); |
| return true; |
| } |
| |
| bool MultiDrawManager::MultiDrawElements(GLenum mode, |
| base::span<const GLsizei> counts, |
| GLenum type, |
| base::span<const GLsizei> offsets) { |
| CHECK(counts.size() == offsets.size()); |
| GLsizei drawcount; |
| if (!base::CheckedNumeric<GLsizei>(counts.size()).AssignIfValid(&drawcount)) { |
| return false; |
| } |
| if (!EnsureDrawElementsFunction(DrawFunction::DrawElements, mode, type, |
| drawcount)) { |
| return false; |
| } |
| CopyArraysHelper(drawcount, {}, counts, offsets, {}, {}, {}); |
| return true; |
| } |
| |
| bool MultiDrawManager::MultiDrawElementsInstanced( |
| GLenum mode, |
| base::span<const GLsizei> counts, |
| GLenum type, |
| base::span<const GLsizei> offsets, |
| base::span<const GLsizei> instance_counts) { |
| CHECK(counts.size() == offsets.size()); |
| CHECK(counts.size() == instance_counts.size()); |
| GLsizei drawcount; |
| if (!base::CheckedNumeric<GLsizei>(counts.size()).AssignIfValid(&drawcount)) { |
| return false; |
| } |
| if (!EnsureDrawElementsFunction(DrawFunction::DrawElementsInstanced, mode, |
| type, drawcount)) { |
| return false; |
| } |
| CopyArraysHelper(drawcount, {}, counts, offsets, instance_counts, {}, {}); |
| return true; |
| } |
| |
| bool MultiDrawManager::MultiDrawElementsInstancedBaseVertexBaseInstance( |
| GLenum mode, |
| base::span<const GLsizei> counts, |
| GLenum type, |
| base::span<const GLsizei> offsets, |
| base::span<const GLsizei> instance_counts, |
| base::span<const GLint> basevertices, |
| base::span<const GLuint> baseinstances) { |
| CHECK(counts.size() == offsets.size()); |
| CHECK(counts.size() == instance_counts.size()); |
| CHECK(counts.size() == basevertices.size()); |
| CHECK(counts.size() == baseinstances.size()); |
| GLsizei drawcount; |
| if (!base::CheckedNumeric<GLsizei>(counts.size()).AssignIfValid(&drawcount)) { |
| return false; |
| } |
| if (!EnsureDrawElementsFunction( |
| DrawFunction::DrawElementsInstancedBaseVertexBaseInstance, mode, type, |
| drawcount)) { |
| return false; |
| } |
| CopyArraysHelper(drawcount, {}, counts, offsets, instance_counts, |
| basevertices, baseinstances); |
| return true; |
| } |
| |
| void MultiDrawManager::ResizeArrays() { |
| switch (result_.draw_function) { |
| case DrawFunction::DrawArraysInstancedBaseInstance: |
| result_.baseinstances.resize(result_.drawcount); |
| [[fallthrough]]; |
| case DrawFunction::DrawArraysInstanced: |
| result_.instance_counts.resize(result_.drawcount); |
| [[fallthrough]]; |
| case DrawFunction::DrawArrays: |
| result_.firsts.resize(result_.drawcount); |
| result_.counts.resize(result_.drawcount); |
| break; |
| case DrawFunction::DrawElementsInstancedBaseVertexBaseInstance: |
| result_.basevertices.resize(result_.drawcount); |
| result_.baseinstances.resize(result_.drawcount); |
| [[fallthrough]]; |
| case DrawFunction::DrawElementsInstanced: |
| result_.instance_counts.resize(result_.drawcount); |
| [[fallthrough]]; |
| case DrawFunction::DrawElements: |
| result_.counts.resize(result_.drawcount); |
| switch (index_type_) { |
| case IndexStorageType::Offset: |
| result_.offsets.resize(result_.drawcount); |
| break; |
| case IndexStorageType::Pointer: |
| result_.indices.resize(result_.drawcount); |
| break; |
| } |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| bool MultiDrawManager::ValidateDrawcount(GLsizei drawcount) const { |
| if (drawcount < 0) { |
| return false; |
| } |
| GLsizei new_offset; |
| if (!base::CheckAdd(current_draw_offset_, drawcount) |
| .AssignIfValid(&new_offset)) { |
| return false; |
| } |
| if (new_offset > result_.drawcount) { |
| return false; |
| } |
| return true; |
| } |
| |
| bool MultiDrawManager::EnsureDrawArraysFunction(DrawFunction draw_function, |
| GLenum mode, |
| GLsizei drawcount) { |
| if (!ValidateDrawcount(drawcount)) { |
| return false; |
| } |
| bool invalid_draw_state = draw_state_ == DrawState::End; |
| bool first_call = draw_state_ == DrawState::Begin; |
| bool enums_match = result_.mode == mode; |
| |
| if (invalid_draw_state || (!first_call && !enums_match)) { |
| return false; |
| } |
| if (first_call) { |
| draw_state_ = DrawState::Draw; |
| result_.draw_function = draw_function; |
| result_.mode = mode; |
| ResizeArrays(); |
| } |
| return true; |
| } |
| |
| bool MultiDrawManager::EnsureDrawElementsFunction(DrawFunction draw_function, |
| GLenum mode, |
| GLenum type, |
| GLsizei drawcount) { |
| if (!ValidateDrawcount(drawcount)) { |
| return false; |
| } |
| bool invalid_draw_state = draw_state_ == DrawState::End; |
| bool first_call = draw_state_ == DrawState::Begin; |
| bool enums_match = result_.mode == mode && result_.type == type; |
| |
| if (invalid_draw_state || (!first_call && !enums_match)) { |
| return false; |
| } |
| if (first_call) { |
| draw_state_ = DrawState::Draw; |
| result_.draw_function = draw_function; |
| result_.mode = mode; |
| result_.type = type; |
| ResizeArrays(); |
| } |
| return true; |
| } |
| |
| void MultiDrawManager::CopyArraysHelper( |
| GLsizei drawcount, |
| base::span<const GLint> firsts, |
| base::span<const GLsizei> counts, |
| base::span<const GLsizei> offsets, |
| base::span<const GLsizei> instance_counts, |
| base::span<const GLint> basevertices, |
| base::span<const GLuint> baseinstances) { |
| size_t size = static_cast<size_t>(drawcount); |
| size_t offset = static_cast<size_t>(current_draw_offset_); |
| if (!firsts.empty()) { |
| base::span(result_.firsts) |
| .subspan(offset, size) |
| .copy_from(firsts.first(size)); |
| } |
| |
| if (!counts.empty()) { |
| base::span(result_.counts) |
| .subspan(offset, size) |
| .copy_from(counts.first(size)); |
| } |
| |
| if (!instance_counts.empty()) { |
| base::span(result_.instance_counts) |
| .subspan(offset, size) |
| .copy_from(instance_counts.first(size)); |
| } |
| |
| if (!basevertices.empty()) { |
| base::span(result_.basevertices) |
| .subspan(offset, size) |
| .copy_from(basevertices.first(size)); |
| } |
| |
| if (!baseinstances.empty()) { |
| base::span(result_.baseinstances) |
| .subspan(offset, size) |
| .copy_from(baseinstances.first(size)); |
| } |
| |
| if (!offsets.empty()) { |
| auto offsets_subspan = offsets.first(size); |
| switch (index_type_) { |
| case IndexStorageType::Offset: |
| base::span(result_.offsets) |
| .subspan(offset, size) |
| .copy_from(offsets_subspan); |
| break; |
| case IndexStorageType::Pointer: |
| std::ranges::transform( |
| offsets_subspan, |
| base::span(result_.indices).subspan(offset, size).begin(), |
| [](uint32_t offset) { |
| return reinterpret_cast<void*>(static_cast<intptr_t>(offset)); |
| }); |
| break; |
| } |
| } |
| |
| current_draw_offset_ += drawcount; |
| } |
| |
| } // namespace gles2 |
| } // namespace gpu |