| // Copyright 2019 The Dawn & Tint Authors | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright notice, this | |
| // list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| // this list of conditions and the following disclaimer in the documentation | |
| // and/or other materials provided with the distribution. | |
| // | |
| // 3. Neither the name of the copyright holder nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| #include "src/dawn/native/PassResourceUsageTracker.h" | |
| #include <utility> | |
| #include "src/dawn/common/MatchVariant.h" | |
| #include "src/dawn/native/BindGroup.h" | |
| #include "src/dawn/native/Buffer.h" | |
| #include "src/dawn/native/EnumMaskIterator.h" | |
| #include "src/dawn/native/ExternalTexture.h" | |
| #include "src/dawn/native/Format.h" | |
| #include "src/dawn/native/QuerySet.h" | |
| #include "src/dawn/native/TexelBufferView.h" | |
| #include "src/dawn/native/Texture.h" | |
| namespace dawn::native { | |
| SyncScopeUsageTracker::SyncScopeUsageTracker() = default; | |
| SyncScopeUsageTracker::SyncScopeUsageTracker(SyncScopeUsageTracker&&) = default; | |
| SyncScopeUsageTracker::~SyncScopeUsageTracker() = default; | |
| SyncScopeUsageTracker& SyncScopeUsageTracker::operator=(SyncScopeUsageTracker&&) = default; | |
| void SyncScopeUsageTracker::BufferUsedAs(BufferBase* buffer, | |
| wgpu::BufferUsage usage, | |
| wgpu::ShaderStage shaderStages) { | |
| // absl::flat_hash_map's operator[] will create a new element using the default constructor | |
| // if the key didn't exist before. | |
| BufferSyncInfo& bufferSyncInfo = mBufferSyncInfos[buffer]; | |
| bufferSyncInfo.usage |= usage; | |
| bufferSyncInfo.shaderStages |= shaderStages; | |
| } | |
| void SyncScopeUsageTracker::TextureViewUsedAs(TextureViewBase* view, | |
| wgpu::TextureUsage usage, | |
| wgpu::ShaderStage shaderStages) { | |
| TextureRangeUsedAs(view->GetTexture(), view->GetSubresourceRange(), usage, shaderStages); | |
| } | |
| void SyncScopeUsageTracker::TextureRangeUsedAs(TextureBase* texture, | |
| const SubresourceRange& range, | |
| wgpu::TextureUsage usage, | |
| wgpu::ShaderStage shaderStages) { | |
| // Get or create a new TextureSubresourceSyncInfo for that texture (initially filled with | |
| // wgpu::TextureUsage::None and WGPUShaderStage_None) | |
| auto it = mTextureSyncInfos.try_emplace( | |
| texture, texture->GetFormat().aspects, texture->GetArrayLayers(), | |
| texture->GetNumMipLevels(), | |
| TextureSyncInfo{wgpu::TextureUsage::None, wgpu::ShaderStage::None}); | |
| TextureSubresourceSyncInfo& textureSyncInfo = it.first->second; | |
| textureSyncInfo.Update( | |
| range, [usage, shaderStages](const SubresourceRange&, TextureSyncInfo* storedSyncInfo) { | |
| storedSyncInfo->usage |= usage; | |
| storedSyncInfo->shaderStages |= shaderStages; | |
| }); | |
| } | |
| void SyncScopeUsageTracker::MergeTextureUsage(TextureBase* texture, | |
| const TextureSubresourceSyncInfo& textureSyncInfo) { | |
| // Get or create a new TextureSubresourceSyncInfo for that texture (initially filled with | |
| // wgpu::TextureUsage::None and WGPUShaderStage_None) | |
| auto it = mTextureSyncInfos.try_emplace( | |
| texture, texture->GetFormat().aspects, texture->GetArrayLayers(), | |
| texture->GetNumMipLevels(), | |
| TextureSyncInfo{wgpu::TextureUsage::None, wgpu::ShaderStage::None}); | |
| TextureSubresourceSyncInfo& passTextureSyncInfo = it.first->second; | |
| passTextureSyncInfo.Merge( | |
| textureSyncInfo, [](const SubresourceRange&, TextureSyncInfo* storedSyncInfo, | |
| const TextureSyncInfo& addedSyncInfo) { | |
| DAWN_ASSERT((addedSyncInfo.usage & wgpu::TextureUsage::RenderAttachment) == 0); | |
| storedSyncInfo->usage |= addedSyncInfo.usage; | |
| storedSyncInfo->shaderStages |= addedSyncInfo.shaderStages; | |
| }); | |
| } | |
| void SyncScopeUsageTracker::MergeResourceUsages(const SyncScopeResourceUsage& usages) { | |
| for (uint32_t i = 0; i < usages.buffers.size(); ++i) { | |
| BufferUsedAs(usages.buffers[i], usages.bufferSyncInfos[i].usage, | |
| usages.bufferSyncInfos[i].shaderStages); | |
| } | |
| for (uint32_t i = 0; i < usages.textures.size(); ++i) { | |
| MergeTextureUsage(usages.textures[i], usages.textureSyncInfos[i]); | |
| } | |
| for (ExternalTextureBase* t : usages.externalTextures) { | |
| mExternalTextureUsages.insert(t); | |
| } | |
| for (ResourceTableBase* t : usages.usedResourceTables) { | |
| mUsedResourceTables.insert(t); | |
| } | |
| } | |
| void SyncScopeUsageTracker::AddBindGroup(BindGroupBase* group) { | |
| const auto* layout = group->GetLayout(); | |
| for (BindingIndex i : layout->GetBufferIndices()) { | |
| const BindingInfo& bindingInfo = group->GetLayout()->GetBindingInfo(i); | |
| const BufferBindingInfo& bufferInfo = | |
| std::get<BufferBindingInfo>(bindingInfo.bindingLayout); | |
| wgpu::BufferUsage usage; | |
| switch (bufferInfo.type) { | |
| case wgpu::BufferBindingType::Uniform: | |
| usage = wgpu::BufferUsage::Uniform; | |
| break; | |
| case wgpu::BufferBindingType::Storage: | |
| usage = wgpu::BufferUsage::Storage; | |
| break; | |
| case kInternalStorageBufferBinding: | |
| usage = kInternalStorageBuffer; | |
| break; | |
| case wgpu::BufferBindingType::ReadOnlyStorage: | |
| case kInternalReadOnlyStorageBufferBinding: | |
| usage = kReadOnlyStorageBuffer; | |
| break; | |
| case wgpu::BufferBindingType::BindingNotUsed: | |
| case wgpu::BufferBindingType::Undefined: | |
| DAWN_UNREACHABLE(); | |
| } | |
| BufferBase* buffer = group->GetBindingAsBuffer(i); | |
| BufferUsedAs(buffer, usage, bindingInfo.visibility); | |
| } | |
| for (BindingIndex i : layout->GetSampledTextureIndices()) { | |
| const BindingInfo& bindingInfo = group->GetLayout()->GetBindingInfo(i); | |
| const TextureBindingInfo& textureInfo = | |
| std::get<TextureBindingInfo>(bindingInfo.bindingLayout); | |
| wgpu::TextureUsage usage; | |
| switch (textureInfo.sampleType) { | |
| case kInternalResolveAttachmentSampleType: | |
| usage = kResolveAttachmentLoadingUsage; | |
| break; | |
| default: | |
| usage = wgpu::TextureUsage::TextureBinding; | |
| break; | |
| } | |
| TextureViewBase* view = group->GetBindingAsTextureView(i); | |
| TextureViewUsedAs(view, usage, bindingInfo.visibility); | |
| } | |
| for (BindingIndex i : layout->GetStorageTextureIndices()) { | |
| const BindingInfo& bindingInfo = group->GetLayout()->GetBindingInfo(i); | |
| const StorageTextureBindingInfo& textureInfo = | |
| std::get<StorageTextureBindingInfo>(bindingInfo.bindingLayout); | |
| wgpu::TextureUsage usage; | |
| switch (textureInfo.access) { | |
| case wgpu::StorageTextureAccess::WriteOnly: | |
| usage = kWriteOnlyStorageTexture; | |
| break; | |
| case wgpu::StorageTextureAccess::ReadWrite: | |
| usage = wgpu::TextureUsage::StorageBinding; | |
| break; | |
| case wgpu::StorageTextureAccess::ReadOnly: | |
| usage = kReadOnlyStorageTexture; | |
| break; | |
| case wgpu::StorageTextureAccess::BindingNotUsed: | |
| case wgpu::StorageTextureAccess::Undefined: | |
| DAWN_UNREACHABLE(); | |
| } | |
| TextureViewBase* view = group->GetBindingAsTextureView(i); | |
| TextureViewUsedAs(view, usage, bindingInfo.visibility); | |
| } | |
| for (BindingIndex i : layout->GetTexelBufferIndices()) { | |
| const BindingInfo& bindingInfo = group->GetLayout()->GetBindingInfo(i); | |
| const TexelBufferBindingInfo& texelInfo = | |
| std::get<TexelBufferBindingInfo>(bindingInfo.bindingLayout); | |
| wgpu::BufferUsage usage = wgpu::BufferUsage::None; | |
| switch (texelInfo.access) { | |
| case wgpu::TexelBufferAccess::ReadOnly: | |
| usage = kReadOnlyTexelBuffer; | |
| break; | |
| case wgpu::TexelBufferAccess::ReadWrite: | |
| usage = wgpu::BufferUsage::Storage; | |
| break; | |
| case wgpu::TexelBufferAccess::Undefined: | |
| DAWN_UNREACHABLE(); | |
| } | |
| BufferBase* buffer = group->GetBindingAsTexelBufferView(i)->GetBuffer(); | |
| BufferUsedAs(buffer, usage, bindingInfo.visibility); | |
| } | |
| for (const Ref<ExternalTextureBase>& externalTexture : group->GetBoundExternalTextures()) { | |
| // The ref of bound external texture can be null. | |
| if (externalTexture) { | |
| mExternalTextureUsages.insert(externalTexture.Get()); | |
| } | |
| } | |
| } | |
| void SyncScopeUsageTracker::AddResourceTableUsage(ResourceTableBase* table) { | |
| mUsedResourceTables.insert(table); | |
| } | |
| SyncScopeResourceUsage SyncScopeUsageTracker::AcquireSyncScopeUsage() { | |
| SyncScopeResourceUsage result; | |
| result.buffers.reserve(mBufferSyncInfos.size()); | |
| result.bufferSyncInfos.reserve(mBufferSyncInfos.size()); | |
| result.textures.reserve(mTextureSyncInfos.size()); | |
| result.textureSyncInfos.reserve(mTextureSyncInfos.size()); | |
| for (auto& [buffer, syncInfo] : mBufferSyncInfos) { | |
| result.buffers.push_back(buffer); | |
| result.bufferSyncInfos.push_back(std::move(syncInfo)); | |
| } | |
| mBufferSyncInfos.clear(); | |
| for (auto& [texture, syncInfo] : mTextureSyncInfos) { | |
| result.textures.push_back(texture); | |
| result.textureSyncInfos.push_back(std::move(syncInfo)); | |
| } | |
| mTextureSyncInfos.clear(); | |
| for (auto* const it : mExternalTextureUsages) { | |
| result.externalTextures.push_back(it); | |
| } | |
| mExternalTextureUsages.clear(); | |
| for (auto* const it : mUsedResourceTables) { | |
| result.usedResourceTables.push_back(it); | |
| } | |
| mUsedResourceTables.clear(); | |
| return result; | |
| } | |
| ComputePassResourceUsageTracker::ComputePassResourceUsageTracker() = default; | |
| ComputePassResourceUsageTracker::~ComputePassResourceUsageTracker() = default; | |
| void ComputePassResourceUsageTracker::AddDispatch(SyncScopeResourceUsage scope) { | |
| mUsage.dispatchUsages.push_back(std::move(scope)); | |
| } | |
| void ComputePassResourceUsageTracker::AddReferencedBuffer(BufferBase* buffer) { | |
| mUsage.referencedBuffers.insert(buffer); | |
| } | |
| void ComputePassResourceUsageTracker::AddReferencedResourceTable(ResourceTableBase* table) { | |
| mUsage.referencedResourceTables.insert(table); | |
| } | |
| void ComputePassResourceUsageTracker::AddResourcesReferencedByBindGroup(BindGroupBase* group) { | |
| const auto* layout = group->GetLayout(); | |
| for (BindingIndex i : layout->GetBufferIndices()) { | |
| mUsage.referencedBuffers.insert(group->GetBindingAsBuffer(i)); | |
| } | |
| for (BindingIndex i : layout->GetTextureIndices()) { | |
| mUsage.referencedTextures.insert(group->GetBindingAsTextureView(i)->GetTexture()); | |
| } | |
| for (BindingIndex i : layout->GetTexelBufferIndices()) { | |
| mUsage.referencedBuffers.insert(group->GetBindingAsTexelBufferView(i)->GetBuffer()); | |
| } | |
| for (const Ref<ExternalTextureBase>& externalTexture : group->GetBoundExternalTextures()) { | |
| // The ref of bound external texture can be null. | |
| if (externalTexture) { | |
| mUsage.referencedExternalTextures.insert(externalTexture.Get()); | |
| } | |
| } | |
| } | |
| ComputePassResourceUsage ComputePassResourceUsageTracker::AcquireResourceUsage() { | |
| return std::move(mUsage); | |
| } | |
| RenderPassResourceUsageTracker::RenderPassResourceUsageTracker() = default; | |
| RenderPassResourceUsageTracker::RenderPassResourceUsageTracker(RenderPassResourceUsageTracker&&) = | |
| default; | |
| RenderPassResourceUsageTracker::~RenderPassResourceUsageTracker() = default; | |
| RenderPassResourceUsageTracker& RenderPassResourceUsageTracker::operator=( | |
| RenderPassResourceUsageTracker&&) = default; | |
| void RenderPassResourceUsageTracker::MarkFramebufferFetchUsed() { | |
| mFramebufferFetchUsed = true; | |
| } | |
| RenderPassResourceUsage RenderPassResourceUsageTracker::AcquireResourceUsage() { | |
| RenderPassResourceUsage result; | |
| static_cast<SyncScopeResourceUsage&>(result) = AcquireSyncScopeUsage(); | |
| result.querySets.reserve(mQueryAvailabilities.size()); | |
| result.queryAvailabilities.reserve(mQueryAvailabilities.size()); | |
| for (auto& it : mQueryAvailabilities) { | |
| result.querySets.push_back(it.first); | |
| result.queryAvailabilities.push_back(std::move(it.second)); | |
| } | |
| mQueryAvailabilities.clear(); | |
| result.usesFramebufferFetch = mFramebufferFetchUsed; | |
| return result; | |
| } | |
| void RenderPassResourceUsageTracker::TrackQueryAvailability(QuerySetBase* querySet, | |
| QueryIndex queryIndex) { | |
| // The query availability only needs to be tracked again on render passes for checking | |
| // query overwrite on render pass and resetting query sets on the Vulkan backend. | |
| DAWN_ASSERT(querySet != nullptr); | |
| // Gets the iterator for that querySet or create a new vector of bool set to false | |
| // if the querySet wasn't registered. | |
| auto it = mQueryAvailabilities.try_emplace(querySet, querySet->GetQueryCount()).first; | |
| it->second[queryIndex] = true; | |
| } | |
| const QueryAvailabilityMap& RenderPassResourceUsageTracker::GetQueryAvailabilityMap() const { | |
| return mQueryAvailabilities; | |
| } | |
| } // namespace dawn::native |