1 // Copyright 2021 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "dawn_native/IndirectDrawMetadata.h" 16 17 #include "common/Constants.h" 18 #include "common/RefCounted.h" 19 #include "dawn_native/IndirectDrawValidationEncoder.h" 20 #include "dawn_native/Limits.h" 21 #include "dawn_native/RenderBundle.h" 22 23 #include <algorithm> 24 #include <utility> 25 26 namespace dawn_native { 27 ComputeMaxIndirectValidationBatchOffsetRange(const CombinedLimits & limits)28 uint32_t ComputeMaxIndirectValidationBatchOffsetRange(const CombinedLimits& limits) { 29 return limits.v1.maxStorageBufferBindingSize - limits.v1.minStorageBufferOffsetAlignment - 30 kDrawIndexedIndirectSize; 31 } 32 IndexedIndirectBufferValidationInfo(BufferBase * indirectBuffer)33 IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::IndexedIndirectBufferValidationInfo( 34 BufferBase* indirectBuffer) 35 : mIndirectBuffer(indirectBuffer) { 36 } 37 AddIndexedIndirectDraw(uint32_t maxDrawCallsPerIndirectValidationBatch,uint32_t maxBatchOffsetRange,IndexedIndirectDraw draw)38 void IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::AddIndexedIndirectDraw( 39 uint32_t maxDrawCallsPerIndirectValidationBatch, 40 uint32_t maxBatchOffsetRange, 41 IndexedIndirectDraw draw) { 42 const uint64_t newOffset = draw.clientBufferOffset; 43 auto it = mBatches.begin(); 44 while (it != mBatches.end()) { 45 IndexedIndirectValidationBatch& batch = *it; 46 if (batch.draws.size() >= maxDrawCallsPerIndirectValidationBatch) { 47 // This batch is full. If its minOffset is to the right of the new offset, we can 48 // just insert a new batch here. 49 if (newOffset < batch.minOffset) { 50 break; 51 } 52 53 // Otherwise keep looking. 54 ++it; 55 continue; 56 } 57 58 if (newOffset >= batch.minOffset && newOffset <= batch.maxOffset) { 59 batch.draws.push_back(std::move(draw)); 60 return; 61 } 62 63 if (newOffset < batch.minOffset && batch.maxOffset - newOffset <= maxBatchOffsetRange) { 64 // We can extend this batch to the left in order to fit the new offset. 65 batch.minOffset = newOffset; 66 batch.draws.push_back(std::move(draw)); 67 return; 68 } 69 70 if (newOffset > batch.maxOffset && newOffset - batch.minOffset <= maxBatchOffsetRange) { 71 // We can extend this batch to the right in order to fit the new offset. 72 batch.maxOffset = newOffset; 73 batch.draws.push_back(std::move(draw)); 74 return; 75 } 76 77 if (newOffset < batch.minOffset) { 78 // We want to insert a new batch just before this one. 79 break; 80 } 81 82 ++it; 83 } 84 85 IndexedIndirectValidationBatch newBatch; 86 newBatch.minOffset = newOffset; 87 newBatch.maxOffset = newOffset; 88 newBatch.draws.push_back(std::move(draw)); 89 90 mBatches.insert(it, std::move(newBatch)); 91 } 92 AddBatch(uint32_t maxDrawCallsPerIndirectValidationBatch,uint32_t maxBatchOffsetRange,const IndexedIndirectValidationBatch & newBatch)93 void IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::AddBatch( 94 uint32_t maxDrawCallsPerIndirectValidationBatch, 95 uint32_t maxBatchOffsetRange, 96 const IndexedIndirectValidationBatch& newBatch) { 97 auto it = mBatches.begin(); 98 while (it != mBatches.end()) { 99 IndexedIndirectValidationBatch& batch = *it; 100 uint64_t min = std::min(newBatch.minOffset, batch.minOffset); 101 uint64_t max = std::max(newBatch.maxOffset, batch.maxOffset); 102 if (max - min <= maxBatchOffsetRange && batch.draws.size() + newBatch.draws.size() <= 103 maxDrawCallsPerIndirectValidationBatch) { 104 // This batch fits within the limits of an existing batch. Merge it. 105 batch.minOffset = min; 106 batch.maxOffset = max; 107 batch.draws.insert(batch.draws.end(), newBatch.draws.begin(), newBatch.draws.end()); 108 return; 109 } 110 111 if (newBatch.minOffset < batch.minOffset) { 112 break; 113 } 114 115 ++it; 116 } 117 mBatches.push_back(newBatch); 118 } 119 120 const std::vector<IndirectDrawMetadata::IndexedIndirectValidationBatch>& GetBatches() const121 IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::GetBatches() const { 122 return mBatches; 123 } 124 IndirectDrawMetadata(const CombinedLimits & limits)125 IndirectDrawMetadata::IndirectDrawMetadata(const CombinedLimits& limits) 126 : mMaxDrawCallsPerBatch(ComputeMaxDrawCallsPerIndirectValidationBatch(limits)), 127 mMaxBatchOffsetRange(ComputeMaxIndirectValidationBatchOffsetRange(limits)) { 128 } 129 130 IndirectDrawMetadata::~IndirectDrawMetadata() = default; 131 132 IndirectDrawMetadata::IndirectDrawMetadata(IndirectDrawMetadata&&) = default; 133 134 IndirectDrawMetadata& IndirectDrawMetadata::operator=(IndirectDrawMetadata&&) = default; 135 136 IndirectDrawMetadata::IndexedIndirectBufferValidationInfoMap* GetIndexedIndirectBufferValidationInfo()137 IndirectDrawMetadata::GetIndexedIndirectBufferValidationInfo() { 138 return &mIndexedIndirectBufferValidationInfo; 139 } 140 AddBundle(RenderBundleBase * bundle)141 void IndirectDrawMetadata::AddBundle(RenderBundleBase* bundle) { 142 auto result = mAddedBundles.insert(bundle); 143 if (!result.second) { 144 return; 145 } 146 147 for (const auto& entry : 148 bundle->GetIndirectDrawMetadata().mIndexedIndirectBufferValidationInfo) { 149 const IndexedIndirectConfig& config = entry.first; 150 auto it = mIndexedIndirectBufferValidationInfo.lower_bound(config); 151 if (it != mIndexedIndirectBufferValidationInfo.end() && it->first == config) { 152 // We already have batches for the same config. Merge the new ones in. 153 for (const IndexedIndirectValidationBatch& batch : entry.second.GetBatches()) { 154 it->second.AddBatch(mMaxDrawCallsPerBatch, mMaxBatchOffsetRange, batch); 155 } 156 } else { 157 mIndexedIndirectBufferValidationInfo.emplace_hint(it, config, entry.second); 158 } 159 } 160 } 161 AddIndexedIndirectDraw(wgpu::IndexFormat indexFormat,uint64_t indexBufferSize,BufferBase * indirectBuffer,uint64_t indirectOffset,DrawIndexedIndirectCmd * cmd)162 void IndirectDrawMetadata::AddIndexedIndirectDraw(wgpu::IndexFormat indexFormat, 163 uint64_t indexBufferSize, 164 BufferBase* indirectBuffer, 165 uint64_t indirectOffset, 166 DrawIndexedIndirectCmd* cmd) { 167 uint64_t numIndexBufferElements; 168 switch (indexFormat) { 169 case wgpu::IndexFormat::Uint16: 170 numIndexBufferElements = indexBufferSize / 2; 171 break; 172 case wgpu::IndexFormat::Uint32: 173 numIndexBufferElements = indexBufferSize / 4; 174 break; 175 case wgpu::IndexFormat::Undefined: 176 UNREACHABLE(); 177 } 178 179 const IndexedIndirectConfig config(indirectBuffer, numIndexBufferElements); 180 auto it = mIndexedIndirectBufferValidationInfo.find(config); 181 if (it == mIndexedIndirectBufferValidationInfo.end()) { 182 auto result = mIndexedIndirectBufferValidationInfo.emplace( 183 config, IndexedIndirectBufferValidationInfo(indirectBuffer)); 184 it = result.first; 185 } 186 187 IndexedIndirectDraw draw; 188 draw.clientBufferOffset = indirectOffset; 189 draw.cmd = cmd; 190 it->second.AddIndexedIndirectDraw(mMaxDrawCallsPerBatch, mMaxBatchOffsetRange, 191 std::move(draw)); 192 } 193 194 } // namespace dawn_native 195