| // 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 "common/Constants.h" | |
| #include "common/RefCounted.h" | |
| #include "dawn_native/IndirectDrawValidationEncoder.h" | |
| #include "dawn_native/Limits.h" | |
| #include "dawn_native/RenderBundle.h" | |
| #include <algorithm> | |
| #include <utility> | |
| namespace dawn::native { | |
| uint32_t ComputeMaxIndirectValidationBatchOffsetRange(const CombinedLimits& limits) { | |
| return limits.v1.maxStorageBufferBindingSize - limits.v1.minStorageBufferOffsetAlignment - | |
| kDrawIndexedIndirectSize; | |
| } | |
| IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::IndexedIndirectBufferValidationInfo( | |
| BufferBase* indirectBuffer) | |
| : mIndirectBuffer(indirectBuffer) { | |
| } | |
| void IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::AddIndexedIndirectDraw( | |
| uint32_t maxDrawCallsPerIndirectValidationBatch, | |
| uint32_t maxBatchOffsetRange, | |
| IndexedIndirectDraw draw) { | |
| const uint64_t newOffset = draw.clientBufferOffset; | |
| auto it = mBatches.begin(); | |
| while (it != mBatches.end()) { | |
| IndexedIndirectValidationBatch& 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; | |
| } | |
| IndexedIndirectValidationBatch 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, | |
| uint32_t maxBatchOffsetRange, | |
| const IndexedIndirectValidationBatch& newBatch) { | |
| auto it = mBatches.begin(); | |
| while (it != mBatches.end()) { | |
| IndexedIndirectValidationBatch& 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::IndexedIndirectValidationBatch>& | |
| IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::GetBatches() const { | |
| return mBatches; | |
| } | |
| IndirectDrawMetadata::IndirectDrawMetadata(const CombinedLimits& limits) | |
| : mMaxDrawCallsPerBatch(ComputeMaxDrawCallsPerIndirectValidationBatch(limits)), | |
| mMaxBatchOffsetRange(ComputeMaxIndirectValidationBatchOffsetRange(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 IndexedIndirectValidationBatch& 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, | |
| 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); | |
| auto it = mIndexedIndirectBufferValidationInfo.find(config); | |
| if (it == mIndexedIndirectBufferValidationInfo.end()) { | |
| auto result = mIndexedIndirectBufferValidationInfo.emplace( | |
| config, IndexedIndirectBufferValidationInfo(indirectBuffer)); | |
| it = result.first; | |
| } | |
| IndexedIndirectDraw draw; | |
| draw.clientBufferOffset = indirectOffset; | |
| draw.cmd = cmd; | |
| it->second.AddIndexedIndirectDraw(mMaxDrawCallsPerBatch, mMaxBatchOffsetRange, | |
| std::move(draw)); | |
| } | |
| } // namespace dawn::native |