diff --git a/src/dawn/common/Constants.h b/src/dawn/common/Constants.h index 74853a5d28..13b5995d22 100644 --- a/src/dawn/common/Constants.h +++ b/src/dawn/common/Constants.h @@ -58,7 +58,7 @@ static constexpr uint32_t kMaxQueryCount = 8192u; // An external texture occupies multiple binding slots. These are the per-external-texture bindings // needed. -static constexpr uint8_t kSampledTexturesPerExternalTexture = 3u; +static constexpr uint8_t kSampledTexturesPerExternalTexture = 4u; static constexpr uint8_t kSamplersPerExternalTexture = 1u; static constexpr uint8_t kUniformsPerExternalTexture = 1u; diff --git a/src/dawn/native/BindGroup.cpp b/src/dawn/native/BindGroup.cpp index 0f3fe4b599..a96843c848 100644 --- a/src/dawn/native/BindGroup.cpp +++ b/src/dawn/native/BindGroup.cpp @@ -240,12 +240,10 @@ namespace dawn::native { return {}; } - MaybeError ValidateExternalTextureBinding(const DeviceBase* device, - const BindGroupEntry& entry, - const BindingInfo& bindingInfo) { - const ExternalTextureBindingEntry* externalTextureBindingEntry = nullptr; - FindInChain(entry.nextInChain, &externalTextureBindingEntry); - + MaybeError ValidateExternalTextureBinding( + const DeviceBase* device, + const BindGroupEntry& entry, + const ExternalTextureBindingEntry* externalTextureBindingEntry) { DAWN_INVALID_IF(externalTextureBindingEntry == nullptr, "Binding entry external texture not set."); @@ -270,7 +268,7 @@ namespace dawn::native { DAWN_TRY(device->ValidateObject(descriptor->layout)); DAWN_INVALID_IF( - BindingIndex(descriptor->entryCount) != descriptor->layout->GetBindingCount(), + descriptor->entryCount != descriptor->layout->GetUnexpandedBindingCount(), "Number of entries (%u) did not match the number of entries (%u) specified in %s", descriptor->entryCount, static_cast(descriptor->layout->GetBindingCount()), descriptor->layout); @@ -296,6 +294,21 @@ namespace dawn::native { bindingsSet.set(bindingIndex); + // Below this block we validate entries based on the bind group layout, in which + // external textures have been expanded into their underlying contents. For this reason + // we must identify external texture binding entries by checking the bind group entry + // itself. + // TODO:(dawn:1293): Store external textures in + // BindGroupLayoutBase::BindingDataPointers::bindings so checking external textures can + // be moved in the switch below. + const ExternalTextureBindingEntry* externalTextureBindingEntry = nullptr; + FindInChain(entry.nextInChain, &externalTextureBindingEntry); + if (externalTextureBindingEntry != nullptr) { + DAWN_TRY( + ValidateExternalTextureBinding(device, entry, externalTextureBindingEntry)); + continue; + } + const BindingInfo& bindingInfo = descriptor->layout->GetBindingInfo(bindingIndex); // Perform binding-type specific validation. @@ -314,8 +327,7 @@ namespace dawn::native { "validating entries[%u] as a Sampler", i); break; case BindingInfoType::ExternalTexture: - DAWN_TRY_CONTEXT(ValidateExternalTextureBinding(device, entry, bindingInfo), - "validating entries[%u] as an ExternalTexture", i); + UNREACHABLE(); break; } } @@ -325,7 +337,7 @@ namespace dawn::native { // - Each binding must be set at most once // // We don't validate the equality because it wouldn't be possible to cover it with a test. - ASSERT(bindingsSet.count() == bindingMap.size()); + ASSERT(bindingsSet.count() == descriptor->layout->GetUnexpandedBindingCount()); return {}; } // anonymous namespace @@ -377,11 +389,45 @@ namespace dawn::native { continue; } + // Here we unpack external texture bindings into multiple additional bindings for the + // external texture's contents. New binding locations previously determined in the bind + // group layout are created in this bind group and filled with the external texture's + // underlying resources. const ExternalTextureBindingEntry* externalTextureBindingEntry = nullptr; FindInChain(entry.nextInChain, &externalTextureBindingEntry); if (externalTextureBindingEntry != nullptr) { - ASSERT(mBindingData.bindings[bindingIndex] == nullptr); - mBindingData.bindings[bindingIndex] = externalTextureBindingEntry->externalTexture; + mBoundExternalTextures.push_back(externalTextureBindingEntry->externalTexture); + + ExternalTextureBindingExpansionMap expansions = + mLayout->GetExternalTextureBindingExpansionMap(); + ExternalTextureBindingExpansionMap::iterator it = + expansions.find(BindingNumber(entry.binding)); + + ASSERT(it != expansions.end()); + + BindingIndex plane0BindingIndex = + descriptor->layout->GetBindingIndex(it->second.plane0); + BindingIndex plane1BindingIndex = + descriptor->layout->GetBindingIndex(it->second.plane1); + BindingIndex paramsBindingIndex = + descriptor->layout->GetBindingIndex(it->second.params); + + ASSERT(mBindingData.bindings[plane0BindingIndex] == nullptr); + + mBindingData.bindings[plane0BindingIndex] = + externalTextureBindingEntry->externalTexture->GetTextureViews()[0]; + + ASSERT(mBindingData.bindings[plane1BindingIndex] == nullptr); + mBindingData.bindings[plane1BindingIndex] = + externalTextureBindingEntry->externalTexture->GetTextureViews()[1]; + + ASSERT(mBindingData.bindings[paramsBindingIndex] == nullptr); + mBindingData.bindings[paramsBindingIndex] = + externalTextureBindingEntry->externalTexture->GetParamsBuffer(); + mBindingData.bufferData[paramsBindingIndex].offset = 0; + mBindingData.bufferData[paramsBindingIndex].size = + sizeof(dawn_native::ExternalTextureParams); + continue; } } @@ -475,12 +521,8 @@ namespace dawn::native { return static_cast(mBindingData.bindings[bindingIndex].Get()); } - ExternalTextureBase* BindGroupBase::GetBindingAsExternalTexture(BindingIndex bindingIndex) { - ASSERT(!IsError()); - ASSERT(bindingIndex < mLayout->GetBindingCount()); - ASSERT(mLayout->GetBindingInfo(bindingIndex).bindingType == - BindingInfoType::ExternalTexture); - return static_cast(mBindingData.bindings[bindingIndex].Get()); + const std::vector>& BindGroupBase::GetBoundExternalTextures() const { + return mBoundExternalTextures; } } // namespace dawn::native diff --git a/src/dawn/native/BindGroup.h b/src/dawn/native/BindGroup.h index 60bdd67fef..7ba883a9fa 100644 --- a/src/dawn/native/BindGroup.h +++ b/src/dawn/native/BindGroup.h @@ -51,7 +51,7 @@ namespace dawn::native { SamplerBase* GetBindingAsSampler(BindingIndex bindingIndex) const; TextureViewBase* GetBindingAsTextureView(BindingIndex bindingIndex); const ityp::span& GetUnverifiedBufferSizes() const; - ExternalTextureBase* GetBindingAsExternalTexture(BindingIndex bindingIndex); + const std::vector>& GetBoundExternalTextures() const; protected: // To save memory, the size of a bind group is dynamically determined and the bind group is @@ -85,6 +85,10 @@ namespace dawn::native { Ref mLayout; BindGroupLayoutBase::BindingDataPointers mBindingData; + + // TODO:(dawn:1293): Store external textures in + // BindGroupLayoutBase::BindingDataPointers::bindings + std::vector> mBoundExternalTextures; }; } // namespace dawn::native diff --git a/src/dawn/native/BindGroupLayout.cpp b/src/dawn/native/BindGroupLayout.cpp index 08c618cb8d..a7378abc4b 100644 --- a/src/dawn/native/BindGroupLayout.cpp +++ b/src/dawn/native/BindGroupLayout.cpp @@ -153,6 +153,90 @@ namespace dawn::native { return {}; } + BindGroupLayoutEntry CreateSampledTextureBindingForExternalTexture( + uint32_t binding, + wgpu::ShaderStage visibility) { + BindGroupLayoutEntry entry; + entry.binding = binding; + entry.visibility = visibility; + entry.texture.viewDimension = wgpu::TextureViewDimension::e2D; + entry.texture.multisampled = false; + entry.texture.sampleType = wgpu::TextureSampleType::Float; + return entry; + } + + BindGroupLayoutEntry CreateUniformBindingForExternalTexture(uint32_t binding, + wgpu::ShaderStage visibility) { + BindGroupLayoutEntry entry; + entry.binding = binding; + entry.visibility = visibility; + entry.buffer.hasDynamicOffset = false; + entry.buffer.type = wgpu::BufferBindingType::Uniform; + return entry; + } + + std::vector ExtractAndExpandBglEntries( + const BindGroupLayoutDescriptor* descriptor, + BindingCounts* bindingCounts, + ExternalTextureBindingExpansionMap* externalTextureBindingExpansions) { + std::vector expandedOutput; + + // When new bgl entries are created, we use binding numbers larger than + // kMaxBindingNumber to ensure there are no collisions. + uint32_t nextOpenBindingNumberForNewEntry = kMaxBindingNumber + 1; + for (uint32_t i = 0; i < descriptor->entryCount; i++) { + const BindGroupLayoutEntry& entry = descriptor->entries[i]; + const ExternalTextureBindingLayout* externalTextureBindingLayout = nullptr; + FindInChain(entry.nextInChain, &externalTextureBindingLayout); + // External textures are expanded from a texture_external into two sampled texture + // bindings and one uniform buffer binding. The original binding number is used + // for the first sampled texture. + if (externalTextureBindingLayout != nullptr) { + for (SingleShaderStage stage : IterateStages(entry.visibility)) { + // External textures are not fully implemented, which means that expanding + // the external texture at this time will not occupy the same number of + // binding slots as defined in the WebGPU specification. Here we prematurely + // increment the binding counts for an additional sampled textures and a + // sampler so that an external texture will occupy the correct number of + // slots for correct validation of shader binding limits. + // TODO:(dawn:1082): Consider removing this and instead making a change to + // the validation. + constexpr uint32_t kUnimplementedSampledTexturesPerExternalTexture = 2; + constexpr uint32_t kUnimplementedSamplersPerExternalTexture = 1; + bindingCounts->perStage[stage].sampledTextureCount += + kUnimplementedSampledTexturesPerExternalTexture; + bindingCounts->perStage[stage].samplerCount += + kUnimplementedSamplersPerExternalTexture; + } + + dawn_native::ExternalTextureBindingExpansion bindingExpansion; + + BindGroupLayoutEntry plane0Entry = + CreateSampledTextureBindingForExternalTexture(entry.binding, + entry.visibility); + bindingExpansion.plane0 = BindingNumber(plane0Entry.binding); + expandedOutput.push_back(plane0Entry); + + BindGroupLayoutEntry plane1Entry = + CreateSampledTextureBindingForExternalTexture( + nextOpenBindingNumberForNewEntry++, entry.visibility); + bindingExpansion.plane1 = BindingNumber(plane1Entry.binding); + expandedOutput.push_back(plane1Entry); + + BindGroupLayoutEntry paramsEntry = CreateUniformBindingForExternalTexture( + nextOpenBindingNumberForNewEntry++, entry.visibility); + bindingExpansion.params = BindingNumber(paramsEntry.binding); + expandedOutput.push_back(paramsEntry); + + externalTextureBindingExpansions->insert( + {BindingNumber(entry.binding), bindingExpansion}); + } else { + expandedOutput.push_back(entry); + } + } + + return expandedOutput; + } } // anonymous namespace MaybeError ValidateBindGroupLayoutDescriptor(DeviceBase* device, @@ -370,21 +454,21 @@ namespace dawn::native { PipelineCompatibilityToken pipelineCompatibilityToken, ApiObjectBase::UntrackedByDeviceTag tag) : ApiObjectBase(device, descriptor->label), - mBindingInfo(BindingIndex(descriptor->entryCount)), - mPipelineCompatibilityToken(pipelineCompatibilityToken) { - std::vector sortedBindings( - descriptor->entries, descriptor->entries + descriptor->entryCount); + mPipelineCompatibilityToken(pipelineCompatibilityToken), + mUnexpandedBindingCount(descriptor->entryCount) { + std::vector sortedBindings = ExtractAndExpandBglEntries( + descriptor, &mBindingCounts, &mExternalTextureBindingExpansionMap); std::sort(sortedBindings.begin(), sortedBindings.end(), SortBindingsCompare); - for (BindingIndex i{0}; i < mBindingInfo.size(); ++i) { + for (uint32_t i = 0; i < sortedBindings.size(); ++i) { const BindGroupLayoutEntry& binding = sortedBindings[static_cast(i)]; - mBindingInfo[i] = CreateBindGroupLayoutInfo(binding); + mBindingInfo.push_back(CreateBindGroupLayoutInfo(binding)); if (IsBufferBinding(binding)) { // Buffers must be contiguously packed at the start of the binding info. - ASSERT(GetBufferCount() == i); + ASSERT(GetBufferCount() == BindingIndex(i)); } IncrementBindingCounts(&mBindingCounts, binding); @@ -489,10 +573,23 @@ namespace dawn::native { return mBindingCounts.unverifiedBufferCount; } + uint32_t BindGroupLayoutBase::GetExternalTextureBindingCount() const { + return mExternalTextureBindingExpansionMap.size(); + } + const BindingCounts& BindGroupLayoutBase::GetBindingCountInfo() const { return mBindingCounts; } + const ExternalTextureBindingExpansionMap& + BindGroupLayoutBase::GetExternalTextureBindingExpansionMap() const { + return mExternalTextureBindingExpansionMap; + } + + uint32_t BindGroupLayoutBase::GetUnexpandedBindingCount() const { + return mUnexpandedBindingCount; + } + bool BindGroupLayoutBase::IsLayoutEqual(const BindGroupLayoutBase* other, bool excludePipelineCompatibiltyToken) const { if (!excludePipelineCompatibiltyToken && diff --git a/src/dawn/native/BindGroupLayout.h b/src/dawn/native/BindGroupLayout.h index 94d4e5eb55..2dfb846cd1 100644 --- a/src/dawn/native/BindGroupLayout.h +++ b/src/dawn/native/BindGroupLayout.h @@ -32,6 +32,15 @@ #include namespace dawn::native { + // TODO(dawn:1082): Minor optimization to use BindingIndex instead of BindingNumber + struct ExternalTextureBindingExpansion { + BindingNumber plane0; + BindingNumber plane1; + BindingNumber params; + }; + + using ExternalTextureBindingExpansionMap = + std::map; MaybeError ValidateBindGroupLayoutDescriptor(DeviceBase* device, const BindGroupLayoutDescriptor* descriptor, @@ -85,6 +94,13 @@ namespace dawn::native { // should be used to get typed integer counts. const BindingCounts& GetBindingCountInfo() const; + uint32_t GetExternalTextureBindingCount() const; + + // Used to specify unpacked external texture binding slots when transforming shader modules. + const ExternalTextureBindingExpansionMap& GetExternalTextureBindingExpansionMap() const; + + uint32_t GetUnexpandedBindingCount() const; + // Tests that the BindingInfo of two bind groups are equal, // ignoring their compatibility groups. bool IsLayoutEqual(const BindGroupLayoutBase* other, @@ -137,9 +153,13 @@ namespace dawn::native { // Map from BindGroupLayoutEntry.binding to packed indices. BindingMap mBindingMap; + ExternalTextureBindingExpansionMap mExternalTextureBindingExpansionMap; + // Non-0 if this BindGroupLayout was created as part of a default PipelineLayout. const PipelineCompatibilityToken mPipelineCompatibilityToken = PipelineCompatibilityToken(0); + + uint32_t mUnexpandedBindingCount; }; } // namespace dawn::native diff --git a/src/dawn/native/ExternalTexture.cpp b/src/dawn/native/ExternalTexture.cpp index 09ee1a210b..fe06ccc176 100644 --- a/src/dawn/native/ExternalTexture.cpp +++ b/src/dawn/native/ExternalTexture.cpp @@ -219,6 +219,10 @@ namespace dawn::native { return new ExternalTextureBase(device, ObjectBase::kError); } + BufferBase* ExternalTextureBase::GetParamsBuffer() const { + return mParamsBuffer.Get(); + } + ObjectType ExternalTextureBase::GetType() const { return ObjectType::ExternalTexture; } diff --git a/src/dawn/native/ExternalTexture.h b/src/dawn/native/ExternalTexture.h index 10460e756e..e32b6317f4 100644 --- a/src/dawn/native/ExternalTexture.h +++ b/src/dawn/native/ExternalTexture.h @@ -43,14 +43,13 @@ namespace dawn::native { DeviceBase* device, const ExternalTextureDescriptor* descriptor); + BufferBase* GetParamsBuffer() const; const std::array, kMaxPlanesPerFormat>& GetTextureViews() const; + ObjectType GetType() const override; MaybeError ValidateCanUseInSubmitNow() const; - MaybeError Initialize(DeviceBase* device, const ExternalTextureDescriptor* descriptor); static ExternalTextureBase* MakeError(DeviceBase* device); - ObjectType GetType() const override; - void APIDestroy(); protected: @@ -61,9 +60,11 @@ namespace dawn::native { ~ExternalTextureBase() override; private: - enum class ExternalTextureState { Alive, Destroyed }; ExternalTextureBase(DeviceBase* device, const ExternalTextureDescriptor* descriptor); + + enum class ExternalTextureState { Alive, Destroyed }; ExternalTextureBase(DeviceBase* device, ObjectBase::ErrorTag tag); + MaybeError Initialize(DeviceBase* device, const ExternalTextureDescriptor* descriptor); Ref mDummyTexture; Ref mParamsBuffer; diff --git a/src/dawn/native/PassResourceUsageTracker.cpp b/src/dawn/native/PassResourceUsageTracker.cpp index 34b66b67a7..b4814cfa62 100644 --- a/src/dawn/native/PassResourceUsageTracker.cpp +++ b/src/dawn/native/PassResourceUsageTracker.cpp @@ -124,24 +124,18 @@ namespace dawn::native { break; } - case BindingInfoType::ExternalTexture: { - ExternalTextureBase* externalTexture = - group->GetBindingAsExternalTexture(bindingIndex); - - const std::array, kMaxPlanesPerFormat>& textureViews = - externalTexture->GetTextureViews(); - - ASSERT(textureViews[2].Get() == nullptr); - - mExternalTextureUsages.insert(externalTexture); - TextureViewUsedAs(textureViews[0].Get(), wgpu::TextureUsage::TextureBinding); + case BindingInfoType::ExternalTexture: + UNREACHABLE(); break; - } case BindingInfoType::Sampler: break; } } + + for (const Ref& externalTexture : group->GetBoundExternalTextures()) { + mExternalTextureUsages.insert(externalTexture.Get()); + } } SyncScopeResourceUsage SyncScopeUsageTracker::AcquireSyncScopeUsage() { @@ -196,24 +190,17 @@ namespace dawn::native { break; } - case BindingInfoType::ExternalTexture: { - ExternalTextureBase* externalTexture = - group->GetBindingAsExternalTexture(index); - const std::array, kMaxPlanesPerFormat>& textureViews = - externalTexture->GetTextureViews(); - - ASSERT(textureViews[2].Get() == nullptr); - - mUsage.referencedExternalTextures.insert(externalTexture); - mUsage.referencedTextures.insert(textureViews[0].Get()->GetTexture()); - break; - } - + case BindingInfoType::ExternalTexture: + UNREACHABLE(); case BindingInfoType::StorageTexture: case BindingInfoType::Sampler: break; } } + + for (const Ref& externalTexture : group->GetBoundExternalTextures()) { + mUsage.referencedExternalTextures.insert(externalTexture.Get()); + } } ComputePassResourceUsage ComputePassResourceUsageTracker::AcquireResourceUsage() { diff --git a/src/dawn/native/ShaderModule.cpp b/src/dawn/native/ShaderModule.cpp index 5e343fc106..0467d3272a 100644 --- a/src/dawn/native/ShaderModule.cpp +++ b/src/dawn/native/ShaderModule.cpp @@ -15,6 +15,7 @@ #include "dawn/native/ShaderModule.h" #include "absl/strings/str_format.h" +#include "dawn/common/BitSetIterator.h" #include "dawn/common/Constants.h" #include "dawn/common/HashUtils.h" #include "dawn/native/BindGroupLayout.h" @@ -444,6 +445,26 @@ namespace dawn::native { const ShaderBindingInfo& shaderInfo) { const BindGroupLayoutBase::BindingMap& layoutBindings = layout->GetBindingMap(); + // An external texture binding found in the shader will later be expanded into multiple + // bindings at compile time. This expansion will have already happened in the bgl - so + // the shader and bgl will always mismatch at this point. Expansion info is contained in + // the bgl object, so we can still verify the bgl used to have an external texture in + // the slot corresponding to the shader reflection. + if (shaderInfo.bindingType == BindingInfoType::ExternalTexture) { + // If an external texture binding used to exist in the bgl, it will be found as a + // key in the ExternalTextureBindingExpansions map. + ExternalTextureBindingExpansionMap expansions = + layout->GetExternalTextureBindingExpansionMap(); + std::map::iterator it = + expansions.find(bindingNumber); + // TODO(dawn:563): Provide info about the binding types. + DAWN_INVALID_IF(it == expansions.end(), + "Binding type in the shader (texture_external) doesn't match the " + "type in the layout."); + + return {}; + } + const auto& bindingIt = layoutBindings.find(bindingNumber); DAWN_INVALID_IF(bindingIt == layoutBindings.end(), "Binding doesn't exist in %s.", layout); @@ -452,9 +473,16 @@ namespace dawn::native { const BindingInfo& layoutInfo = layout->GetBindingInfo(bindingIndex); // TODO(dawn:563): Provide info about the binding types. - DAWN_INVALID_IF(layoutInfo.bindingType != shaderInfo.bindingType, - "Binding type (buffer vs. texture vs. sampler) doesn't match the type " - "in the layout."); + DAWN_INVALID_IF( + layoutInfo.bindingType != shaderInfo.bindingType, + "Binding type (buffer vs. texture vs. sampler vs. external) doesn't match the type " + "in the layout."); + + ExternalTextureBindingExpansionMap expansions = + layout->GetExternalTextureBindingExpansionMap(); + DAWN_INVALID_IF(expansions.find(bindingNumber) != expansions.end(), + "Binding type (buffer vs. texture vs. sampler vs. external) doesn't " + "match the type in the layout."); // TODO(dawn:563): Provide info about the visibility. DAWN_INVALID_IF( @@ -509,11 +537,6 @@ namespace dawn::native { break; } - case BindingInfoType::ExternalTexture: { - // Nothing to validate! (yet?) - break; - } - case BindingInfoType::Buffer: { // Binding mismatch between shader and bind group is invalid. For example, a // writable binding in the shader with a readonly storage buffer in the bind @@ -551,6 +574,11 @@ namespace dawn::native { shaderInfo.sampler.isComparison, layoutInfo.sampler.type == wgpu::SamplerBindingType::Comparison); break; + + case BindingInfoType::ExternalTexture: { + UNREACHABLE(); + break; + } } return {}; @@ -1264,6 +1292,30 @@ namespace dawn::native { return mCompilationMessages.get(); } + // static + void ShaderModuleBase::AddExternalTextureTransform(const PipelineLayoutBase* layout, + tint::transform::Manager* transformManager, + tint::transform::DataMap* transformInputs) { + tint::transform::MultiplanarExternalTexture::BindingsMap newBindingsMap; + for (BindGroupIndex i : IterateBitSet(layout->GetBindGroupLayoutsMask())) { + const BindGroupLayoutBase* bgl = layout->GetBindGroupLayout(i); + + for (const auto& expansion : bgl->GetExternalTextureBindingExpansionMap()) { + newBindingsMap[{static_cast(i), + static_cast(expansion.second.plane0)}] = { + {static_cast(i), static_cast(expansion.second.plane1)}, + {static_cast(i), static_cast(expansion.second.params)}}; + } + } + tint::transform::Manager m; + + if (!newBindingsMap.empty()) { + transformManager->Add(); + transformInputs->Add( + newBindingsMap); + } + } + MaybeError ShaderModuleBase::InitializeBase(ShaderModuleParseResult* parseResult) { mTintProgram = std::move(parseResult->tintProgram); mTintSource = std::move(parseResult->tintSource); diff --git a/src/dawn/native/ShaderModule.h b/src/dawn/native/ShaderModule.h index a2a884c700..ea446840d4 100644 --- a/src/dawn/native/ShaderModule.h +++ b/src/dawn/native/ShaderModule.h @@ -41,6 +41,7 @@ namespace tint { namespace transform { class DataMap; + class Manager; class Transform; class VertexPulling; } // namespace transform @@ -282,6 +283,10 @@ namespace dawn::native { MaybeError InitializeBase(ShaderModuleParseResult* parseResult); + static void AddExternalTextureTransform(const PipelineLayoutBase* layout, + tint::transform::Manager* transformManager, + tint::transform::DataMap* transformInputs); + private: ShaderModuleBase(DeviceBase* device, ObjectBase::ErrorTag tag); diff --git a/src/dawn/native/d3d12/BindGroupD3D12.cpp b/src/dawn/native/d3d12/BindGroupD3D12.cpp index fd604a3fdd..8594455a3a 100644 --- a/src/dawn/native/d3d12/BindGroupD3D12.cpp +++ b/src/dawn/native/d3d12/BindGroupD3D12.cpp @@ -177,21 +177,7 @@ namespace dawn::native::d3d12 { } case BindingInfoType::ExternalTexture: { - const std::array, kMaxPlanesPerFormat>& views = - GetBindingAsExternalTexture(bindingIndex)->GetTextureViews(); - - ASSERT(views[2].Get() == nullptr); - - auto& srv = ToBackend(views[0])->GetSRVDescriptor(); - - ID3D12Resource* resource = - ToBackend(views[0]->GetTexture())->GetD3D12Resource(); - - d3d12Device->CreateShaderResourceView( - resource, &srv, - viewAllocation.OffsetFrom(viewSizeIncrement, - descriptorHeapOffsets[bindingIndex])); - break; + UNREACHABLE(); } case BindingInfoType::Sampler: { diff --git a/src/dawn/native/d3d12/ShaderModuleD3D12.cpp b/src/dawn/native/d3d12/ShaderModuleD3D12.cpp index cfa4f54ede..0dea76e1d6 100644 --- a/src/dawn/native/d3d12/ShaderModuleD3D12.cpp +++ b/src/dawn/native/d3d12/ShaderModuleD3D12.cpp @@ -753,7 +753,7 @@ namespace dawn::native::d3d12 { ResultOrError ShaderModule::Compile(const ProgrammableStage& programmableStage, SingleShaderStage stage, - PipelineLayout* layout, + const PipelineLayout* layout, uint32_t compileFlags) { TRACE_EVENT0(GetDevice()->GetPlatform(), General, "ShaderModuleD3D12::Compile"); ASSERT(!IsError()); @@ -767,19 +767,24 @@ namespace dawn::native::d3d12 { tint::transform::Manager transformManager; tint::transform::DataMap transformInputs; - const tint::Program* program; + const tint::Program* program = GetTintProgram(); tint::Program programAsValue; + + AddExternalTextureTransform(layout, &transformManager, &transformInputs); + if (stage == SingleShaderStage::Vertex) { transformManager.Add(); transformInputs.Add( layout->GetFirstIndexOffsetShaderRegister(), layout->GetFirstIndexOffsetRegisterSpace()); + } - tint::transform::DataMap transformOutputs; - DAWN_TRY_ASSIGN(programAsValue, - RunTransforms(&transformManager, GetTintProgram(), transformInputs, - &transformOutputs, nullptr)); + tint::transform::DataMap transformOutputs; + DAWN_TRY_ASSIGN(programAsValue, RunTransforms(&transformManager, program, transformInputs, + &transformOutputs, nullptr)); + program = &programAsValue; + if (stage == SingleShaderStage::Vertex) { if (auto* data = transformOutputs.Get()) { // TODO(dawn:549): Consider adding this information to the pipeline cache once we // can store more than the shader blob in it. @@ -793,10 +798,6 @@ namespace dawn::native::d3d12 { data->first_instance_offset; } } - - program = &programAsValue; - } else { - program = GetTintProgram(); } ShaderCompilationRequest request; diff --git a/src/dawn/native/d3d12/ShaderModuleD3D12.h b/src/dawn/native/d3d12/ShaderModuleD3D12.h index 13d00e5a45..2fd3a80380 100644 --- a/src/dawn/native/d3d12/ShaderModuleD3D12.h +++ b/src/dawn/native/d3d12/ShaderModuleD3D12.h @@ -55,7 +55,7 @@ namespace dawn::native::d3d12 { ResultOrError Compile(const ProgrammableStage& programmableStage, SingleShaderStage stage, - PipelineLayout* layout, + const PipelineLayout* layout, uint32_t compileFlags); private: diff --git a/src/dawn/native/metal/CommandBufferMTL.mm b/src/dawn/native/metal/CommandBufferMTL.mm index 5ad9e03a5c..b9a4960012 100644 --- a/src/dawn/native/metal/CommandBufferMTL.mm +++ b/src/dawn/native/metal/CommandBufferMTL.mm @@ -517,28 +517,8 @@ namespace dawn::native::metal { break; } - case BindingInfoType::ExternalTexture: { - const std::array, kMaxPlanesPerFormat>& views = - group->GetBindingAsExternalTexture(bindingIndex)->GetTextureViews(); - - ASSERT(views[2].Get() == nullptr); - - TextureView* textureView = ToBackend(views[0].Get()); - - if (hasVertStage) { - [render setVertexTexture:textureView->GetMTLTexture() - atIndex:vertIndex]; - } - if (hasFragStage) { - [render setFragmentTexture:textureView->GetMTLTexture() - atIndex:fragIndex]; - } - if (hasComputeStage) { - [compute setTexture:textureView->GetMTLTexture() - atIndex:computeIndex]; - } - break; - } + case BindingInfoType::ExternalTexture: + UNREACHABLE(); } } } diff --git a/src/dawn/native/metal/ShaderModuleMTL.mm b/src/dawn/native/metal/ShaderModuleMTL.mm index 79a705dbc9..e182898d2a 100644 --- a/src/dawn/native/metal/ShaderModuleMTL.mm +++ b/src/dawn/native/metal/ShaderModuleMTL.mm @@ -97,6 +97,8 @@ namespace dawn::native::metal { transformManager.Add(); transformInputs.Add(entryPointName); + AddExternalTextureTransform(layout, &transformManager, &transformInputs); + if (stage == SingleShaderStage::Vertex && GetDevice()->IsToggleEnabled(Toggle::MetalEnableVertexPulling)) { transformManager.Add(); diff --git a/src/dawn/native/opengl/CommandBufferGL.cpp b/src/dawn/native/opengl/CommandBufferGL.cpp index 4efc8839f2..c71d8f6d3e 100644 --- a/src/dawn/native/opengl/CommandBufferGL.cpp +++ b/src/dawn/native/opengl/CommandBufferGL.cpp @@ -364,25 +364,9 @@ namespace dawn::native::opengl { break; } - case BindingInfoType::ExternalTexture: { - const std::array, kMaxPlanesPerFormat>& - textureViews = mBindGroups[index] - ->GetBindingAsExternalTexture(bindingIndex) - ->GetTextureViews(); - - ASSERT(textureViews[2].Get() == nullptr); - - TextureView* view = ToBackend(textureViews[0].Get()); - GLuint handle = view->GetHandle(); - GLenum target = view->GetGLTarget(); - GLuint viewIndex = indices[bindingIndex]; - - for (auto unit : mPipeline->GetTextureUnitsForTextureView(viewIndex)) { - gl.ActiveTexture(GL_TEXTURE0 + unit); - gl.BindTexture(target, handle); - } + case BindingInfoType::ExternalTexture: + UNREACHABLE(); break; - } } } } diff --git a/src/dawn/native/opengl/ShaderModuleGL.cpp b/src/dawn/native/opengl/ShaderModuleGL.cpp index bdafcc6e7f..fc2ac493b2 100644 --- a/src/dawn/native/opengl/ShaderModuleGL.cpp +++ b/src/dawn/native/opengl/ShaderModuleGL.cpp @@ -268,15 +268,18 @@ namespace dawn::native::opengl { const PipelineLayout* layout, bool* needsDummySampler) const { TRACE_EVENT0(GetDevice()->GetPlatform(), General, "TranslateToGLSL"); - tint::transform::SingleEntryPoint singleEntryPointTransform; + tint::transform::Manager transformManager; + transformManager.append(std::make_unique()); tint::transform::DataMap transformInputs; transformInputs.Add(entryPointName); + AddExternalTextureTransform(layout, &transformManager, &transformInputs); + tint::Program program; { TRACE_EVENT0(GetDevice()->GetPlatform(), General, "RunTransforms"); - DAWN_TRY_ASSIGN(program, RunTransforms(&singleEntryPointTransform, GetTintProgram(), + DAWN_TRY_ASSIGN(program, RunTransforms(&transformManager, GetTintProgram(), transformInputs, nullptr, nullptr)); } diff --git a/src/dawn/native/vulkan/BindGroupVk.cpp b/src/dawn/native/vulkan/BindGroupVk.cpp index 12703f227c..00e70cfc16 100644 --- a/src/dawn/native/vulkan/BindGroupVk.cpp +++ b/src/dawn/native/vulkan/BindGroupVk.cpp @@ -130,21 +130,9 @@ namespace dawn::native::vulkan { break; } - case BindingInfoType::ExternalTexture: { - const std::array, kMaxPlanesPerFormat>& - textureViews = GetBindingAsExternalTexture(bindingIndex)->GetTextureViews(); - - ASSERT(textureViews[2].Get() == nullptr); - - TextureView* view = ToBackend(textureViews[0].Get()); - - writeImageInfo[numWrites].imageView = view->GetHandle(); - writeImageInfo[numWrites].imageLayout = VulkanImageLayout( - ToBackend(view->GetTexture()), wgpu::TextureUsage::TextureBinding); - - write.pImageInfo = &writeImageInfo[numWrites]; + case BindingInfoType::ExternalTexture: + UNREACHABLE(); break; - } } numWrites++; diff --git a/src/dawn/native/vulkan/ShaderModuleVk.cpp b/src/dawn/native/vulkan/ShaderModuleVk.cpp index d0ad35b5f3..9c1e7df4d0 100644 --- a/src/dawn/native/vulkan/ShaderModuleVk.cpp +++ b/src/dawn/native/vulkan/ShaderModuleVk.cpp @@ -165,6 +165,35 @@ namespace dawn::native::vulkan { /* mayCollide */ false); transformInputs.Add(entryPointName); + // Transform external textures into the binding locations specified in the bgl + // TODO(dawn:1082): Replace this block with ShaderModuleBase::AddExternalTextureTransform. + tint::transform::MultiplanarExternalTexture::BindingsMap newBindingsMap; + for (BindGroupIndex i : IterateBitSet(layout->GetBindGroupLayoutsMask())) { + BindGroupLayoutBase* bgl = layout->GetBindGroupLayout(i); + + ExternalTextureBindingExpansionMap expansions = + bgl->GetExternalTextureBindingExpansionMap(); + + std::map::iterator it = + expansions.begin(); + + while (it != expansions.end()) { + newBindingsMap[{static_cast(i), + static_cast(bgl->GetBindingIndex(it->second.plane0))}] = { + {static_cast(i), + static_cast(bgl->GetBindingIndex(it->second.plane1))}, + {static_cast(i), + static_cast(bgl->GetBindingIndex(it->second.params))}}; + it++; + } + } + + if (!newBindingsMap.empty()) { + transformManager.Add(); + transformInputs.Add( + newBindingsMap); + } + tint::Program program; { TRACE_EVENT0(GetDevice()->GetPlatform(), General, "RunTransforms"); diff --git a/src/dawn/tests/end2end/ExternalTextureTests.cpp b/src/dawn/tests/end2end/ExternalTextureTests.cpp index e911837830..591046264f 100644 --- a/src/dawn/tests/end2end/ExternalTextureTests.cpp +++ b/src/dawn/tests/end2end/ExternalTextureTests.cpp @@ -61,9 +61,13 @@ TEST_P(ExternalTextureTests, CreateExternalTextureSuccess) { } TEST_P(ExternalTextureTests, SampleExternalTexture) { - wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"( + // TODO(crbug.com/dawn/1263): SPIR-V has an issue compiling the output from Tint's external + // texture transform. Re-enable this test for OpenGL when the switch to Tint is complete. + DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES()); + + const wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"( @stage(vertex) fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4 { - var positions : array, 3> = array, 3>( + var positions = array, 3>( vec4(-1.0, 1.0, 0.0, 1.0), vec4(-1.0, -1.0, 0.0, 1.0), vec4(1.0, 1.0, 0.0, 1.0) @@ -102,6 +106,13 @@ TEST_P(ExternalTextureTests, SampleExternalTexture) { queue.Submit(1, &commands); } + // Pipeline Creation + utils::ComboRenderPipelineDescriptor descriptor; + descriptor.vertex.module = vsModule; + descriptor.cFragment.module = fsModule; + descriptor.cTargets[0].format = kFormat; + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); + // Create an ExternalTextureDescriptor from the texture view wgpu::ExternalTextureDescriptor externalDesc; externalDesc.plane0 = externalView; @@ -112,19 +123,8 @@ TEST_P(ExternalTextureTests, SampleExternalTexture) { // Create a sampler and bind group wgpu::Sampler sampler = device.CreateSampler(); - wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( - device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}, - {1, wgpu::ShaderStage::Fragment, &utils::kExternalTextureBindingLayout}}); - wgpu::BindGroup bindGroup = - utils::MakeBindGroup(device, bgl, {{0, sampler}, {1, externalTexture}}); - - // Pipeline Creation - utils::ComboRenderPipelineDescriptor descriptor; - descriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl); - descriptor.vertex.module = vsModule; - descriptor.cFragment.module = fsModule; - descriptor.cTargets[0].format = kFormat; - wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, sampler}, {1, externalTexture}}); // Run the shader, which should sample from the external texture and draw a triangle into the // upper left corner of the render texture. @@ -145,6 +145,114 @@ TEST_P(ExternalTextureTests, SampleExternalTexture) { EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderTexture, 0, 0); } +TEST_P(ExternalTextureTests, SampleMultiplanarExternalTexture) { + // TODO(crbug.com/dawn/1263): SPIR-V has an issue compiling the output from Tint's external + // texture transform. Re-enable this test for OpenGL when the switch to Tint is complete. + DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES()); + + const wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"( + @stage(vertex) fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4 { + var positions = array, 3>( + vec4(-1.0, 1.0, 0.0, 1.0), + vec4(-1.0, -1.0, 0.0, 1.0), + vec4(1.0, 1.0, 0.0, 1.0) + ); + return positions[VertexIndex]; + })"); + + const wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"( + @group(0) @binding(0) var s : sampler; + @group(0) @binding(1) var t : texture_external; + + @stage(fragment) fn main(@builtin(position) FragCoord : vec4) + -> @location(0) vec4 { + return textureSampleLevel(t, s, FragCoord.xy / vec2(4.0, 4.0)); + })"); + + wgpu::Texture sampledTexturePlane0 = + Create2DTexture(device, kWidth, kHeight, wgpu::TextureFormat::R8Unorm, + wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); + wgpu::Texture sampledTexturePlane1 = + Create2DTexture(device, kWidth, kHeight, wgpu::TextureFormat::RG8Unorm, + wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); + + wgpu::Texture renderTexture = + Create2DTexture(device, kWidth, kHeight, kFormat, + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); + + // Create a texture view for the external texture + wgpu::TextureView externalViewPlane0 = sampledTexturePlane0.CreateView(); + wgpu::TextureView externalViewPlane1 = sampledTexturePlane1.CreateView(); + + struct ConversionExpectation { + double y; + double u; + double v; + RGBA8 rgba; + }; + + std::array expectations = {{{0.0f, 0.5f, 0.5f, RGBA8::kBlack}, + {0.298f, 0.329f, 1.0f, RGBA8::kRed}, + {0.584f, -0.168f, -0.823f, RGBA8::kGreen}, + {0.113f, 1.0f, 0.419f, RGBA8::kBlue}}}; + + for (ConversionExpectation expectation : expectations) { + // Initialize the texture planes with YUV data + { + utils::ComboRenderPassDescriptor renderPass({externalViewPlane0, externalViewPlane1}, + nullptr); + renderPass.cColorAttachments[0].clearColor = {expectation.y, 0.0f, 0.0f, 0.0f}; + renderPass.cColorAttachments[1].clearColor = {expectation.u, expectation.v, 0.0f, 0.0f}; + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.End(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + + // Pipeline Creation + utils::ComboRenderPipelineDescriptor descriptor; + // descriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl); + descriptor.vertex.module = vsModule; + descriptor.cFragment.module = fsModule; + descriptor.cTargets[0].format = kFormat; + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); + + // Create an ExternalTextureDescriptor from the texture views + wgpu::ExternalTextureDescriptor externalDesc; + externalDesc.plane0 = externalViewPlane0; + externalDesc.plane1 = externalViewPlane1; + + // Import the external texture + wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); + + // Create a sampler and bind group + wgpu::Sampler sampler = device.CreateSampler(); + + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, sampler}, {1, externalTexture}}); + + // Run the shader, which should sample from the external texture and draw a triangle into + // the upper left corner of the render texture. + wgpu::TextureView renderView = renderTexture.CreateView(); + utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + { + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(3); + pass.End(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(expectation.rgba, renderTexture, 0, 0); + } +} + DAWN_INSTANTIATE_TEST(ExternalTextureTests, D3D12Backend(), MetalBackend(), diff --git a/src/dawn/tests/unittests/validation/ExternalTextureTests.cpp b/src/dawn/tests/unittests/validation/ExternalTextureTests.cpp index 61aa8f032a..fb62e5ab20 100644 --- a/src/dawn/tests/unittests/validation/ExternalTextureTests.cpp +++ b/src/dawn/tests/unittests/validation/ExternalTextureTests.cpp @@ -264,6 +264,57 @@ namespace { } } + // Test that submitting a render pass that contains a dereferenced external texture results in + // success + TEST_F(ExternalTextureTest, SubmitDereferencedExternalTextureInRenderPass) { + wgpu::TextureDescriptor textureDescriptor = CreateTextureDescriptor(); + wgpu::Texture texture = device.CreateTexture(&textureDescriptor); + + wgpu::ExternalTextureDescriptor externalDesc; + externalDesc.plane0 = texture.CreateView(); + wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); + + // Create a bind group that contains the external texture. + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, &utils::kExternalTextureBindingLayout}}); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, externalTexture}}); + + // Create another texture to use as a color attachment. + wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor(); + wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor); + wgpu::TextureView renderView = renderTexture.CreateView(); + + utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr); + + // Control case should succeed. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + { + pass.SetBindGroup(0, bindGroup); + pass.End(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + + queue.Submit(1, &commands); + } + + // Dereferencing the external texture should not result in a use-after-free error. + { + externalTexture = nullptr; + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + { + pass.SetBindGroup(0, bindGroup); + pass.End(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + } + // Test that submitting a render pass that contains a destroyed external texture plane // results in an error. TEST_F(ExternalTextureTest, SubmitDestroyedExternalTexturePlaneInRenderPass) {