// Copyright 2021 The Dawn Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dawn/native/IndirectDrawMetadata.h" #include #include #include "dawn/common/Constants.h" #include "dawn/common/RefCounted.h" #include "dawn/native/IndirectDrawValidationEncoder.h" #include "dawn/native/Limits.h" #include "dawn/native/RenderBundle.h" namespace dawn::native { uint64_t ComputeMaxIndirectValidationBatchOffsetRange(const CombinedLimits& limits) { return limits.v1.maxStorageBufferBindingSize - limits.v1.minStorageBufferOffsetAlignment - kDrawIndexedIndirectSize; } IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::IndexedIndirectBufferValidationInfo( BufferBase* indirectBuffer) : mIndirectBuffer(indirectBuffer) {} void IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::AddIndirectDraw( uint32_t maxDrawCallsPerIndirectValidationBatch, uint64_t maxBatchOffsetRange, IndirectDraw draw) { const uint64_t newOffset = draw.inputBufferOffset; auto it = mBatches.begin(); while (it != mBatches.end()) { IndirectValidationBatch& batch = *it; if (batch.draws.size() >= maxDrawCallsPerIndirectValidationBatch) { // This batch is full. If its minOffset is to the right of the new offset, we can // just insert a new batch here. if (newOffset < batch.minOffset) { break; } // Otherwise keep looking. ++it; continue; } if (newOffset >= batch.minOffset && newOffset <= batch.maxOffset) { batch.draws.push_back(std::move(draw)); return; } if (newOffset < batch.minOffset && batch.maxOffset - newOffset <= maxBatchOffsetRange) { // We can extend this batch to the left in order to fit the new offset. batch.minOffset = newOffset; batch.draws.push_back(std::move(draw)); return; } if (newOffset > batch.maxOffset && newOffset - batch.minOffset <= maxBatchOffsetRange) { // We can extend this batch to the right in order to fit the new offset. batch.maxOffset = newOffset; batch.draws.push_back(std::move(draw)); return; } if (newOffset < batch.minOffset) { // We want to insert a new batch just before this one. break; } ++it; } IndirectValidationBatch newBatch; newBatch.minOffset = newOffset; newBatch.maxOffset = newOffset; newBatch.draws.push_back(std::move(draw)); mBatches.insert(it, std::move(newBatch)); } void IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::AddBatch( uint32_t maxDrawCallsPerIndirectValidationBatch, uint64_t maxBatchOffsetRange, const IndirectValidationBatch& newBatch) { auto it = mBatches.begin(); while (it != mBatches.end()) { IndirectValidationBatch& batch = *it; uint64_t min = std::min(newBatch.minOffset, batch.minOffset); uint64_t max = std::max(newBatch.maxOffset, batch.maxOffset); if (max - min <= maxBatchOffsetRange && batch.draws.size() + newBatch.draws.size() <= maxDrawCallsPerIndirectValidationBatch) { // This batch fits within the limits of an existing batch. Merge it. batch.minOffset = min; batch.maxOffset = max; batch.draws.insert(batch.draws.end(), newBatch.draws.begin(), newBatch.draws.end()); return; } if (newBatch.minOffset < batch.minOffset) { break; } ++it; } mBatches.push_back(newBatch); } const std::vector& IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::GetBatches() const { return mBatches; } IndirectDrawMetadata::IndirectDrawMetadata(const CombinedLimits& limits) : mMaxBatchOffsetRange(ComputeMaxIndirectValidationBatchOffsetRange(limits)), mMaxDrawCallsPerBatch(ComputeMaxDrawCallsPerIndirectValidationBatch(limits)) {} IndirectDrawMetadata::~IndirectDrawMetadata() = default; IndirectDrawMetadata::IndirectDrawMetadata(IndirectDrawMetadata&&) = default; IndirectDrawMetadata& IndirectDrawMetadata::operator=(IndirectDrawMetadata&&) = default; IndirectDrawMetadata::IndexedIndirectBufferValidationInfoMap* IndirectDrawMetadata::GetIndexedIndirectBufferValidationInfo() { return &mIndexedIndirectBufferValidationInfo; } void IndirectDrawMetadata::AddBundle(RenderBundleBase* bundle) { auto [_, inserted] = mAddedBundles.insert(bundle); if (!inserted) { return; } for (const auto& [config, validationInfo] : bundle->GetIndirectDrawMetadata().mIndexedIndirectBufferValidationInfo) { auto it = mIndexedIndirectBufferValidationInfo.lower_bound(config); if (it != mIndexedIndirectBufferValidationInfo.end() && it->first == config) { // We already have batches for the same config. Merge the new ones in. for (const IndirectValidationBatch& batch : validationInfo.GetBatches()) { it->second.AddBatch(mMaxDrawCallsPerBatch, mMaxBatchOffsetRange, batch); } } else { mIndexedIndirectBufferValidationInfo.emplace_hint(it, config, validationInfo); } } } void IndirectDrawMetadata::AddIndexedIndirectDraw(wgpu::IndexFormat indexFormat, uint64_t indexBufferSize, BufferBase* indirectBuffer, uint64_t indirectOffset, bool duplicateBaseVertexInstance, DrawIndexedIndirectCmd* cmd) { uint64_t numIndexBufferElements; switch (indexFormat) { case wgpu::IndexFormat::Uint16: numIndexBufferElements = indexBufferSize / 2; break; case wgpu::IndexFormat::Uint32: numIndexBufferElements = indexBufferSize / 4; break; case wgpu::IndexFormat::Undefined: UNREACHABLE(); } const IndexedIndirectConfig config = {indirectBuffer, numIndexBufferElements, duplicateBaseVertexInstance, DrawType::Indexed}; auto it = mIndexedIndirectBufferValidationInfo.find(config); if (it == mIndexedIndirectBufferValidationInfo.end()) { auto result = mIndexedIndirectBufferValidationInfo.emplace( config, IndexedIndirectBufferValidationInfo(indirectBuffer)); it = result.first; } IndirectDraw draw{}; draw.inputBufferOffset = indirectOffset; draw.cmd = cmd; it->second.AddIndirectDraw(mMaxDrawCallsPerBatch, mMaxBatchOffsetRange, draw); } void IndirectDrawMetadata::AddIndirectDraw(BufferBase* indirectBuffer, uint64_t indirectOffset, bool duplicateBaseVertexInstance, DrawIndirectCmd* cmd) { const IndexedIndirectConfig config = {indirectBuffer, 0, duplicateBaseVertexInstance, DrawType::NonIndexed}; auto it = mIndexedIndirectBufferValidationInfo.find(config); if (it == mIndexedIndirectBufferValidationInfo.end()) { auto result = mIndexedIndirectBufferValidationInfo.emplace( config, IndexedIndirectBufferValidationInfo(indirectBuffer)); it = result.first; } IndirectDraw draw{}; draw.inputBufferOffset = indirectOffset; draw.cmd = cmd; it->second.AddIndirectDraw(mMaxDrawCallsPerBatch, mMaxBatchOffsetRange, draw); } bool IndirectDrawMetadata::IndexedIndirectConfig::operator<( const IndexedIndirectConfig& other) const { return std::tie(inputIndirectBuffer, numIndexBufferElements, duplicateBaseVertexInstance, drawType) < std::tie(other.inputIndirectBuffer, other.numIndexBufferElements, other.duplicateBaseVertexInstance, other.drawType); } bool IndirectDrawMetadata::IndexedIndirectConfig::operator==( const IndexedIndirectConfig& other) const { return std::tie(inputIndirectBuffer, numIndexBufferElements, duplicateBaseVertexInstance, drawType) == std::tie(other.inputIndirectBuffer, other.numIndexBufferElements, other.duplicateBaseVertexInstance, other.drawType); } } // namespace dawn::native