Integrate Multiplanar External Texture Transform

Introduces the majority of the logic associated with enabling
multiplanar external textures. Removes most backend logic associated
with external textures in favor of expanding them into their components
in the frontend. Includes a basic e2e test demonstrating multiplanar
YUV-to-RGB conversion.

Bug: dawn:1082
Change-Id: Ib5c042e5639b1a8efe2954680abc346c8c6c76d7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/78248
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Brandon1 Jones <brandon1.jones@intel.com>
This commit is contained in:
Brandon Jones 2022-02-10 19:31:13 +00:00 committed by Dawn LUCI CQ
parent e6e2bcf436
commit cff04b4d3f
21 changed files with 505 additions and 161 deletions

View File

@ -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;

View File

@ -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<uint32_t>(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<TextureViewBase*>(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<ExternalTextureBase*>(mBindingData.bindings[bindingIndex].Get());
const std::vector<Ref<ExternalTextureBase>>& BindGroupBase::GetBoundExternalTextures() const {
return mBoundExternalTextures;
}
} // namespace dawn::native

View File

@ -51,7 +51,7 @@ namespace dawn::native {
SamplerBase* GetBindingAsSampler(BindingIndex bindingIndex) const;
TextureViewBase* GetBindingAsTextureView(BindingIndex bindingIndex);
const ityp::span<uint32_t, uint64_t>& GetUnverifiedBufferSizes() const;
ExternalTextureBase* GetBindingAsExternalTexture(BindingIndex bindingIndex);
const std::vector<Ref<ExternalTextureBase>>& 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<BindGroupLayoutBase> mLayout;
BindGroupLayoutBase::BindingDataPointers mBindingData;
// TODO:(dawn:1293): Store external textures in
// BindGroupLayoutBase::BindingDataPointers::bindings
std::vector<Ref<ExternalTextureBase>> mBoundExternalTextures;
};
} // namespace dawn::native

View File

@ -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<BindGroupLayoutEntry> ExtractAndExpandBglEntries(
const BindGroupLayoutDescriptor* descriptor,
BindingCounts* bindingCounts,
ExternalTextureBindingExpansionMap* externalTextureBindingExpansions) {
std::vector<BindGroupLayoutEntry> 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<BindGroupLayoutEntry> sortedBindings(
descriptor->entries, descriptor->entries + descriptor->entryCount);
mPipelineCompatibilityToken(pipelineCompatibilityToken),
mUnexpandedBindingCount(descriptor->entryCount) {
std::vector<BindGroupLayoutEntry> 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<uint32_t>(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 &&

View File

@ -32,6 +32,15 @@
#include <map>
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<BindingNumber, ExternalTextureBindingExpansion>;
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

View File

@ -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;
}

View File

@ -43,14 +43,13 @@ namespace dawn::native {
DeviceBase* device,
const ExternalTextureDescriptor* descriptor);
BufferBase* GetParamsBuffer() const;
const std::array<Ref<TextureViewBase>, 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<TextureBase> mDummyTexture;
Ref<BufferBase> mParamsBuffer;

View File

@ -124,24 +124,18 @@ namespace dawn::native {
break;
}
case BindingInfoType::ExternalTexture: {
ExternalTextureBase* externalTexture =
group->GetBindingAsExternalTexture(bindingIndex);
const std::array<Ref<TextureViewBase>, 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<ExternalTextureBase>& 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<Ref<TextureViewBase>, 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<ExternalTextureBase>& externalTexture : group->GetBoundExternalTextures()) {
mUsage.referencedExternalTextures.insert(externalTexture.Get());
}
}
ComputePassResourceUsage ComputePassResourceUsageTracker::AcquireResourceUsage() {

View File

@ -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<BindingNumber, dawn_native::ExternalTextureBindingExpansion>::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<uint32_t>(i),
static_cast<uint32_t>(expansion.second.plane0)}] = {
{static_cast<uint32_t>(i), static_cast<uint32_t>(expansion.second.plane1)},
{static_cast<uint32_t>(i), static_cast<uint32_t>(expansion.second.params)}};
}
}
tint::transform::Manager m;
if (!newBindingsMap.empty()) {
transformManager->Add<tint::transform::MultiplanarExternalTexture>();
transformInputs->Add<tint::transform::MultiplanarExternalTexture::NewBindingPoints>(
newBindingsMap);
}
}
MaybeError ShaderModuleBase::InitializeBase(ShaderModuleParseResult* parseResult) {
mTintProgram = std::move(parseResult->tintProgram);
mTintSource = std::move(parseResult->tintSource);

View File

@ -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);

View File

@ -177,21 +177,7 @@ namespace dawn::native::d3d12 {
}
case BindingInfoType::ExternalTexture: {
const std::array<Ref<TextureViewBase>, 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: {

View File

@ -753,7 +753,7 @@ namespace dawn::native::d3d12 {
ResultOrError<CompiledShader> 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<tint::transform::FirstIndexOffset>();
transformInputs.Add<tint::transform::FirstIndexOffset::BindingPoint>(
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<tint::transform::FirstIndexOffset::Data>()) {
// 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;

View File

@ -55,7 +55,7 @@ namespace dawn::native::d3d12 {
ResultOrError<CompiledShader> Compile(const ProgrammableStage& programmableStage,
SingleShaderStage stage,
PipelineLayout* layout,
const PipelineLayout* layout,
uint32_t compileFlags);
private:

View File

@ -517,28 +517,8 @@ namespace dawn::native::metal {
break;
}
case BindingInfoType::ExternalTexture: {
const std::array<Ref<TextureViewBase>, 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();
}
}
}

View File

@ -97,6 +97,8 @@ namespace dawn::native::metal {
transformManager.Add<tint::transform::SingleEntryPoint>();
transformInputs.Add<tint::transform::SingleEntryPoint::Config>(entryPointName);
AddExternalTextureTransform(layout, &transformManager, &transformInputs);
if (stage == SingleShaderStage::Vertex &&
GetDevice()->IsToggleEnabled(Toggle::MetalEnableVertexPulling)) {
transformManager.Add<tint::transform::VertexPulling>();

View File

@ -364,25 +364,9 @@ namespace dawn::native::opengl {
break;
}
case BindingInfoType::ExternalTexture: {
const std::array<Ref<TextureViewBase>, 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;
}
}
}
}

View File

@ -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::SingleEntryPoint>());
tint::transform::DataMap transformInputs;
transformInputs.Add<tint::transform::SingleEntryPoint::Config>(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));
}

View File

@ -130,21 +130,9 @@ namespace dawn::native::vulkan {
break;
}
case BindingInfoType::ExternalTexture: {
const std::array<Ref<dawn::native::TextureViewBase>, 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++;

View File

@ -165,6 +165,35 @@ namespace dawn::native::vulkan {
/* mayCollide */ false);
transformInputs.Add<tint::transform::SingleEntryPoint::Config>(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<BindingNumber, dawn_native::ExternalTextureBindingExpansion>::iterator it =
expansions.begin();
while (it != expansions.end()) {
newBindingsMap[{static_cast<uint32_t>(i),
static_cast<uint32_t>(bgl->GetBindingIndex(it->second.plane0))}] = {
{static_cast<uint32_t>(i),
static_cast<uint32_t>(bgl->GetBindingIndex(it->second.plane1))},
{static_cast<uint32_t>(i),
static_cast<uint32_t>(bgl->GetBindingIndex(it->second.params))}};
it++;
}
}
if (!newBindingsMap.empty()) {
transformManager.Add<tint::transform::MultiplanarExternalTexture>();
transformInputs.Add<tint::transform::MultiplanarExternalTexture::NewBindingPoints>(
newBindingsMap);
}
tint::Program program;
{
TRACE_EVENT0(GetDevice()->GetPlatform(), General, "RunTransforms");

View File

@ -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<f32> {
var positions : array<vec4<f32>, 3> = array<vec4<f32>, 3>(
var positions = array<vec4<f32>, 3>(
vec4<f32>(-1.0, 1.0, 0.0, 1.0),
vec4<f32>(-1.0, -1.0, 0.0, 1.0),
vec4<f32>(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<f32> {
var positions = array<vec4<f32>, 3>(
vec4<f32>(-1.0, 1.0, 0.0, 1.0),
vec4<f32>(-1.0, -1.0, 0.0, 1.0),
vec4<f32>(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<f32>)
-> @location(0) vec4<f32> {
return textureSampleLevel(t, s, FragCoord.xy / vec2<f32>(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<ConversionExpectation, 4> 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(),

View File

@ -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) {