Implement LoadResolveTexture in vulkan.
Using 2 subpasses
Bug: dawn:1710
Change-Id: If1c2ddcbadc502388f01ba56ccd0fc6966a4b5b1
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/187562
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Quyen Le <lehoangquyen@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/docs/dawn/features/dawn_load_resolve_texture.md b/docs/dawn/features/dawn_load_resolve_texture.md
index 7da8a3f..02b739b 100644
--- a/docs/dawn/features/dawn_load_resolve_texture.md
+++ b/docs/dawn/features/dawn_load_resolve_texture.md
@@ -74,3 +74,4 @@
- If render pipeline's color target `i` has no `wgpu::ColorTargetStateExpandResolveTextureDawn` included, then the compatible render pass's attachment `i` **must not** have any resolve target.
- Currently the `ExpandResolveTexture` LoadOp only works on color attachment, this could be changed in future.
- The texture is not supported if it is not resolvable by WebGPU standard. This means this feature currently doesn't work with integer textures.
+ - Using `ExpandResolveTexture` load op on a multiplanar texture's view is not supported currently.
diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn
index b64a7e8..f2ea39e 100644
--- a/src/dawn/native/BUILD.gn
+++ b/src/dawn/native/BUILD.gn
@@ -804,6 +804,8 @@
"vulkan/RenderPassCache.h",
"vulkan/RenderPipelineVk.cpp",
"vulkan/RenderPipelineVk.h",
+ "vulkan/ResolveTextureLoadingUtilsVk.cpp",
+ "vulkan/ResolveTextureLoadingUtilsVk.h",
"vulkan/ResourceHeapVk.cpp",
"vulkan/ResourceHeapVk.h",
"vulkan/ResourceMemoryAllocatorVk.cpp",
diff --git a/src/dawn/native/BindGroupLayoutInternal.cpp b/src/dawn/native/BindGroupLayoutInternal.cpp
index 2290810..711c9ec 100644
--- a/src/dawn/native/BindGroupLayoutInternal.cpp
+++ b/src/dawn/native/BindGroupLayoutInternal.cpp
@@ -143,7 +143,16 @@
// viewDimension defaults to 2D if left undefined, needs validation otherwise.
wgpu::TextureViewDimension viewDimension = wgpu::TextureViewDimension::e2D;
if (texture.viewDimension != wgpu::TextureViewDimension::Undefined) {
- DAWN_TRY(ValidateTextureViewDimension(texture.viewDimension));
+ switch (texture.viewDimension) {
+ case kInternalInputAttachmentDim:
+ if (allowInternalBinding) {
+ break;
+ }
+ // should return validation error.
+ [[fallthrough]];
+ default:
+ DAWN_TRY(ValidateTextureViewDimension(texture.viewDimension));
+ }
viewDimension = texture.viewDimension;
}
diff --git a/src/dawn/native/BindingInfo.cpp b/src/dawn/native/BindingInfo.cpp
index e9199eb..a2e997a 100644
--- a/src/dawn/native/BindingInfo.cpp
+++ b/src/dawn/native/BindingInfo.cpp
@@ -90,7 +90,10 @@
} else if (entry->sampler.type != wgpu::SamplerBindingType::Undefined) {
perStageBindingCountMember = &PerStageBindingCounts::samplerCount;
} else if (entry->texture.sampleType != wgpu::TextureSampleType::Undefined) {
- if (entry->texture.viewDimension != kInternalInputAttachmentDim) {
+ if (entry->texture.viewDimension == kInternalInputAttachmentDim) {
+ // Internal use only.
+ return;
+ } else {
perStageBindingCountMember = &PerStageBindingCounts::sampledTextureCount;
}
} else if (entry->storageTexture.access != wgpu::StorageTextureAccess::Undefined) {
diff --git a/src/dawn/native/BlitColorToColorWithDraw.cpp b/src/dawn/native/BlitColorToColorWithDraw.cpp
index d2115b7..772167c 100644
--- a/src/dawn/native/BlitColorToColorWithDraw.cpp
+++ b/src/dawn/native/BlitColorToColorWithDraw.cpp
@@ -196,7 +196,7 @@
RenderPassEncoder* renderEncoder,
const RenderPassDescriptor* renderPassDescriptor) {
DAWN_ASSERT(device->IsLockedByCurrentThreadIfNeeded());
- DAWN_ASSERT(device->IsResolveTextureBlitWithDrawSupported());
+ DAWN_ASSERT(device->CanTextureLoadResolveTargetInTheSameRenderpass());
BlitColorToColorWithDrawPipelineKey pipelineKey;
for (uint8_t i = 0; i < renderPassDescriptor->colorAttachmentCount; ++i) {
diff --git a/src/dawn/native/BlitColorToColorWithDraw.h b/src/dawn/native/BlitColorToColorWithDraw.h
index ac5463a..6a47ab6 100644
--- a/src/dawn/native/BlitColorToColorWithDraw.h
+++ b/src/dawn/native/BlitColorToColorWithDraw.h
@@ -75,7 +75,7 @@
//
// The function assumes that the render pass is already started. It won't break the render pass,
// just performing a draw call to blit.
-// This is only valid if the device's IsResolveTextureBlitWithDrawSupported() is true.
+// This is only valid if the device's CanTextureLoadResolveTargetInTheSameRenderpass() is true.
MaybeError ExpandResolveTextureWithDraw(DeviceBase* device,
RenderPassEncoder* renderEncoder,
const RenderPassDescriptor* renderPassDescriptor);
diff --git a/src/dawn/native/CMakeLists.txt b/src/dawn/native/CMakeLists.txt
index 259a0a4..3178f05 100644
--- a/src/dawn/native/CMakeLists.txt
+++ b/src/dawn/native/CMakeLists.txt
@@ -679,6 +679,8 @@
"vulkan/RenderPassCache.h"
"vulkan/RenderPipelineVk.cpp"
"vulkan/RenderPipelineVk.h"
+ "vulkan/ResolveTextureLoadingUtilsVk.cpp"
+ "vulkan/ResolveTextureLoadingUtilsVk.h"
"vulkan/ResourceHeapVk.cpp"
"vulkan/ResourceHeapVk.h"
"vulkan/ResourceMemoryAllocatorVk.cpp"
diff --git a/src/dawn/native/CommandEncoder.cpp b/src/dawn/native/CommandEncoder.cpp
index e52910a..92556e4 100644
--- a/src/dawn/native/CommandEncoder.cpp
+++ b/src/dawn/native/CommandEncoder.cpp
@@ -509,6 +509,10 @@
colorAttachment.resolveTarget, wgpu::TextureUsage::TextureBinding,
wgpu::LoadOp::ExpandResolveTexture);
+ // TODO(42240662): multiplanar textures are not supported as resolve target.
+ // The RenderPassValidationState currently rejects such usage.
+ DAWN_ASSERT(!colorAttachment.resolveTarget->GetTexture()->GetFormat().IsMultiPlanar());
+
validationState->SetWillExpandResolveTexture(true);
return {};
@@ -930,9 +934,11 @@
MaybeError ApplyExpandResolveTextureLoadOp(DeviceBase* device,
RenderPassEncoder* renderPassEncoder,
const RenderPassDescriptor* renderPassDescriptor) {
- // TODO(dawn:1710): support loading resolve texture on platforms that don't support reading
- // it in fragment shader such as vulkan.
- DAWN_ASSERT(device->IsResolveTextureBlitWithDrawSupported());
+ // If backend doesn't support textureLoad on resolve targets, then it should handle the load op
+ // internally.
+ if (!device->CanTextureLoadResolveTargetInTheSameRenderpass()) {
+ return {};
+ }
// Read implicit resolve texture in fragment shader and copy to the implicit MSAA attachment.
return ExpandResolveTextureWithDraw(device, renderPassEncoder, renderPassDescriptor);
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index dcbd086..ad32632 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -261,14 +261,16 @@
*outDescriptor = descriptor;
if (outDescriptor->layout == nullptr) {
- DAWN_TRY_ASSIGN(layoutRef, PipelineLayoutBase::CreateDefault(
- device, {{
- SingleShaderStage::Compute,
- outDescriptor->compute.module,
- outDescriptor->compute.entryPoint,
- outDescriptor->compute.constantCount,
- outDescriptor->compute.constants,
- }}));
+ DAWN_TRY_ASSIGN(layoutRef,
+ PipelineLayoutBase::CreateDefault(device,
+ {{
+ SingleShaderStage::Compute,
+ outDescriptor->compute.module,
+ outDescriptor->compute.entryPoint,
+ outDescriptor->compute.constantCount,
+ outDescriptor->compute.constants,
+ }},
+ /*allowInternalBinding=*/false));
outDescriptor->layout = layoutRef.Get();
}
@@ -278,7 +280,8 @@
ResultOrError<Ref<PipelineLayoutBase>> ValidateLayoutAndGetRenderPipelineDescriptorWithDefaults(
DeviceBase* device,
const RenderPipelineDescriptor& descriptor,
- RenderPipelineDescriptor* outDescriptor) {
+ RenderPipelineDescriptor* outDescriptor,
+ bool allowInternalBinding) {
Ref<PipelineLayoutBase> layoutRef;
*outDescriptor = descriptor;
@@ -287,7 +290,8 @@
// the pipeline will take another reference.
DAWN_TRY_ASSIGN(layoutRef,
PipelineLayoutBase::CreateDefault(
- device, GetRenderStagesAndSetPlaceholderShader(device, &descriptor)));
+ device, GetRenderStagesAndSetPlaceholderShader(device, &descriptor),
+ allowInternalBinding));
outDescriptor->layout = layoutRef.Get();
}
@@ -1825,6 +1829,10 @@
return mWGSLAllowedFeatures;
}
+void DeviceBase::EnableAdditionalWGSLExtension(tint::wgsl::Extension extension) {
+ mWGSLAllowedFeatures.extensions.insert(extension);
+}
+
bool DeviceBase::IsValidationEnabled() const {
return !IsToggleEnabled(Toggle::SkipValidation);
}
@@ -2162,12 +2170,14 @@
}
ResultOrError<Ref<RenderPipelineBase>> DeviceBase::CreateRenderPipeline(
- const RenderPipelineDescriptor* descriptor) {
+ const RenderPipelineDescriptor* descriptor,
+ bool allowInternalBinding) {
// If a pipeline layout is not specified, we cannot use cached pipelines.
bool useCache = descriptor->layout != nullptr;
Ref<RenderPipelineBase> uninitializedRenderPipeline;
- DAWN_TRY_ASSIGN(uninitializedRenderPipeline, CreateUninitializedRenderPipeline(descriptor));
+ DAWN_TRY_ASSIGN(uninitializedRenderPipeline,
+ CreateUninitializedRenderPipeline(descriptor, allowInternalBinding));
if (useCache) {
Ref<RenderPipelineBase> cachedRenderPipeline =
@@ -2190,7 +2200,8 @@
}
ResultOrError<Ref<RenderPipelineBase>> DeviceBase::CreateUninitializedRenderPipeline(
- const RenderPipelineDescriptor* descriptor) {
+ const RenderPipelineDescriptor* descriptor,
+ bool allowInternalBinding) {
DAWN_TRY(ValidateIsAlive());
if (IsValidationEnabled()) {
DAWN_TRY(ValidateRenderPipelineDescriptor(this, descriptor));
@@ -2205,7 +2216,7 @@
Ref<PipelineLayoutBase> layoutRef;
RenderPipelineDescriptor appliedDescriptor;
DAWN_TRY_ASSIGN(layoutRef, ValidateLayoutAndGetRenderPipelineDescriptorWithDefaults(
- this, *descriptor, &appliedDescriptor));
+ this, *descriptor, &appliedDescriptor, allowInternalBinding));
return CreateUninitializedRenderPipelineImpl(Unpack(&appliedDescriptor));
}
@@ -2462,7 +2473,7 @@
return false;
}
-bool DeviceBase::IsResolveTextureBlitWithDrawSupported() const {
+bool DeviceBase::CanTextureLoadResolveTargetInTheSameRenderpass() const {
return false;
}
diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h
index 6fc5dec..2d395a4 100644
--- a/src/dawn/native/Device.h
+++ b/src/dawn/native/Device.h
@@ -220,9 +220,11 @@
ResultOrError<Ref<RenderBundleEncoder>> CreateRenderBundleEncoder(
const RenderBundleEncoderDescriptor* descriptor);
ResultOrError<Ref<RenderPipelineBase>> CreateRenderPipeline(
- const RenderPipelineDescriptor* descriptor);
+ const RenderPipelineDescriptor* descriptor,
+ bool allowInternalBinding = false);
ResultOrError<Ref<RenderPipelineBase>> CreateUninitializedRenderPipeline(
- const RenderPipelineDescriptor* descriptor);
+ const RenderPipelineDescriptor* descriptor,
+ bool allowInternalBinding = false);
ResultOrError<Ref<SamplerBase>> CreateSampler(const SamplerDescriptor* descriptor = nullptr);
ResultOrError<Ref<ShaderModuleBase>> CreateShaderModule(
const ShaderModuleDescriptor* descriptor,
@@ -398,9 +400,9 @@
// See https://crbug.com/dawn/161
virtual bool ShouldApplyIndexBufferOffsetToFirstIndex() const;
- // Whether the backend supports blitting the resolve texture with draw calls in the same render
- // pass that it will be resolved into.
- virtual bool IsResolveTextureBlitWithDrawSupported() const;
+ // Whether the backend can use textureLoad() on a resolve target in the same render pass that it
+ // will be resolved into.
+ virtual bool CanTextureLoadResolveTargetInTheSameRenderpass() const;
bool HasFeature(Feature feature) const;
@@ -467,6 +469,8 @@
void DestroyObjects();
void Destroy();
+ void EnableAdditionalWGSLExtension(tint::wgsl::Extension extension);
+
virtual MaybeError GetAHardwareBufferPropertiesImpl(
void* handle,
AHardwareBufferProperties* properties) const {
@@ -630,7 +634,8 @@
ResultOrError<Ref<PipelineLayoutBase>> ValidateLayoutAndGetRenderPipelineDescriptorWithDefaults(
DeviceBase* device,
const RenderPipelineDescriptor& descriptor,
- RenderPipelineDescriptor* outDescriptor);
+ RenderPipelineDescriptor* outDescriptor,
+ bool allowInternalBinding = false);
class IgnoreLazyClearCountScope : public NonMovable, public StackAllocated {
public:
diff --git a/src/dawn/native/PipelineLayout.cpp b/src/dawn/native/PipelineLayout.cpp
index 0f45480..64e8816 100644
--- a/src/dawn/native/PipelineLayout.cpp
+++ b/src/dawn/native/PipelineLayout.cpp
@@ -161,7 +161,8 @@
// static
ResultOrError<Ref<PipelineLayoutBase>> PipelineLayoutBase::CreateDefault(
DeviceBase* device,
- std::vector<StageAndDescriptor> stages) {
+ std::vector<StageAndDescriptor> stages,
+ bool allowInternalBinding) {
using EntryMap = std::map<BindingNumber, BindGroupLayoutEntry>;
// Merges two entries at the same location, if they are allowed to be merged.
@@ -271,8 +272,8 @@
// Creates the BGL from the entries for a stage, checking it is valid.
auto CreateBGL = [](DeviceBase* device, const EntryMap& entries,
- PipelineCompatibilityToken pipelineCompatibilityToken)
- -> ResultOrError<Ref<BindGroupLayoutBase>> {
+ PipelineCompatibilityToken pipelineCompatibilityToken,
+ bool allowInternalBinding) -> ResultOrError<Ref<BindGroupLayoutBase>> {
std::vector<BindGroupLayoutEntry> entryVec;
entryVec.reserve(entries.size());
for (auto& [_, entry] : entries) {
@@ -284,8 +285,8 @@
desc.entryCount = entryVec.size();
if (device->IsValidationEnabled()) {
- DAWN_TRY_CONTEXT(ValidateBindGroupLayoutDescriptor(device, &desc), "validating %s",
- &desc);
+ DAWN_TRY_CONTEXT(ValidateBindGroupLayoutDescriptor(device, &desc, allowInternalBinding),
+ "validating %s", &desc);
}
return device->GetOrCreateBindGroupLayout(&desc, pipelineCompatibilityToken);
};
@@ -350,8 +351,9 @@
BindGroupIndex pipelineBGLCount = BindGroupIndex(0);
PerBindGroup<Ref<BindGroupLayoutBase>> bindGroupLayouts = {};
for (auto group : Range(kMaxBindGroupsTyped)) {
- DAWN_TRY_ASSIGN(bindGroupLayouts[group],
- CreateBGL(device, entryData[group], pipelineCompatibilityToken));
+ DAWN_TRY_ASSIGN(
+ bindGroupLayouts[group],
+ CreateBGL(device, entryData[group], pipelineCompatibilityToken, allowInternalBinding));
if (entryData[group].size() != 0) {
pipelineBGLCount = ityp::PlusOne(group);
}
diff --git a/src/dawn/native/PipelineLayout.h b/src/dawn/native/PipelineLayout.h
index b8a66c1..a288304 100644
--- a/src/dawn/native/PipelineLayout.h
+++ b/src/dawn/native/PipelineLayout.h
@@ -83,7 +83,8 @@
static Ref<PipelineLayoutBase> MakeError(DeviceBase* device, const char* label);
static ResultOrError<Ref<PipelineLayoutBase>> CreateDefault(
DeviceBase* device,
- std::vector<StageAndDescriptor> stages);
+ std::vector<StageAndDescriptor> stages,
+ bool allowInternalBinding);
ObjectType GetType() const override;
diff --git a/src/dawn/native/d3d11/DeviceD3D11.cpp b/src/dawn/native/d3d11/DeviceD3D11.cpp
index 9747f21..2430bb6 100644
--- a/src/dawn/native/d3d11/DeviceD3D11.cpp
+++ b/src/dawn/native/d3d11/DeviceD3D11.cpp
@@ -408,7 +408,7 @@
return DeviceBase::GetBufferCopyOffsetAlignmentForDepthStencil();
}
-bool Device::IsResolveTextureBlitWithDrawSupported() const {
+bool Device::CanTextureLoadResolveTargetInTheSameRenderpass() const {
return true;
}
diff --git a/src/dawn/native/d3d11/DeviceD3D11.h b/src/dawn/native/d3d11/DeviceD3D11.h
index c13f12b..332dcdb 100644
--- a/src/dawn/native/d3d11/DeviceD3D11.h
+++ b/src/dawn/native/d3d11/DeviceD3D11.h
@@ -75,7 +75,7 @@
float GetTimestampPeriodInNS() const override;
bool MayRequireDuplicationOfIndirectParameters() const override;
uint64_t GetBufferCopyOffsetAlignmentForDepthStencil() const override;
- bool IsResolveTextureBlitWithDrawSupported() const override;
+ bool CanTextureLoadResolveTargetInTheSameRenderpass() const override;
void SetLabelImpl() override;
void DisposeKeyedMutex(ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex) override;
diff --git a/src/dawn/native/metal/DeviceMTL.h b/src/dawn/native/metal/DeviceMTL.h
index 846b276..44e8f8f 100644
--- a/src/dawn/native/metal/DeviceMTL.h
+++ b/src/dawn/native/metal/DeviceMTL.h
@@ -78,7 +78,7 @@
float GetTimestampPeriodInNS() const override;
- bool IsResolveTextureBlitWithDrawSupported() const override;
+ bool CanTextureLoadResolveTargetInTheSameRenderpass() const override;
bool UseCounterSamplingAtCommandBoundary() const;
bool UseCounterSamplingAtStageBoundary() const;
diff --git a/src/dawn/native/metal/DeviceMTL.mm b/src/dawn/native/metal/DeviceMTL.mm
index cb3de76..2a58e4e 100644
--- a/src/dawn/native/metal/DeviceMTL.mm
+++ b/src/dawn/native/metal/DeviceMTL.mm
@@ -393,7 +393,7 @@
return mTimestampPeriod;
}
-bool Device::IsResolveTextureBlitWithDrawSupported() const {
+bool Device::CanTextureLoadResolveTargetInTheSameRenderpass() const {
return true;
}
diff --git a/src/dawn/native/null/DeviceNull.cpp b/src/dawn/native/null/DeviceNull.cpp
index c0720268..83030b5 100644
--- a/src/dawn/native/null/DeviceNull.cpp
+++ b/src/dawn/native/null/DeviceNull.cpp
@@ -574,7 +574,7 @@
return 1.0f;
}
-bool Device::IsResolveTextureBlitWithDrawSupported() const {
+bool Device::CanTextureLoadResolveTargetInTheSameRenderpass() const {
return true;
}
diff --git a/src/dawn/native/null/DeviceNull.h b/src/dawn/native/null/DeviceNull.h
index c47de81..a9190ca 100644
--- a/src/dawn/native/null/DeviceNull.h
+++ b/src/dawn/native/null/DeviceNull.h
@@ -138,7 +138,7 @@
float GetTimestampPeriodInNS() const override;
- bool IsResolveTextureBlitWithDrawSupported() const override;
+ bool CanTextureLoadResolveTargetInTheSameRenderpass() const override;
private:
using DeviceBase::DeviceBase;
diff --git a/src/dawn/native/vulkan/CommandBufferVk.cpp b/src/dawn/native/vulkan/CommandBufferVk.cpp
index a3ad777..9f7dd8b 100644
--- a/src/dawn/native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn/native/vulkan/CommandBufferVk.cpp
@@ -49,6 +49,7 @@
#include "dawn/native/vulkan/QueueVk.h"
#include "dawn/native/vulkan/RenderPassCache.h"
#include "dawn/native/vulkan/RenderPipelineVk.h"
+#include "dawn/native/vulkan/ResolveTextureLoadingUtilsVk.h"
#include "dawn/native/vulkan/TextureVk.h"
#include "dawn/native/vulkan/UtilsVulkan.h"
#include "dawn/native/vulkan/VulkanError.h"
@@ -389,7 +390,9 @@
query.SetSampleCount(renderPass->attachmentState->GetSampleCount());
- DAWN_TRY_ASSIGN(renderPassVK, device->GetRenderPassCache()->GetRenderPass(query));
+ RenderPassCache::RenderPassInfo renderPassInfo;
+ DAWN_TRY_ASSIGN(renderPassInfo, device->GetRenderPassCache()->GetRenderPass(query));
+ renderPassVK = renderPassInfo.renderPass;
}
// Create a framebuffer that will be used once for the render pass and gather the clear
@@ -501,7 +504,12 @@
beginInfo.clearValueCount = attachmentCount;
beginInfo.pClearValues = clearValues.data();
- device->fn.CmdBeginRenderPass(commands, &beginInfo, VK_SUBPASS_CONTENTS_INLINE);
+ if (renderPass->attachmentState->GetExpandResolveInfo().attachmentsToExpandResolve.any()) {
+ DAWN_TRY(BeginRenderPassAndExpandResolveTextureWithDraw(device, recordingContext,
+ renderPass, beginInfo));
+ } else {
+ device->fn.CmdBeginRenderPass(commands, &beginInfo, VK_SUBPASS_CONTENTS_INLINE);
+ }
return {};
}
diff --git a/src/dawn/native/vulkan/DeviceVk.cpp b/src/dawn/native/vulkan/DeviceVk.cpp
index e197dfa..55879ea 100644
--- a/src/dawn/native/vulkan/DeviceVk.cpp
+++ b/src/dawn/native/vulkan/DeviceVk.cpp
@@ -168,7 +168,15 @@
Ref<Queue> queue;
DAWN_TRY_ASSIGN(queue, Queue::Create(this, &descriptor->defaultQueue, mMainQueueFamily));
- return DeviceBase::Initialize(std::move(queue));
+ DAWN_TRY(DeviceBase::Initialize(std::move(queue)));
+
+ if (HasFeature(Feature::DawnLoadResolveTexture)) {
+ // TODO(42240662): Add a way to add additional extensions when compiling specific shader
+ // modules only.
+ EnableAdditionalWGSLExtension(tint::wgsl::Extension::kChromiumInternalInputAttachments);
+ }
+
+ return {};
}
Device::~Device() {
@@ -673,12 +681,13 @@
DAWN_INVALID_IF(!mExternalSemaphoreService->Supported(),
"External semaphore usage not supported");
- DAWN_INVALID_IF(!mExternalMemoryService->SupportsImportMemory(
- descriptor->GetType(), VulkanImageFormat(this, textureDescriptor->format),
- VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
- VulkanImageUsage(usage, GetValidInternalFormat(textureDescriptor->format)),
- VK_IMAGE_CREATE_ALIAS_BIT_KHR),
- "External memory usage not supported");
+ DAWN_INVALID_IF(
+ !mExternalMemoryService->SupportsImportMemory(
+ descriptor->GetType(), VulkanImageFormat(this, textureDescriptor->format),
+ VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
+ VulkanImageUsage(this, usage, GetValidInternalFormat(textureDescriptor->format)),
+ VK_IMAGE_CREATE_ALIAS_BIT_KHR),
+ "External memory usage not supported");
// Import the external image's memory
external_memory::MemoryImportParams importParams;
diff --git a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
index c77c31b..6075a4c 100644
--- a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
+++ b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
@@ -369,6 +369,7 @@
EnableFeature(Feature::SurfaceCapabilities);
EnableFeature(Feature::TransientAttachments);
EnableFeature(Feature::AdapterPropertiesVk);
+ EnableFeature(Feature::DawnLoadResolveTexture);
// Enable ChromiumExperimentalSubgroups feature if:
// 1. Vulkan API version is 1.1 or later, and
diff --git a/src/dawn/native/vulkan/RenderPassCache.cpp b/src/dawn/native/vulkan/RenderPassCache.cpp
index ced4ffa..f38e469 100644
--- a/src/dawn/native/vulkan/RenderPassCache.cpp
+++ b/src/dawn/native/vulkan/RenderPassCache.cpp
@@ -27,6 +27,7 @@
#include "dawn/native/vulkan/RenderPassCache.h"
+#include "absl/container/inlined_vector.h"
#include "dawn/common/BitSetIterator.h"
#include "dawn/common/Enumerator.h"
#include "dawn/common/HashUtils.h"
@@ -45,9 +46,7 @@
case wgpu::LoadOp::Clear:
return VK_ATTACHMENT_LOAD_OP_CLEAR;
case wgpu::LoadOp::ExpandResolveTexture:
- // TODO(dawn:1710): Implement this on vulkan.
- DAWN_UNREACHABLE();
- break;
+ return VK_ATTACHMENT_LOAD_OP_DONT_CARE;
case wgpu::LoadOp::Undefined:
DAWN_UNREACHABLE();
break;
@@ -69,6 +68,32 @@
}
DAWN_UNREACHABLE();
}
+
+void InitializeLoadResolveSubpassDependencies(
+ absl::InlinedVector<VkSubpassDependency, 2>* subpassDependenciesOut) {
+ VkSubpassDependency dependencies[2];
+ // Dependency for resolve texture's read -> resolve texture's write.
+ dependencies[0].srcSubpass = 0;
+ dependencies[0].dstSubpass = 1;
+ dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dependencies[0].srcAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
+ dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
+
+ // Dependency for color write in subpass 0 -> color write in subpass 1
+ dependencies[1].srcSubpass = 0;
+ dependencies[1].dstSubpass = 1;
+ dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dependencies[1].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ dependencies[1].dstAccessMask =
+ VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
+
+ subpassDependenciesOut->insert(subpassDependenciesOut->end(), std::begin(dependencies),
+ std::end(dependencies));
+}
} // anonymous namespace
// RenderPassCacheQuery
@@ -83,6 +108,7 @@
colorLoadOp[index] = loadOp;
colorStoreOp[index] = storeOp;
resolveTargetMask[index] = hasResolveTarget;
+ expandResolveMask.set(index, loadOp == wgpu::LoadOp::ExpandResolveTexture);
}
void RenderPassCacheQuery::SetDepthStencil(wgpu::TextureFormat format,
@@ -112,27 +138,28 @@
RenderPassCache::~RenderPassCache() {
std::lock_guard<std::mutex> lock(mMutex);
- for (auto [_, renderPass] : mCache) {
- mDevice->fn.DestroyRenderPass(mDevice->GetVkDevice(), renderPass, nullptr);
+ for (auto [_, renderPassInfo] : mCache) {
+ mDevice->fn.DestroyRenderPass(mDevice->GetVkDevice(), renderPassInfo.renderPass, nullptr);
}
mCache.clear();
}
-ResultOrError<VkRenderPass> RenderPassCache::GetRenderPass(const RenderPassCacheQuery& query) {
+ResultOrError<RenderPassCache::RenderPassInfo> RenderPassCache::GetRenderPass(
+ const RenderPassCacheQuery& query) {
std::lock_guard<std::mutex> lock(mMutex);
auto it = mCache.find(query);
if (it != mCache.end()) {
- return VkRenderPass(it->second);
+ return RenderPassInfo(it->second);
}
- VkRenderPass renderPass;
+ RenderPassInfo renderPass;
DAWN_TRY_ASSIGN(renderPass, CreateRenderPassForQuery(query));
mCache.emplace(query, renderPass);
return renderPass;
}
-ResultOrError<VkRenderPass> RenderPassCache::CreateRenderPassForQuery(
+ResultOrError<RenderPassCache::RenderPassInfo> RenderPassCache::CreateRenderPassForQuery(
const RenderPassCacheQuery& query) const {
// The Vulkan subpasses want to know the layout of the attachments with VkAttachmentRef.
// Precompute them as they must be pointer-chained in VkSubpassDescription.
@@ -140,14 +167,17 @@
// filled with VK_ATTACHMENT_UNUSED.
PerColorAttachment<VkAttachmentReference> colorAttachmentRefs;
PerColorAttachment<VkAttachmentReference> resolveAttachmentRefs;
+ PerColorAttachment<VkAttachmentReference> inputAttachmentRefs;
VkAttachmentReference depthStencilAttachmentRef;
for (auto i : Range(kMaxColorAttachmentsTyped)) {
colorAttachmentRefs[i].attachment = VK_ATTACHMENT_UNUSED;
resolveAttachmentRefs[i].attachment = VK_ATTACHMENT_UNUSED;
+ inputAttachmentRefs[i].attachment = VK_ATTACHMENT_UNUSED;
// The Khronos Vulkan validation layer will complain if not set
colorAttachmentRefs[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
resolveAttachmentRefs[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ inputAttachmentRefs[i].layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
// Contains the attachment description that will be chained in the create info
@@ -208,27 +238,58 @@
}
uint32_t resolveAttachmentCount = 0;
+ ColorAttachmentIndex highestInputAttachmentIndex(static_cast<uint8_t>(0));
+
for (auto i : IterateBitSet(query.resolveTargetMask)) {
- auto& attachmentRef = resolveAttachmentRefs[i];
- auto& attachmentDesc = attachmentDescs[attachmentCount];
+ auto& resolveAttachmentRef = resolveAttachmentRefs[i];
+ auto& resolveAttachmentDesc = attachmentDescs[attachmentCount];
- attachmentRef.attachment = attachmentCount;
- attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ resolveAttachmentRef.attachment = attachmentCount;
+ resolveAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
- attachmentDesc.flags = 0;
- attachmentDesc.format = VulkanImageFormat(mDevice, query.colorFormats[i]);
- attachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT;
- attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
- attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
- attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
- attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ resolveAttachmentDesc.flags = 0;
+ resolveAttachmentDesc.format = VulkanImageFormat(mDevice, query.colorFormats[i]);
+ resolveAttachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT;
+ resolveAttachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ if (query.expandResolveMask.test(i)) {
+ resolveAttachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+ resolveAttachmentDesc.initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+ inputAttachmentRefs[i].attachment = resolveAttachmentRefs[i].attachment;
+
+ highestInputAttachmentIndex = i;
+ } else {
+ resolveAttachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ resolveAttachmentDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ }
+
+ resolveAttachmentDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachmentCount++;
resolveAttachmentCount++;
}
+ absl::InlinedVector<VkSubpassDescription, 2> subpassDescs;
+ absl::InlinedVector<VkSubpassDependency, 2> subpassDependencies;
+ if (query.expandResolveMask.any()) {
+ // To simulate ExpandResolveTexture, we use two subpasses. The first subpass will read the
+ // resolve texture as input attachment.
+ subpassDescs.push_back({});
+ VkSubpassDescription& subpassDesc = subpassDescs.back();
+ subpassDesc.flags = 0;
+ subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpassDesc.inputAttachmentCount = static_cast<uint8_t>(highestInputAttachmentIndex) + 1;
+ subpassDesc.pInputAttachments = inputAttachmentRefs.data();
+ subpassDesc.colorAttachmentCount = static_cast<uint8_t>(highestColorAttachmentIndexPlusOne);
+ subpassDesc.pColorAttachments = colorAttachmentRefs.data();
+ subpassDesc.pDepthStencilAttachment = depthStencilAttachment;
+
+ InitializeLoadResolveSubpassDependencies(&subpassDependencies);
+ }
+
// Create the VkSubpassDescription that will be chained in the VkRenderPassCreateInfo
- VkSubpassDescription subpassDesc;
+ subpassDescs.push_back({});
+ VkSubpassDescription& subpassDesc = subpassDescs.back();
subpassDesc.flags = 0;
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDesc.inputAttachmentCount = 0;
@@ -255,17 +316,18 @@
createInfo.flags = 0;
createInfo.attachmentCount = attachmentCount;
createInfo.pAttachments = attachmentDescs.data();
- createInfo.subpassCount = 1;
- createInfo.pSubpasses = &subpassDesc;
- createInfo.dependencyCount = 0;
- createInfo.pDependencies = nullptr;
+ createInfo.subpassCount = subpassDescs.size();
+ createInfo.pSubpasses = subpassDescs.data();
+ createInfo.dependencyCount = subpassDependencies.size();
+ createInfo.pDependencies = subpassDependencies.data();
// Create the render pass from the zillion parameters
- VkRenderPass renderPass;
- DAWN_TRY(CheckVkSuccess(
- mDevice->fn.CreateRenderPass(mDevice->GetVkDevice(), &createInfo, nullptr, &*renderPass),
- "CreateRenderPass"));
- return renderPass;
+ RenderPassInfo renderPassInfo;
+ renderPassInfo.mainSubpass = subpassDescs.size() - 1;
+ DAWN_TRY(CheckVkSuccess(mDevice->fn.CreateRenderPass(mDevice->GetVkDevice(), &createInfo,
+ nullptr, &*renderPassInfo.renderPass),
+ "CreateRenderPass"));
+ return renderPassInfo;
}
// RenderPassCache
@@ -280,6 +342,7 @@
for (auto i : IterateBitSet(query.colorMask)) {
HashCombine(&hash, query.colorFormats[i], query.colorLoadOp[i], query.colorStoreOp[i]);
}
+ HashCombine(&hash, query.expandResolveMask);
HashCombine(&hash, query.hasDepthStencil);
if (query.hasDepthStencil) {
@@ -314,6 +377,10 @@
}
}
+ if (a.expandResolveMask != b.expandResolveMask) {
+ return false;
+ }
+
if (a.hasDepthStencil != b.hasDepthStencil) {
return false;
}
diff --git a/src/dawn/native/vulkan/RenderPassCache.h b/src/dawn/native/vulkan/RenderPassCache.h
index 5ffa24d..29470aa 100644
--- a/src/dawn/native/vulkan/RenderPassCache.h
+++ b/src/dawn/native/vulkan/RenderPassCache.h
@@ -71,6 +71,7 @@
PerColorAttachment<wgpu::TextureFormat> colorFormats;
PerColorAttachment<wgpu::LoadOp> colorLoadOp;
PerColorAttachment<wgpu::StoreOp> colorStoreOp;
+ ColorAttachmentMask expandResolveMask;
bool hasDepthStencil = false;
wgpu::TextureFormat depthStencilFormat;
@@ -95,11 +96,16 @@
explicit RenderPassCache(Device* device);
~RenderPassCache();
- ResultOrError<VkRenderPass> GetRenderPass(const RenderPassCacheQuery& query);
+ struct RenderPassInfo {
+ VkRenderPass renderPass = VK_NULL_HANDLE;
+ uint32_t mainSubpass = 0;
+ };
+
+ ResultOrError<RenderPassInfo> GetRenderPass(const RenderPassCacheQuery& query);
private:
// Does the actual VkRenderPass creation on a cache miss.
- ResultOrError<VkRenderPass> CreateRenderPassForQuery(const RenderPassCacheQuery& query) const;
+ ResultOrError<RenderPassInfo> CreateRenderPassForQuery(const RenderPassCacheQuery& query) const;
// Implements the functors necessary for to use RenderPassCacheQueries as absl::flat_hash_map
// keys.
@@ -107,7 +113,7 @@
size_t operator()(const RenderPassCacheQuery& query) const;
bool operator()(const RenderPassCacheQuery& a, const RenderPassCacheQuery& b) const;
};
- using Cache = absl::flat_hash_map<RenderPassCacheQuery, VkRenderPass, CacheFuncs, CacheFuncs>;
+ using Cache = absl::flat_hash_map<RenderPassCacheQuery, RenderPassInfo, CacheFuncs, CacheFuncs>;
raw_ptr<Device> mDevice = nullptr;
diff --git a/src/dawn/native/vulkan/RenderPipelineVk.cpp b/src/dawn/native/vulkan/RenderPipelineVk.cpp
index b8a7441..b88fbd8 100644
--- a/src/dawn/native/vulkan/RenderPipelineVk.cpp
+++ b/src/dawn/native/vulkan/RenderPipelineVk.cpp
@@ -349,6 +349,7 @@
->GetHandleAndSpirv(stage, programmableStage, layout,
clampFragDepth, emitPointSize,
/* fullSubgroups */ {}));
+ mHasInputAttachment = mHasInputAttachment || moduleAndSpirv.hasInputAttachment;
// Record cache key for each shader since it will become inaccessible later on.
StreamIn(&mCacheKey, stream::Iterable(moduleAndSpirv.spirv, moduleAndSpirv.wordCount));
@@ -500,17 +501,31 @@
dynamic.dynamicStateCount = sizeof(dynamicStates) / sizeof(dynamicStates[0]);
dynamic.pDynamicStates = dynamicStates;
- // Get a VkRenderPass that matches the attachment formats for this pipeline, load/store ops
- // don't matter so set them all to LoadOp::Load / StoreOp::Store. Whether the render pass
- // has resolve target and whether depth/stencil attachment is read-only also don't matter,
- // so set them both to false.
- VkRenderPass renderPass = VK_NULL_HANDLE;
+ // Get a VkRenderPass that matches the attachment formats for this pipeline.
+ // VkRenderPass compatibility rules let us provide placeholder data for a bunch of arguments.
+ // Load and store ops are all equivalent, though we still specify ExpandResolveTexture as that
+ // controls the use of input attachments. Single subpass VkRenderPasses are compatible
+ // irrespective of resolve attachments being used, but for ExpandResolveTexture that uses two
+ // subpasses we need to specify which attachments will be resolved.
+ RenderPassCache::RenderPassInfo renderPassInfo;
{
RenderPassCacheQuery query;
+ ColorAttachmentMask resolveMask =
+ GetAttachmentState()->GetExpandResolveInfo().resolveTargetsMask;
+ ColorAttachmentMask expandResolveMask =
+ GetAttachmentState()->GetExpandResolveInfo().attachmentsToExpandResolve;
for (auto i : IterateBitSet(GetColorAttachmentsMask())) {
- query.SetColor(i, GetColorAttachmentFormat(i), wgpu::LoadOp::Load, wgpu::StoreOp::Store,
- false);
+ wgpu::LoadOp colorLoadOp = wgpu::LoadOp::Load;
+ bool hasResolveTarget = resolveMask.test(i);
+
+ if (expandResolveMask.test(i)) {
+ // ExpandResolveTexture will use 2 subpasses in a render pass so we have to create
+ // an appropriate query.
+ colorLoadOp = wgpu::LoadOp::ExpandResolveTexture;
+ }
+ query.SetColor(i, GetColorAttachmentFormat(i), colorLoadOp, wgpu::StoreOp::Store,
+ hasResolveTarget);
}
if (HasDepthStencilAttachment()) {
@@ -521,7 +536,7 @@
query.SetSampleCount(GetSampleCount());
StreamIn(&mCacheKey, query);
- DAWN_TRY_ASSIGN(renderPass, device->GetRenderPassCache()->GetRenderPass(query));
+ DAWN_TRY_ASSIGN(renderPassInfo, device->GetRenderPassCache()->GetRenderPass(query));
}
// The create info chains in a bunch of things created on the stack here or inside state
@@ -543,11 +558,18 @@
(GetStageMask() & wgpu::ShaderStage::Fragment) ? &colorBlend : nullptr;
createInfo.pDynamicState = &dynamic;
createInfo.layout = ToBackend(GetLayout())->GetHandle();
- createInfo.renderPass = renderPass;
- createInfo.subpass = 0;
+ createInfo.renderPass = renderPassInfo.renderPass;
createInfo.basePipelineHandle = VkPipeline{};
createInfo.basePipelineIndex = -1;
+ // - If the pipeline uses input attachments in shader, currently this is only used by
+ // ExpandResolveTexture subpass, hence we need to set the subpass to 0.
+ // - Otherwise, the pipeline will operate on the main subpass.
+ // - TODO(42240662): Add explicit way to specify subpass instead of implicitly deducing based on
+ // mHasInputAttachment.
+ // That also means mHasInputAttachment would be removed in future.
+ createInfo.subpass = mHasInputAttachment ? 0 : renderPassInfo.mainSubpass;
+
// Record cache key information now since createInfo is not stored.
StreamIn(&mCacheKey, createInfo, layout->GetCacheKey());
diff --git a/src/dawn/native/vulkan/RenderPipelineVk.h b/src/dawn/native/vulkan/RenderPipelineVk.h
index 003aec3..71fb8ff 100644
--- a/src/dawn/native/vulkan/RenderPipelineVk.h
+++ b/src/dawn/native/vulkan/RenderPipelineVk.h
@@ -64,6 +64,9 @@
VkPipelineDepthStencilStateCreateInfo ComputeDepthStencilDesc();
VkPipeline mHandle = VK_NULL_HANDLE;
+
+ // Whether the pipeline has any input attachment being used in the frag shader.
+ bool mHasInputAttachment = false;
};
} // namespace dawn::native::vulkan
diff --git a/src/dawn/native/vulkan/ResolveTextureLoadingUtilsVk.cpp b/src/dawn/native/vulkan/ResolveTextureLoadingUtilsVk.cpp
new file mode 100644
index 0000000..3f00cad
--- /dev/null
+++ b/src/dawn/native/vulkan/ResolveTextureLoadingUtilsVk.cpp
@@ -0,0 +1,302 @@
+// Copyright 2024 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 "dawn/native/vulkan/ResolveTextureLoadingUtilsVk.h"
+
+#include <sstream>
+#include <string>
+#include <utility>
+
+#include "absl/container/inlined_vector.h"
+#include "dawn/common/Assert.h"
+#include "dawn/common/Enumerator.h"
+#include "dawn/native/BindGroup.h"
+#include "dawn/native/Commands.h"
+#include "dawn/native/Device.h"
+#include "dawn/native/InternalPipelineStore.h"
+#include "dawn/native/utils/WGPUHelpers.h"
+#include "dawn/native/vulkan/BindGroupLayoutVk.h"
+#include "dawn/native/vulkan/BindGroupVk.h"
+#include "dawn/native/vulkan/DeviceVk.h"
+#include "dawn/native/vulkan/PipelineLayoutVk.h"
+#include "dawn/native/vulkan/RenderPipelineVk.h"
+#include "dawn/native/vulkan/TextureVk.h"
+#include "dawn/native/vulkan/UtilsVulkan.h"
+#include "dawn/native/vulkan/VulkanError.h"
+#include "dawn/native/webgpu_absl_format.h"
+
+namespace dawn::native::vulkan {
+
+namespace {
+
+constexpr char kBlitToColorVS[] = R"(
+
+@vertex fn vert_fullscreen_quad(
+ @builtin(vertex_index) vertex_index : u32,
+) -> @builtin(position) vec4f {
+ const pos = array(
+ vec2f(-1.0, -1.0),
+ vec2f( 3.0, -1.0),
+ vec2f(-1.0, 3.0));
+ return vec4f(pos[vertex_index], 0.0, 1.0);
+}
+)";
+
+std::string GenerateFS(const BlitColorToColorWithDrawPipelineKey& pipelineKey) {
+ std::ostringstream outputStructStream;
+ std::ostringstream assignOutputsStream;
+ std::ostringstream finalStream;
+
+ finalStream << "enable chromium_internal_input_attachments;";
+
+ for (auto i : IterateBitSet(pipelineKey.attachmentsToExpandResolve)) {
+ finalStream << absl::StrFormat(
+ "@group(0) @binding(%u) @input_attachment_index(%u) var srcTex%u : "
+ "input_attachment<f32>;\n",
+ i, i, i);
+
+ outputStructStream << absl::StrFormat("@location(%u) output%u : vec4f,\n", i, i);
+
+ assignOutputsStream << absl::StrFormat(
+ "\toutputColor.output%u = inputAttachmentLoad(srcTex%u);\n", i, i);
+ }
+
+ finalStream << "struct OutputColor {\n" << outputStructStream.str() << "}\n\n";
+ finalStream << R"(
+@fragment fn blit_to_color() -> OutputColor {
+ var outputColor : OutputColor;
+)" << assignOutputsStream.str()
+ << R"(
+ return outputColor;
+})";
+
+ return finalStream.str();
+}
+
+ResultOrError<Ref<RenderPipelineBase>> GetOrCreateColorBlitPipeline(
+ DeviceBase* device,
+ const BlitColorToColorWithDrawPipelineKey& pipelineKey,
+ uint8_t colorAttachmentCount) {
+ InternalPipelineStore* store = device->GetInternalPipelineStore();
+ {
+ auto it = store->expandResolveTexturePipelines.find(pipelineKey);
+ if (it != store->expandResolveTexturePipelines.end()) {
+ return it->second;
+ }
+ }
+
+ // vertex shader.
+ Ref<ShaderModuleBase> vshaderModule;
+ DAWN_TRY_ASSIGN(vshaderModule, utils::CreateShaderModule(device, kBlitToColorVS));
+
+ // fragment shader's source will depend on pipeline key.
+ std::string fsCode = GenerateFS(pipelineKey);
+ Ref<ShaderModuleBase> fshaderModule;
+ DAWN_TRY_ASSIGN(fshaderModule, utils::CreateShaderModule(device, fsCode.c_str()));
+
+ FragmentState fragmentState = {};
+ fragmentState.module = fshaderModule.Get();
+
+ // Color target states.
+ PerColorAttachment<ColorTargetState> colorTargets = {};
+ PerColorAttachment<wgpu::ColorTargetStateExpandResolveTextureDawn> msaaExpandResolveStates{};
+
+ for (auto [i, target] : Enumerate(colorTargets)) {
+ target.format = pipelineKey.colorTargetFormats[i];
+ // We shouldn't change the color targets that are not involved in.
+ if (pipelineKey.resolveTargetsMask[i]) {
+ target.nextInChain = &msaaExpandResolveStates[i];
+ msaaExpandResolveStates[i].enabled = pipelineKey.attachmentsToExpandResolve[i];
+ if (msaaExpandResolveStates[i].enabled) {
+ target.writeMask = wgpu::ColorWriteMask::All;
+ } else {
+ target.writeMask = wgpu::ColorWriteMask::None;
+ }
+ } else {
+ target.writeMask = wgpu::ColorWriteMask::None;
+ }
+ }
+
+ fragmentState.targetCount = colorAttachmentCount;
+ fragmentState.targets = colorTargets.data();
+
+ RenderPipelineDescriptor renderPipelineDesc = {};
+ renderPipelineDesc.label = "blit_color_to_color";
+ renderPipelineDesc.vertex.module = vshaderModule.Get();
+ renderPipelineDesc.fragment = &fragmentState;
+
+ // Depth stencil state.
+ DepthStencilState depthStencilState = {};
+ if (pipelineKey.depthStencilFormat != wgpu::TextureFormat::Undefined) {
+ depthStencilState.format = pipelineKey.depthStencilFormat;
+
+ renderPipelineDesc.depthStencil = &depthStencilState;
+ }
+
+ // Multisample state.
+ DAWN_ASSERT(pipelineKey.sampleCount > 1);
+ renderPipelineDesc.multisample.count = pipelineKey.sampleCount;
+
+ renderPipelineDesc.layout = nullptr;
+
+ Ref<RenderPipelineBase> pipeline;
+ DAWN_TRY_ASSIGN(
+ pipeline, device->CreateRenderPipeline(&renderPipelineDesc, /*allowInternalBinding=*/true));
+
+ store->expandResolveTexturePipelines.emplace(pipelineKey, pipeline);
+ return pipeline;
+}
+
+} // namespace
+
+MaybeError BeginRenderPassAndExpandResolveTextureWithDraw(Device* device,
+ CommandRecordingContext* commandContext,
+ const BeginRenderPassCmd* renderPass,
+ const VkRenderPassBeginInfo& beginInfo) {
+ DAWN_ASSERT(device->IsLockedByCurrentThreadIfNeeded());
+
+ // Construct pipeline key
+ BlitColorToColorWithDrawPipelineKey pipelineKey;
+ ColorAttachmentIndex colorAttachmentCount =
+ GetHighestBitIndexPlusOne(renderPass->attachmentState->GetColorAttachmentsMask());
+ for (ColorAttachmentIndex colorIdx :
+ IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
+ const auto& colorAttachment = renderPass->colorAttachments[colorIdx];
+ const auto& view = colorAttachment.view;
+ DAWN_ASSERT(view != nullptr);
+ const Format& format = view->GetFormat();
+ TextureComponentType baseType = format.GetAspectInfo(Aspect::Color).baseType;
+ // Blitting integer textures are not currently supported.
+ DAWN_ASSERT(baseType == TextureComponentType::Float);
+
+ if (colorAttachment.loadOp == wgpu::LoadOp::ExpandResolveTexture) {
+ // TODO(42240662): Handle the cases where resolveTarget is altered by workarounds such
+ // as ResolveMultipleAttachmentInSeparatePasses/AlwaysResolveIntoZeroLevelAndLayer. We
+ // need to careful handle such cases because the render pass' compatibility could be
+ // affected as well.
+ DAWN_INVALID_IF(colorAttachment.resolveTarget == nullptr,
+ "resolveTarget at %d has been removed by some workarounds. %s doesn't "
+ "support this yet.",
+ colorIdx, colorAttachment.loadOp);
+ DAWN_ASSERT(colorAttachment.resolveTarget->GetLayerCount() == 1u);
+ DAWN_ASSERT(colorAttachment.resolveTarget->GetDimension() ==
+ wgpu::TextureViewDimension::e2D);
+ pipelineKey.attachmentsToExpandResolve.set(colorIdx);
+ }
+ pipelineKey.resolveTargetsMask.set(colorIdx, colorAttachment.resolveTarget != nullptr);
+
+ pipelineKey.colorTargetFormats[colorIdx] = format.format;
+ pipelineKey.sampleCount = view->GetTexture()->GetSampleCount();
+ }
+
+ DAWN_ASSERT(pipelineKey.attachmentsToExpandResolve.any());
+
+ pipelineKey.depthStencilFormat = wgpu::TextureFormat::Undefined;
+ if (renderPass->depthStencilAttachment.view != nullptr) {
+ pipelineKey.depthStencilFormat =
+ renderPass->depthStencilAttachment.view->GetFormat().format;
+ }
+
+ Ref<RenderPipelineBase> pipeline;
+ DAWN_TRY_ASSIGN(pipeline, GetOrCreateColorBlitPipeline(
+ device, pipelineKey, static_cast<uint8_t>(colorAttachmentCount)));
+
+ RenderPipeline* pipelineVk = ToBackend(pipeline.Get());
+ PipelineLayout* layoutVk = ToBackend(pipeline->GetLayout());
+ DAWN_ASSERT(layoutVk != nullptr);
+
+ // Construct bind group.
+ Ref<BindGroupLayoutBase> bgl;
+ DAWN_TRY_ASSIGN(bgl, pipelineVk->GetBindGroupLayout(0));
+
+ Ref<BindGroupBase> bindGroup;
+ absl::InlinedVector<BindGroupEntry, kMaxColorAttachments> bgEntries = {};
+
+ for (auto colorIdx : IterateBitSet(pipelineKey.attachmentsToExpandResolve)) {
+ const auto& colorAttachment = renderPass->colorAttachments[colorIdx];
+ bgEntries.push_back({});
+ auto& bgEntry = bgEntries.back();
+ bgEntry.binding = static_cast<uint8_t>(colorIdx);
+ bgEntry.textureView = colorAttachment.resolveTarget.Get();
+
+ // Transition the resolve texture
+ auto* textureVk = static_cast<Texture*>(colorAttachment.resolveTarget->GetTexture());
+ textureVk->TransitionUsageNow(commandContext, kResolveAttachmentLoadingUsage,
+ wgpu::ShaderStage::Fragment,
+ colorAttachment.resolveTarget->GetSubresourceRange());
+ }
+
+ BindGroupDescriptor bgDesc = {};
+ bgDesc.layout = bgl.Get();
+ bgDesc.entryCount = bgEntries.size();
+ bgDesc.entries = bgEntries.data();
+ DAWN_TRY_ASSIGN(bindGroup, device->CreateBindGroup(&bgDesc, UsageValidationMode::Internal));
+ BindGroup* bindGroupVk = ToBackend(bindGroup.Get());
+
+ // Start the render pass
+ VkCommandBuffer commandBuffer = commandContext->commandBuffer;
+
+ device->fn.CmdBeginRenderPass(commandBuffer, &beginInfo, VK_SUBPASS_CONTENTS_INLINE);
+
+ // Draw to perform the blit.
+ VkViewport viewport{};
+ viewport.x = 0.0f;
+ viewport.y = 0.0f;
+ viewport.width = static_cast<float>(renderPass->width);
+ viewport.height = static_cast<float>(renderPass->height);
+ viewport.minDepth = 0.0f;
+ viewport.maxDepth = 1.0f;
+ device->fn.CmdSetViewport(commandBuffer, 0, 1, &viewport);
+
+ VkRect2D scissor{};
+ scissor.offset = {0, 0};
+ scissor.extent.width = renderPass->width;
+ scissor.extent.height = renderPass->height;
+ device->fn.CmdSetScissor(commandBuffer, 0, 1, &scissor);
+
+ device->fn.CmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ *pipelineVk->GetHandle());
+ device->fn.CmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ *layoutVk->GetHandle(), 0, 1, &*bindGroupVk->GetHandle(), 0,
+ nullptr);
+ device->fn.CmdDraw(commandBuffer, 3, 1, 0, 0);
+
+ device->fn.CmdNextSubpass(commandBuffer, VK_SUBPASS_CONTENTS_INLINE);
+
+ // Subpass dependency automatically transitions the layouts of the resolve textures
+ // to RenderAttachment. So we need to notify TextureVk and don't need to use any explicit
+ // barriers.
+ for (auto colorIdx : IterateBitSet(pipelineKey.attachmentsToExpandResolve)) {
+ const auto& colorAttachment = renderPass->colorAttachments[colorIdx];
+ auto* textureVk = static_cast<Texture*>(colorAttachment.resolveTarget->GetTexture());
+ textureVk->UpdateUsage(wgpu::TextureUsage::RenderAttachment, wgpu::ShaderStage::Fragment,
+ colorAttachment.resolveTarget->GetSubresourceRange());
+ }
+
+ return {};
+}
+} // namespace dawn::native::vulkan
diff --git a/src/dawn/native/vulkan/ResolveTextureLoadingUtilsVk.h b/src/dawn/native/vulkan/ResolveTextureLoadingUtilsVk.h
new file mode 100644
index 0000000..39ae9c5
--- /dev/null
+++ b/src/dawn/native/vulkan/ResolveTextureLoadingUtilsVk.h
@@ -0,0 +1,53 @@
+// Copyright 2024 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.
+
+#ifndef SRC_DAWN_NATIVE_VULKAN_RESOLVETEXTURELOADINGUTILSVK_H_
+#define SRC_DAWN_NATIVE_VULKAN_RESOLVETEXTURELOADINGUTILSVK_H_
+
+#include "dawn/common/vulkan_platform.h"
+#include "dawn/native/Error.h"
+
+namespace dawn::native {
+
+struct BeginRenderPassCmd;
+
+namespace vulkan {
+
+struct CommandRecordingContext;
+class Device;
+
+// This function begins render pass then performs the ExpandResolveTexture load operation for the
+// render pass by blitting the resolve target to the MSAA attachment.
+MaybeError BeginRenderPassAndExpandResolveTextureWithDraw(Device* device,
+ CommandRecordingContext* commandContext,
+ const BeginRenderPassCmd* renderPass,
+ const VkRenderPassBeginInfo& beginInfo);
+
+} // namespace vulkan
+} // namespace dawn::native
+
+#endif // SRC_DAWN_NATIVE_VULKAN_RESOLVETEXTURELOADINGUTILSVK_H_
diff --git a/src/dawn/native/vulkan/ShaderModuleVk.cpp b/src/dawn/native/vulkan/ShaderModuleVk.cpp
index 5f004ac..1e06207 100644
--- a/src/dawn/native/vulkan/ShaderModuleVk.cpp
+++ b/src/dawn/native/vulkan/ShaderModuleVk.cpp
@@ -114,7 +114,8 @@
}
ModuleAndSpirv AddOrGet(const TransformedShaderModuleCacheKey& key,
VkShaderModule module,
- CompiledSpirv compilation) {
+ CompiledSpirv compilation,
+ bool hasInputAttachment) {
DAWN_ASSERT(module != VK_NULL_HANDLE);
std::lock_guard<std::mutex> lock(mMutex);
@@ -123,7 +124,7 @@
bool added = false;
std::tie(iter, added) = mTransformedShaderModuleCache.emplace(
key, Entry{module, std::move(compilation.spirv),
- std::move(compilation.remappedEntryPoint)});
+ std::move(compilation.remappedEntryPoint), hasInputAttachment});
DAWN_ASSERT(added);
} else {
// No need to use FencedDeleter since this shader module was just created and does
@@ -139,13 +140,12 @@
VkShaderModule vkModule;
std::vector<uint32_t> spirv;
std::string remappedEntryPoint;
+ bool hasInputAttachment;
ModuleAndSpirv AsRefs() const {
return {
- vkModule,
- spirv.data(),
- spirv.size(),
- remappedEntryPoint.c_str(),
+ vkModule, spirv.data(), spirv.size(), remappedEntryPoint.c_str(),
+ hasInputAttachment,
};
}
};
@@ -309,12 +309,15 @@
tint::spirv::writer::binding::ExternalTexture{metadata, plane0, plane1});
},
[&](const InputAttachmentBindingInfo& bindingInfo) {
- // TODO(341117913): implement input attachment binding.
- DAWN_UNREACHABLE();
+ bindings.input_attachment.emplace(
+ srcBindingPoint, tint::spirv::writer::binding::InputAttachment{
+ dstBindingPoint.group, dstBindingPoint.binding});
});
}
}
+ const bool hasInputAttachment = !bindings.input_attachment.empty();
+
std::optional<tint::ast::transform::SubstituteOverride::Config> substituteOverrideConfig;
if (!programmableStage.metadata->overrides.empty()) {
substituteOverrideConfig = BuildSubstituteOverridesTransformConfig(programmableStage);
@@ -479,8 +482,8 @@
// Set the label on `newHandle` now, and not on `moduleAndSpirv.module` later
// since `moduleAndSpirv.module` may be in use by multiple threads.
SetDebugName(ToBackend(GetDevice()), newHandle, "Dawn_ShaderModule", GetLabel());
- moduleAndSpirv =
- mTransformedShaderModuleCache->AddOrGet(cacheKey, newHandle, compilation.Acquire());
+ moduleAndSpirv = mTransformedShaderModuleCache->AddOrGet(
+ cacheKey, newHandle, compilation.Acquire(), hasInputAttachment);
}
return std::move(moduleAndSpirv);
diff --git a/src/dawn/native/vulkan/ShaderModuleVk.h b/src/dawn/native/vulkan/ShaderModuleVk.h
index 06959d1..f87c682 100644
--- a/src/dawn/native/vulkan/ShaderModuleVk.h
+++ b/src/dawn/native/vulkan/ShaderModuleVk.h
@@ -69,6 +69,7 @@
const uint32_t* spirv;
size_t wordCount;
std::string remappedEntryPoint;
+ bool hasInputAttachment;
};
static ResultOrError<Ref<ShaderModule>> Create(
diff --git a/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp b/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp
index 6560fad..7691e89 100644
--- a/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp
+++ b/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp
@@ -251,7 +251,7 @@
VkFormat vkFormat = VulkanImageFormat(device, properties.format);
// Usage flags to create the image with.
- VkImageUsageFlags vkUsageFlags = VulkanImageUsage(properties.usage, *internalFormat);
+ VkImageUsageFlags vkUsageFlags = VulkanImageUsage(device, properties.usage, *internalFormat);
// Number of memory planes in the image which will be queried from the DRM modifier.
uint32_t memoryPlaneCount;
@@ -595,7 +595,7 @@
sharedTextureMemory->APIGetProperties(&properties);
// Compute the Vulkan usage flags to create the image with.
- VkImageUsageFlags vkUsageFlags = VulkanImageUsage(properties.usage, *internalFormat);
+ VkImageUsageFlags vkUsageFlags = VulkanImageUsage(device, properties.usage, *internalFormat);
const auto& compatibleViewFormats = device->GetCompatibleViewFormats(*internalFormat);
diff --git a/src/dawn/native/vulkan/SwapChainVk.cpp b/src/dawn/native/vulkan/SwapChainVk.cpp
index 21505a5..32ec032 100644
--- a/src/dawn/native/vulkan/SwapChainVk.cpp
+++ b/src/dawn/native/vulkan/SwapChainVk.cpp
@@ -265,7 +265,7 @@
// Choose the target usage or do a blit.
VkImageUsageFlags targetUsages =
- VulkanImageUsage(GetUsage(), GetDevice()->GetValidInternalFormat(GetFormat()));
+ VulkanImageUsage(GetDevice(), GetUsage(), GetDevice()->GetValidInternalFormat(GetFormat()));
VkImageUsageFlags supportedUsages = surfaceInfo.capabilities.supportedUsageFlags;
if (!IsSubset(targetUsages, supportedUsages)) {
config.needsBlit = true;
diff --git a/src/dawn/native/vulkan/TextureVk.cpp b/src/dawn/native/vulkan/TextureVk.cpp
index ff47906..6c12824 100644
--- a/src/dawn/native/vulkan/TextureVk.cpp
+++ b/src/dawn/native/vulkan/TextureVk.cpp
@@ -154,6 +154,14 @@
flags |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
}
+ if (usage & kResolveAttachmentLoadingUsage) {
+ // - The texture will be used as input attachment in the first subpass and loaded with
+ // VK_ATTACHMENT_LOAD_OP_LOAD. This requires VK_ACCESS_COLOR_ATTACHMENT_READ_BIT access.
+ // - It will also be read as subpass input in fragment shader. This requires
+ // VK_ACCESS_INPUT_ATTACHMENT_READ_BIT.
+ flags |= VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
+ }
+
if (usage & kPresentAcquireTextureUsage) {
// The present acquire usage is only used internally by the swapchain and is never used in
// combination with other usages.
@@ -234,6 +242,14 @@
flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
}
}
+ if (usage & kResolveAttachmentLoadingUsage) {
+ // - The texture will be used as input attachment in the first subpass and loaded with
+ // VK_ATTACHMENT_LOAD_OP_LOAD. This happens at VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
+ // stage.
+ // - It will also be read as subpass input in fragment shader.
+ flags |=
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ }
if (usage & (wgpu::TextureUsage::RenderAttachment | kReadOnlyRenderAttachment)) {
if (format.HasDepthOrStencil()) {
flags |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
@@ -579,7 +595,9 @@
// Converts the Dawn usage flags to Vulkan usage flags. Also needs the format to choose
// between color and depth attachment usages.
-VkImageUsageFlags VulkanImageUsage(wgpu::TextureUsage usage, const Format& format) {
+VkImageUsageFlags VulkanImageUsage(const DeviceBase* device,
+ wgpu::TextureUsage usage,
+ const Format& format) {
VkImageUsageFlags flags = 0;
if (usage & wgpu::TextureUsage::CopySrc) {
@@ -605,6 +623,12 @@
flags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
} else {
flags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+ if (!format.IsMultiPlanar() && (usage & wgpu::TextureUsage::TextureBinding) &&
+ device->HasFeature(Feature::DawnLoadResolveTexture)) {
+ // Automatically set "input attachment" usage so that the texture would be
+ // used in ExpandResolveTexture subpass.
+ flags |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
+ }
}
}
@@ -648,6 +672,7 @@
// The layout returned here is the one that will be used at bindgroup creation time.
case wgpu::TextureUsage::TextureBinding:
+ case kResolveAttachmentLoadingUsage:
// The sampled image can be used as a readonly depth/stencil attachment at the same
// time if it is a depth/stencil renderable format, so the image layout need to be
// VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL.
@@ -839,7 +864,7 @@
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
createInfo.format = VulkanImageFormat(device, GetFormat().format);
createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
- createInfo.usage = VulkanImageUsage(GetInternalUsage(), GetFormat()) | extraUsages;
+ createInfo.usage = VulkanImageUsage(device, GetInternalUsage(), GetFormat()) | extraUsages;
createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
@@ -959,7 +984,7 @@
external_memory::Service* externalMemoryService) {
Device* device = ToBackend(GetDevice());
VkFormat format = VulkanImageFormat(device, GetFormat().format);
- VkImageUsageFlags usage = VulkanImageUsage(GetInternalUsage(), GetFormat());
+ VkImageUsageFlags usage = VulkanImageUsage(device, GetInternalUsage(), GetFormat());
[[maybe_unused]] bool supportsDisjoint;
DAWN_INVALID_IF(
@@ -1456,6 +1481,20 @@
}
}
+void Texture::UpdateUsage(wgpu::TextureUsage usage,
+ wgpu::ShaderStage shaderStages,
+ const SubresourceRange& range) {
+ std::vector<VkImageMemoryBarrier> barriers;
+
+ VkPipelineStageFlags srcStages = 0;
+ VkPipelineStageFlags dstStages = 0;
+
+ TransitionUsageAndGetResourceBarrier(usage, shaderStages, range, &barriers, &srcStages,
+ &dstStages);
+
+ // barriers are ignored.
+}
+
void Texture::TransitionUsageAndGetResourceBarrier(wgpu::TextureUsage usage,
wgpu::ShaderStage shaderStages,
const SubresourceRange& range,
@@ -1766,7 +1805,7 @@
VkImageViewUsageCreateInfo usageInfo = {};
usageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO;
- usageInfo.usage = VulkanImageUsage(usage, GetFormat());
+ usageInfo.usage = VulkanImageUsage(device, usage, GetFormat());
createInfo.pNext = &usageInfo;
VkSamplerYcbcrConversionInfo samplerYCbCrInfo = {};
diff --git a/src/dawn/native/vulkan/TextureVk.h b/src/dawn/native/vulkan/TextureVk.h
index d3409bc..eb08296 100644
--- a/src/dawn/native/vulkan/TextureVk.h
+++ b/src/dawn/native/vulkan/TextureVk.h
@@ -51,7 +51,9 @@
// properties of the Device.
VkFormat ColorVulkanImageFormat(wgpu::TextureFormat format);
ResultOrError<wgpu::TextureFormat> FormatFromVkFormat(const Device* device, VkFormat vkFormat);
-VkImageUsageFlags VulkanImageUsage(wgpu::TextureUsage usage, const Format& format);
+VkImageUsageFlags VulkanImageUsage(const DeviceBase* device,
+ wgpu::TextureUsage usage,
+ const Format& format);
VkImageLayout VulkanImageLayout(const Format& format, wgpu::TextureUsage usage);
VkImageLayout VulkanImageLayoutForDepthStencilAttachment(const Format& format,
bool depthReadOnly,
@@ -106,6 +108,13 @@
std::vector<VkImageMemoryBarrier>* imageBarriers,
VkPipelineStageFlags* srcStages,
VkPipelineStageFlags* dstStages);
+ // Change the texture to be used as `usage`. Note: this function assumes the barriers are
+ // already invoked before calling it. Typical use case is an input attachment, at the beginning
+ // of render pass, its usage is transitioned to TextureBinding. Then subpass' dependency
+ // automatically transitions the texture to RenderAttachment without any explicit barrier call.
+ void UpdateUsage(wgpu::TextureUsage usage,
+ wgpu::ShaderStage shaderStages,
+ const SubresourceRange& range);
// Eagerly transition the texture for export.
void TransitionEagerlyForExport(CommandRecordingContext* recordingContext);
diff --git a/src/dawn/tests/end2end/MultisampledRenderingTests.cpp b/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
index e72e23a..03b6734 100644
--- a/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
+++ b/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
@@ -30,6 +30,7 @@
#include <vector>
#include "dawn/common/Assert.h"
+#include "dawn/native/DawnNative.h"
#include "dawn/tests/DawnTest.h"
#include "dawn/utils/ComboRenderPipelineDescriptor.h"
#include "dawn/utils/WGPUHelpers.h"
@@ -1542,6 +1543,7 @@
}
class DawnLoadResolveTextureTest : public MultisampledRenderingTest {
+ protected:
void SetUp() override {
MultisampledRenderingTest::SetUp();
@@ -1559,6 +1561,10 @@
}
return requiredFeatures;
}
+
+ bool HasResolveMultipleAttachmentInSeparatePassesToggle() {
+ return HasToggleEnabled("resolve_multiple_attachments_in_separate_passes");
+ }
};
// Test rendering into a resolve texture then start another render pass with
@@ -1662,10 +1668,13 @@
// Test rendering into 2 attachments. The 1st attachment will use
// LoadOp::ExpandResolveTexture.
TEST_P(DawnLoadResolveTextureTest, TwoOutputsDrawThenLoadColor0) {
- auto multiSampledTexture1 = CreateTextureForRenderAttachment(
- kColorFormat, 4, 1, 1,
- /*transientAttachment=*/device.HasFeature(wgpu::FeatureName::TransientAttachments),
- /*supportsTextureBinding=*/false);
+ // TODO(42240662): "resolve_multiple_attachments_in_separate_passes" is currently not working
+ // with DawnLoadResolveTexture feature if there are more than one attachment.
+ DAWN_TEST_UNSUPPORTED_IF(HasResolveMultipleAttachmentInSeparatePassesToggle());
+
+ auto multiSampledTexture1 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1,
+ /*transientAttachment=*/false,
+ /*supportsTextureBinding=*/false);
auto multiSampledTextureView1 = multiSampledTexture1.CreateView();
auto multiSampledTexture2 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1,
@@ -1740,15 +1749,18 @@
// Test rendering into 2 attachments. The 2nd attachment will use
// LoadOp::ExpandResolveTexture.
TEST_P(DawnLoadResolveTextureTest, TwoOutputsDrawThenLoadColor1) {
+ // TODO(42240662): "resolve_multiple_attachments_in_separate_passes" is currently not working
+ // with DawnLoadResolveTexture feature if there are more than one attachment.
+ DAWN_TEST_UNSUPPORTED_IF(HasResolveMultipleAttachmentInSeparatePassesToggle());
+
auto multiSampledTexture1 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1,
/*transientAttachment=*/false,
/*supportsTextureBinding=*/false);
auto multiSampledTextureView1 = multiSampledTexture1.CreateView();
- auto multiSampledTexture2 = CreateTextureForRenderAttachment(
- kColorFormat, 4, 1, 1,
- /*transientAttachment=*/device.HasFeature(wgpu::FeatureName::TransientAttachments),
- /*supportsTextureBinding=*/false);
+ auto multiSampledTexture2 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1,
+ /*transientAttachment=*/false,
+ /*supportsTextureBinding=*/false);
auto multiSampledTextureView2 = multiSampledTexture2.CreateView();
auto singleSampledTexture1 =
@@ -1817,16 +1829,18 @@
// Test rendering into 2 attachments. The both attachments will use
// LoadOp::ExpandResolveTexture.
TEST_P(DawnLoadResolveTextureTest, TwoOutputsDrawThenLoadColor0AndColor1) {
- auto multiSampledTexture1 = CreateTextureForRenderAttachment(
- kColorFormat, 4, 1, 1,
- /*transientAttachment=*/device.HasFeature(wgpu::FeatureName::TransientAttachments),
- /*supportsTextureBinding=*/false);
+ // TODO(42240662): "resolve_multiple_attachments_in_separate_passes" is currently not working
+ // with DawnLoadResolveTexture feature if there are more than one attachment.
+ DAWN_TEST_UNSUPPORTED_IF(HasResolveMultipleAttachmentInSeparatePassesToggle());
+
+ auto multiSampledTexture1 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1,
+ /*transientAttachment=*/false,
+ /*supportsTextureBinding=*/false);
auto multiSampledTextureView1 = multiSampledTexture1.CreateView();
- auto multiSampledTexture2 = CreateTextureForRenderAttachment(
- kColorFormat, 4, 1, 1,
- /*transientAttachment=*/device.HasFeature(wgpu::FeatureName::TransientAttachments),
- /*supportsTextureBinding=*/false);
+ auto multiSampledTexture2 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1,
+ /*transientAttachment=*/false,
+ /*supportsTextureBinding=*/false);
auto multiSampledTextureView2 = multiSampledTexture2.CreateView();
auto singleSampledTexture1 =
@@ -1968,16 +1982,18 @@
// Test ExpandResolveTexture load op rendering with depth test works correctly with
// two outputs both use ExpandResolveTexture load op.
TEST_P(DawnLoadResolveTextureTest, TwoOutputsDrawWithDepthTestColor0AndColor1) {
- auto multiSampledTexture1 = CreateTextureForRenderAttachment(
- kColorFormat, 4, 1, 1,
- /*transientAttachment=*/device.HasFeature(wgpu::FeatureName::TransientAttachments),
- /*supportsTextureBinding=*/false);
+ // TODO(42240662): "resolve_multiple_attachments_in_separate_passes" is currently not working
+ // with DawnLoadResolveTexture feature if there are more than one attachment.
+ DAWN_TEST_UNSUPPORTED_IF(HasResolveMultipleAttachmentInSeparatePassesToggle());
+
+ auto multiSampledTexture1 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1,
+ /*transientAttachment=*/false,
+ /*supportsTextureBinding=*/false);
auto multiSampledTextureView1 = multiSampledTexture1.CreateView();
- auto multiSampledTexture2 = CreateTextureForRenderAttachment(
- kColorFormat, 4, 1, 1,
- /*transientAttachment=*/device.HasFeature(wgpu::FeatureName::TransientAttachments),
- /*supportsTextureBinding=*/false);
+ auto multiSampledTexture2 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1,
+ /*transientAttachment=*/false,
+ /*supportsTextureBinding=*/false);
auto multiSampledTextureView2 = multiSampledTexture2.CreateView();
auto singleSampledTexture1 =
@@ -2127,6 +2143,7 @@
OpenGLESBackend(),
VulkanBackend(),
VulkanBackend({"always_resolve_into_zero_level_and_layer"}),
+ VulkanBackend({"resolve_multiple_attachments_in_separate_passes"}),
MetalBackend({"emulate_store_and_msaa_resolve"}),
MetalBackend({"always_resolve_into_zero_level_and_layer"}),
MetalBackend({"always_resolve_into_zero_level_and_layer",
diff --git a/src/tint/lang/spirv/writer/common/option_helper.cc b/src/tint/lang/spirv/writer/common/option_helper.cc
index f209b63..e1723bf 100644
--- a/src/tint/lang/spirv/writer/common/option_helper.cc
+++ b/src/tint/lang/spirv/writer/common/option_helper.cc
@@ -106,6 +106,10 @@
diagnostics.AddNote(Source{}) << "when processing sampler";
return Failure{std::move(diagnostics)};
}
+ if (!valid(options.bindings.input_attachment)) {
+ diagnostics.AddNote(Source{}) << "when processing input_attachment";
+ return Failure{std::move(diagnostics)};
+ }
for (const auto& it : options.bindings.external_texture) {
const auto& src_binding = it.first;
@@ -201,6 +205,7 @@
create_remappings(options.bindings.texture);
create_remappings(options.bindings.storage_texture);
create_remappings(options.bindings.sampler);
+ create_remappings(options.bindings.input_attachment);
// External textures are re-bound to their plane0 location
for (const auto& it : options.bindings.external_texture) {
diff --git a/src/tint/lang/spirv/writer/common/options.h b/src/tint/lang/spirv/writer/common/options.h
index 5eedab8..616e28e 100644
--- a/src/tint/lang/spirv/writer/common/options.h
+++ b/src/tint/lang/spirv/writer/common/options.h
@@ -66,6 +66,7 @@
using Texture = BindingInfo;
using StorageTexture = BindingInfo;
using Sampler = BindingInfo;
+using InputAttachment = BindingInfo;
/// An external texture
struct ExternalTexture {
@@ -94,6 +95,8 @@
using SamplerBindings = std::unordered_map<BindingPoint, binding::Sampler>;
// Maps the WGSL binding point to the plane0, plane1, and metadata information for external textures
using ExternalTextureBindings = std::unordered_map<BindingPoint, binding::ExternalTexture>;
+// Maps the WGSL binding point to the SPIR-V group,binding for input attachments
+using InputAttachmentBindings = std::unordered_map<BindingPoint, binding::InputAttachment>;
/// Binding information
struct Bindings {
@@ -109,9 +112,18 @@
SamplerBindings sampler{};
/// External bindings
ExternalTextureBindings external_texture{};
+ /// Input attachment bindings
+ InputAttachmentBindings input_attachment{};
/// Reflect the fields of this class so that it can be used by tint::ForeachField()
- TINT_REFLECT(Bindings, uniform, storage, texture, storage_texture, sampler, external_texture);
+ TINT_REFLECT(Bindings,
+ uniform,
+ storage,
+ texture,
+ storage_texture,
+ sampler,
+ external_texture,
+ input_attachment);
};
/// Configuration options used for generating SPIR-V.