Add transient attachments and initial Metal support
This CL does the following: * Adds a "transient attachment" bit to texture usage. This bit specifies that the created texture will be used only during this render pass. * Adds a TransientAttachments Feature that gates the usage of transient attachments. * Adds support for transient attachments on Metal, where they're used to create textures as memoryless. * Adds validation tests and an E2T test of the feature. A followup CL will add support in Vulkan. Bug: dawn:1695 Change-Id: I3c7322dd1e4bee113062aae2e0494d292ee8cbc3 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/129080 Commit-Queue: Colin Blundell <blundell@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
c820dcc53e
commit
a406959e50
|
@ -1441,7 +1441,8 @@
|
|||
{"value": 1005, "name": "chromium experimental dp4a", "tags": ["dawn"]},
|
||||
{"value": 1006, "name": "timestamp query inside passes", "tags": ["dawn"]},
|
||||
{"value": 1007, "name": "implicit device synchronization", "tags": ["dawn", "native"]},
|
||||
{"value": 1008, "name": "surface capabilities", "tags": ["dawn", "native"]}
|
||||
{"value": 1008, "name": "surface capabilities", "tags": ["dawn", "native"]},
|
||||
{"value": 1009, "name": "transient attachments", "tags": ["dawn"]}
|
||||
]
|
||||
},
|
||||
"filter mode": {
|
||||
|
@ -2829,7 +2830,8 @@
|
|||
{"value": 4, "name": "texture binding"},
|
||||
{"value": 8, "name": "storage binding"},
|
||||
{"value": 16, "name": "render attachment"},
|
||||
{"value": 32, "name": "present", "tags": ["dawn"]}
|
||||
{"value": 32, "name": "present", "tags": ["dawn"]},
|
||||
{"value": 64, "name": "transient attachment", "tags": ["dawn"]}
|
||||
]
|
||||
},
|
||||
"texture view descriptor": {
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# Transient Attachments
|
||||
|
||||
The `transient-attachments` feature allows creation of attachments that allow
|
||||
render pass operations to stay in tile memory, avoiding VRAM traffic and
|
||||
potentially avoiding VRAM allocation for the textures.
|
||||
|
||||
Example Usage:
|
||||
```
|
||||
wgpu::TextureDescriptor desc;
|
||||
desc.format = wgpu::TextureFormat::RGBA8Unorm;
|
||||
desc.size = {1, 1, 1};
|
||||
desc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TransientAttachment;
|
||||
|
||||
auto transientTexture = device.CreateTexture(&desc);
|
||||
|
||||
// Can now create views from the texture to serve as transient attachments, e.g.
|
||||
// as color attachments in a render pipeline.
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Only supported usage is wgpu::TextureUsage::RenderAttachment |
|
||||
wgpu::TextureUsage::TransientAttachment
|
||||
- It is not possible to load from or store to TextureViews that are used as
|
||||
transient attachments
|
|
@ -238,6 +238,16 @@ MaybeError ValidateRenderPassColorAttachment(DeviceBase* device,
|
|||
DAWN_TRY(ValidateStoreOp(colorAttachment.storeOp));
|
||||
DAWN_INVALID_IF(colorAttachment.loadOp == wgpu::LoadOp::Undefined, "loadOp must be set.");
|
||||
DAWN_INVALID_IF(colorAttachment.storeOp == wgpu::StoreOp::Undefined, "storeOp must be set.");
|
||||
if (attachment->GetTexture()->GetUsage() & wgpu::TextureUsage::TransientAttachment) {
|
||||
DAWN_INVALID_IF(colorAttachment.loadOp != wgpu::LoadOp::Clear,
|
||||
"The color attachment %s has the load op set to %s while its usage (%s) "
|
||||
"has the transient attachment bit set.",
|
||||
attachment, wgpu::LoadOp::Load, attachment->GetTexture()->GetUsage());
|
||||
DAWN_INVALID_IF(colorAttachment.storeOp != wgpu::StoreOp::Discard,
|
||||
"The color attachment %s has the store op set to %s while its usage (%s) "
|
||||
"has the transient attachment bit set.",
|
||||
attachment, wgpu::StoreOp::Store, attachment->GetTexture()->GetUsage());
|
||||
}
|
||||
|
||||
const dawn::native::Color& clearValue = colorAttachment.clearValue;
|
||||
if (colorAttachment.loadOp == wgpu::LoadOp::Clear) {
|
||||
|
|
|
@ -107,6 +107,11 @@ static constexpr FeatureEnumAndInfoList kFeatureNameAndInfoList = {{
|
|||
"Support querying Surface's capabilities such as supported usage flags. This feature also "
|
||||
"enables swap chain to be created with usage other than RenderAttachment.",
|
||||
"https://bugs.chromium.org/p/dawn/issues/detail?id=1760", FeatureInfo::FeatureState::Stable}},
|
||||
{Feature::TransientAttachments,
|
||||
{"transient-attachments",
|
||||
"Support transient attachments that allow render pass operations to stay in tile memory, "
|
||||
"avoiding VRAM traffic and potentially avoiding VRAM allocation for the textures.",
|
||||
"https://bugs.chromium.org/p/dawn/issues/detail?id=1695", FeatureInfo::FeatureState::Stable}},
|
||||
}};
|
||||
|
||||
Feature FromAPIFeature(wgpu::FeatureName feature) {
|
||||
|
@ -153,6 +158,8 @@ Feature FromAPIFeature(wgpu::FeatureName feature) {
|
|||
return Feature::ImplicitDeviceSynchronization;
|
||||
case wgpu::FeatureName::SurfaceCapabilities:
|
||||
return Feature::SurfaceCapabilities;
|
||||
case wgpu::FeatureName::TransientAttachments:
|
||||
return Feature::TransientAttachments;
|
||||
}
|
||||
return Feature::InvalidEnum;
|
||||
}
|
||||
|
@ -195,6 +202,8 @@ wgpu::FeatureName ToAPIFeature(Feature feature) {
|
|||
return wgpu::FeatureName::ImplicitDeviceSynchronization;
|
||||
case Feature::SurfaceCapabilities:
|
||||
return wgpu::FeatureName::SurfaceCapabilities;
|
||||
case Feature::TransientAttachments:
|
||||
return wgpu::FeatureName::TransientAttachments;
|
||||
|
||||
case Feature::EnumCount:
|
||||
break;
|
||||
|
|
|
@ -47,6 +47,7 @@ enum class Feature {
|
|||
DawnNative,
|
||||
ImplicitDeviceSynchronization,
|
||||
SurfaceCapabilities,
|
||||
TransientAttachments,
|
||||
|
||||
EnumCount,
|
||||
InvalidEnum = EnumCount,
|
||||
|
|
|
@ -286,7 +286,8 @@ MaybeError ValidateTextureSize(const DeviceBase* device,
|
|||
return {};
|
||||
}
|
||||
|
||||
MaybeError ValidateTextureUsage(const TextureDescriptor* descriptor,
|
||||
MaybeError ValidateTextureUsage(const DeviceBase* device,
|
||||
const TextureDescriptor* descriptor,
|
||||
wgpu::TextureUsage usage,
|
||||
const Format* format) {
|
||||
DAWN_TRY(dawn::native::ValidateTextureUsage(usage));
|
||||
|
@ -318,6 +319,21 @@ MaybeError ValidateTextureUsage(const TextureDescriptor* descriptor,
|
|||
"The texture usage (%s) includes %s, which is incompatible with the format (%s).", usage,
|
||||
wgpu::TextureUsage::StorageBinding, format->format);
|
||||
|
||||
const auto kTransientAttachment = wgpu::TextureUsage::TransientAttachment;
|
||||
if (usage & kTransientAttachment) {
|
||||
DAWN_INVALID_IF(
|
||||
!device->HasFeature(Feature::TransientAttachments),
|
||||
"The texture usage (%s) includes %s, which requires the %s feature to be set", usage,
|
||||
kTransientAttachment, FeatureEnumToAPIFeature(Feature::TransientAttachments));
|
||||
|
||||
const auto kAllowedTransientUsage =
|
||||
kTransientAttachment | wgpu::TextureUsage::RenderAttachment;
|
||||
DAWN_INVALID_IF(usage != kAllowedTransientUsage,
|
||||
"The texture usage (%s) includes %s, which requires that the texture usage "
|
||||
"be exactly %s",
|
||||
usage, kTransientAttachment, kAllowedTransientUsage);
|
||||
}
|
||||
|
||||
// Only allows simple readonly texture usages.
|
||||
constexpr wgpu::TextureUsage kValidMultiPlanarUsages =
|
||||
wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopySrc;
|
||||
|
@ -356,7 +372,7 @@ MaybeError ValidateTextureDescriptor(const DeviceBase* device,
|
|||
usage |= internalUsageDesc->internalUsage;
|
||||
}
|
||||
|
||||
DAWN_TRY(ValidateTextureUsage(descriptor, usage, format));
|
||||
DAWN_TRY(ValidateTextureUsage(device, descriptor, usage, format));
|
||||
DAWN_TRY(ValidateTextureDimension(descriptor->dimension));
|
||||
DAWN_TRY(ValidateSampleCount(descriptor, usage, format));
|
||||
|
||||
|
|
|
@ -513,6 +513,14 @@ class Adapter : public AdapterBase {
|
|||
EnableFeature(Feature::MultiPlanarFormats);
|
||||
}
|
||||
|
||||
if (@available(macOS 11.0, iOS 10.0, *)) {
|
||||
// Memoryless storage mode for Metal textures is available only
|
||||
// from the Apple2 family of GPUs on.
|
||||
if ([*mDevice supportsFamily:MTLGPUFamilyApple2]) {
|
||||
EnableFeature(Feature::TransientAttachments);
|
||||
}
|
||||
}
|
||||
|
||||
EnableFeature(Feature::IndirectFirstInstance);
|
||||
EnableFeature(Feature::ShaderF16);
|
||||
EnableFeature(Feature::RG11B10UfloatRenderable);
|
||||
|
|
|
@ -677,7 +677,16 @@ NSRef<MTLTextureDescriptor> Texture::CreateMetalTextureDescriptor() const {
|
|||
mtlDesc.usage |= MTLTextureUsagePixelFormatView;
|
||||
}
|
||||
mtlDesc.mipmapLevelCount = GetNumMipLevels();
|
||||
|
||||
// Create the texture in private storage mode unless the client has
|
||||
// specified that this texture is for a transient attachment, in which case
|
||||
// the texture should be created in memoryless storage mode.
|
||||
mtlDesc.storageMode = MTLStorageModePrivate;
|
||||
if (@available(macOS 11.0, iOS 10.0, *)) {
|
||||
if (GetInternalUsage() & wgpu::TextureUsage::TransientAttachment) {
|
||||
mtlDesc.storageMode = MTLStorageModeMemoryless;
|
||||
}
|
||||
}
|
||||
|
||||
// Choose the correct MTLTextureType and paper over differences in how the array layer count
|
||||
// is specified.
|
||||
|
@ -778,6 +787,11 @@ MaybeError Texture::InitializeFromIOSurface(const ExternalImageDescriptor* descr
|
|||
const TextureDescriptor* textureDescriptor,
|
||||
IOSurfaceRef ioSurface,
|
||||
std::vector<MTLSharedEventAndSignalValue> waitEvents) {
|
||||
DAWN_INVALID_IF(
|
||||
GetInternalUsage() & wgpu::TextureUsage::TransientAttachment,
|
||||
"Usage flags (%s) include %s, which is not compatible with creation from IOSurface.",
|
||||
GetInternalUsage(), wgpu::TextureUsage::TransientAttachment);
|
||||
|
||||
mIOSurface = ioSurface;
|
||||
mWaitEvents = std::move(waitEvents);
|
||||
|
||||
|
|
|
@ -494,6 +494,8 @@ VkImageUsageFlags VulkanImageUsage(wgpu::TextureUsage usage, const Format& forma
|
|||
} else {
|
||||
flags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
}
|
||||
// TODO(1695): Check for TransientAttachment flag being present and
|
||||
// convert it into the proper Vulkan flag.
|
||||
}
|
||||
|
||||
// Choosing Vulkan image usages should not know about kReadOnlyRenderAttachment because that's
|
||||
|
@ -575,6 +577,13 @@ VkImageLayout VulkanImageLayout(const Texture* texture, wgpu::TextureUsage usage
|
|||
case kPresentTextureUsage:
|
||||
return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
case wgpu::TextureUsage::TransientAttachment:
|
||||
// Will be covered by RenderAttachment above, as specification of
|
||||
// TransientAttachment requires that RenderAttachment also be
|
||||
// specified.
|
||||
UNREACHABLE();
|
||||
break;
|
||||
|
||||
case wgpu::TextureUsage::None:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -209,6 +209,33 @@ TEST_P(IOSurfaceValidationTests, InvalidFormat) {
|
|||
ASSERT_EQ(texture.Get(), nullptr);
|
||||
}
|
||||
|
||||
class IOSurfaceTransientAttachmentValidationTests : public IOSurfaceValidationTests {
|
||||
void SetUp() override {
|
||||
IOSurfaceValidationTests::SetUp();
|
||||
|
||||
// Skip all tests if the transient attachments feature is not supported.
|
||||
DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::TransientAttachments}));
|
||||
}
|
||||
|
||||
std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
|
||||
std::vector<wgpu::FeatureName> requiredFeatures = {};
|
||||
if (SupportsFeatures({wgpu::FeatureName::TransientAttachments})) {
|
||||
requiredFeatures.push_back(wgpu::FeatureName::TransientAttachments);
|
||||
}
|
||||
return requiredFeatures;
|
||||
}
|
||||
};
|
||||
|
||||
// Test that an error occurs if the transient attachment is specified.
|
||||
TEST_P(IOSurfaceTransientAttachmentValidationTests, ErrorWhenSpecified) {
|
||||
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
||||
|
||||
descriptor.usage |= wgpu::TextureUsage::TransientAttachment;
|
||||
|
||||
ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get()));
|
||||
ASSERT_EQ(texture.Get(), nullptr);
|
||||
}
|
||||
|
||||
// Fixture to test using IOSurfaces through different usages.
|
||||
// These tests are skipped if the harness is using the wire.
|
||||
class IOSurfaceUsageTests : public IOSurfaceTestBase {
|
||||
|
@ -653,5 +680,6 @@ TEST_P(IOSurfaceMultithreadTests, WrapAndClear_OnMultipleThreads) {
|
|||
}
|
||||
|
||||
DAWN_INSTANTIATE_TEST(IOSurfaceValidationTests, MetalBackend());
|
||||
DAWN_INSTANTIATE_TEST(IOSurfaceTransientAttachmentValidationTests, MetalBackend());
|
||||
DAWN_INSTANTIATE_TEST(IOSurfaceUsageTests, MetalBackend());
|
||||
DAWN_INSTANTIATE_TEST(IOSurfaceMultithreadTests, MetalBackend());
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "dawn/common/Assert.h"
|
||||
#include "dawn/tests/DawnTest.h"
|
||||
|
@ -109,7 +110,8 @@ class MultisampledRenderingTest : public DawnTest {
|
|||
wgpu::Texture CreateTextureForRenderAttachment(wgpu::TextureFormat format,
|
||||
uint32_t sampleCount,
|
||||
uint32_t mipLevelCount = 1,
|
||||
uint32_t arrayLayerCount = 1) {
|
||||
uint32_t arrayLayerCount = 1,
|
||||
bool transientAttachment = false) {
|
||||
wgpu::TextureDescriptor descriptor;
|
||||
descriptor.dimension = wgpu::TextureDimension::e2D;
|
||||
descriptor.size.width = kWidth << (mipLevelCount - 1);
|
||||
|
@ -118,7 +120,12 @@ class MultisampledRenderingTest : public DawnTest {
|
|||
descriptor.sampleCount = sampleCount;
|
||||
descriptor.format = format;
|
||||
descriptor.mipLevelCount = mipLevelCount;
|
||||
if (transientAttachment) {
|
||||
descriptor.usage =
|
||||
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TransientAttachment;
|
||||
} else {
|
||||
descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
|
||||
}
|
||||
return device.CreateTexture(&descriptor);
|
||||
}
|
||||
|
||||
|
@ -1129,6 +1136,58 @@ TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithAlphaToCoverageAndRast
|
|||
}
|
||||
}
|
||||
|
||||
class MultisampledRenderingWithTransientAttachmentTest : public MultisampledRenderingTest {
|
||||
void SetUp() override {
|
||||
MultisampledRenderingTest::SetUp();
|
||||
|
||||
// Skip all tests if the transient attachments feature is not supported.
|
||||
DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::TransientAttachments}));
|
||||
}
|
||||
|
||||
std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
|
||||
std::vector<wgpu::FeatureName> requiredFeatures = {};
|
||||
if (SupportsFeatures({wgpu::FeatureName::TransientAttachments})) {
|
||||
requiredFeatures.push_back(wgpu::FeatureName::TransientAttachments);
|
||||
}
|
||||
return requiredFeatures;
|
||||
}
|
||||
};
|
||||
|
||||
// Test using one multisampled color transient attachment with resolve target can render correctly.
|
||||
TEST_P(MultisampledRenderingWithTransientAttachmentTest, ResolveTransientAttachmentInto2DTexture) {
|
||||
constexpr bool kTestDepth = false;
|
||||
wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth);
|
||||
|
||||
auto transientMultisampledColorTexture =
|
||||
CreateTextureForRenderAttachment(kColorFormat, kSampleCount,
|
||||
/*mipLevelCount=*/1,
|
||||
/*arrayLayerCount=*/1,
|
||||
/*transientAttachment=*/true);
|
||||
auto transientMultisampledColorView = transientMultisampledColorTexture.CreateView();
|
||||
constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
|
||||
|
||||
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
||||
|
||||
// Draw a green triangle.
|
||||
{
|
||||
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
|
||||
{transientMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear,
|
||||
wgpu::LoadOp::Clear, kTestDepth);
|
||||
|
||||
// Note: It is not possible to store into a transient attachment.
|
||||
renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard;
|
||||
|
||||
std::array<float, 4> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a};
|
||||
constexpr uint32_t kSize = sizeof(kUniformData);
|
||||
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
|
||||
}
|
||||
|
||||
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
|
||||
queue.Submit(1, &commandBuffer);
|
||||
|
||||
VerifyResolveTarget(kGreen, mResolveTexture);
|
||||
}
|
||||
|
||||
DAWN_INSTANTIATE_TEST(MultisampledRenderingTest,
|
||||
D3D12Backend(),
|
||||
D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}),
|
||||
|
@ -1142,3 +1201,17 @@ DAWN_INSTANTIATE_TEST(MultisampledRenderingTest,
|
|||
MetalBackend({"always_resolve_into_zero_level_and_layer"}),
|
||||
MetalBackend({"always_resolve_into_zero_level_and_layer",
|
||||
"emulate_store_and_msaa_resolve"}));
|
||||
|
||||
DAWN_INSTANTIATE_TEST(MultisampledRenderingWithTransientAttachmentTest,
|
||||
D3D12Backend(),
|
||||
D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}),
|
||||
D3D12Backend({}, {"use_d3d12_render_pass"}),
|
||||
MetalBackend(),
|
||||
OpenGLBackend(),
|
||||
OpenGLESBackend(),
|
||||
VulkanBackend(),
|
||||
VulkanBackend({"always_resolve_into_zero_level_and_layer"}),
|
||||
MetalBackend({"emulate_store_and_msaa_resolve"}),
|
||||
MetalBackend({"always_resolve_into_zero_level_and_layer"}),
|
||||
MetalBackend({"always_resolve_into_zero_level_and_layer",
|
||||
"emulate_store_and_msaa_resolve"}));
|
||||
|
|
|
@ -1892,3 +1892,111 @@ TEST_F(InterStageVariableMatchingValidationTest, DifferentInterpolationAttribute
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RenderPipelineTransientAttachmentValidationTest : public RenderPipelineValidationTest {
|
||||
protected:
|
||||
WGPUDevice CreateTestDevice(dawn::native::Adapter dawnAdapter) override {
|
||||
wgpu::DeviceDescriptor descriptor;
|
||||
wgpu::FeatureName requiredFeatures[2] = {wgpu::FeatureName::ShaderF16,
|
||||
wgpu::FeatureName::TransientAttachments};
|
||||
descriptor.requiredFeatures = requiredFeatures;
|
||||
descriptor.requiredFeaturesCount = 2;
|
||||
return dawnAdapter.CreateDevice(&descriptor);
|
||||
}
|
||||
};
|
||||
|
||||
// Test case where creation should succeed.
|
||||
TEST_F(RenderPipelineTransientAttachmentValidationTest, CreationSuccess) {
|
||||
constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm;
|
||||
|
||||
wgpu::TextureDescriptor textureDescriptor;
|
||||
textureDescriptor.usage =
|
||||
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TransientAttachment;
|
||||
textureDescriptor.format = kColorFormat;
|
||||
textureDescriptor.size.width = 4;
|
||||
textureDescriptor.size.height = 4;
|
||||
|
||||
wgpu::Texture transientTexture = device.CreateTexture(&textureDescriptor);
|
||||
utils::ComboRenderPassDescriptor renderPassDescriptor({transientTexture.CreateView()});
|
||||
|
||||
// Set load and store ops to supported values with transient attachments.
|
||||
renderPassDescriptor.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard;
|
||||
renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
|
||||
|
||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
|
||||
|
||||
utils::ComboRenderPipelineDescriptor pipelineDescriptor;
|
||||
pipelineDescriptor.vertex.module = vsModule;
|
||||
pipelineDescriptor.cFragment.module = fsModule;
|
||||
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
|
||||
|
||||
renderPass.SetPipeline(pipeline);
|
||||
renderPass.End();
|
||||
|
||||
encoder.Finish();
|
||||
}
|
||||
|
||||
// Creation of a pipeline that stores into a transient attachment should cause
|
||||
// an error.
|
||||
TEST_F(RenderPipelineTransientAttachmentValidationTest, StoreCausesError) {
|
||||
constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm;
|
||||
|
||||
wgpu::TextureDescriptor textureDescriptor;
|
||||
textureDescriptor.usage =
|
||||
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TransientAttachment;
|
||||
textureDescriptor.format = kColorFormat;
|
||||
textureDescriptor.size.width = 4;
|
||||
textureDescriptor.size.height = 4;
|
||||
|
||||
wgpu::Texture transientTexture = device.CreateTexture(&textureDescriptor);
|
||||
utils::ComboRenderPassDescriptor renderPassDescriptor({transientTexture.CreateView()});
|
||||
|
||||
renderPassDescriptor.cColorAttachments[0].storeOp = wgpu::StoreOp::Store;
|
||||
renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
|
||||
|
||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
|
||||
|
||||
utils::ComboRenderPipelineDescriptor pipelineDescriptor;
|
||||
pipelineDescriptor.vertex.module = vsModule;
|
||||
pipelineDescriptor.cFragment.module = fsModule;
|
||||
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
|
||||
|
||||
renderPass.SetPipeline(pipeline);
|
||||
renderPass.End();
|
||||
|
||||
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||
}
|
||||
|
||||
// Creation of a pipeline that loads from a transient attachment should cause
|
||||
// an error.
|
||||
TEST_F(RenderPipelineTransientAttachmentValidationTest, LoadCausesError) {
|
||||
constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm;
|
||||
|
||||
wgpu::TextureDescriptor textureDescriptor;
|
||||
textureDescriptor.usage =
|
||||
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TransientAttachment;
|
||||
textureDescriptor.format = kColorFormat;
|
||||
textureDescriptor.size.width = 4;
|
||||
textureDescriptor.size.height = 4;
|
||||
|
||||
wgpu::Texture transientTexture = device.CreateTexture(&textureDescriptor);
|
||||
utils::ComboRenderPassDescriptor renderPassDescriptor({transientTexture.CreateView()});
|
||||
|
||||
renderPassDescriptor.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard;
|
||||
renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Load;
|
||||
|
||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
|
||||
|
||||
utils::ComboRenderPipelineDescriptor pipelineDescriptor;
|
||||
pipelineDescriptor.vertex.module = vsModule;
|
||||
pipelineDescriptor.cFragment.module = fsModule;
|
||||
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
|
||||
|
||||
renderPass.SetPipeline(pipeline);
|
||||
renderPass.End();
|
||||
|
||||
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||
}
|
||||
|
|
|
@ -1025,4 +1025,60 @@ TEST_F(TextureValidationTest, APIValidateTextureDescriptor) {
|
|||
ASSERT_DEVICE_ERROR(device.ValidateTextureDescriptor(&desc));
|
||||
}
|
||||
|
||||
// Tests that specification of the transient attachment on an unsupported device
|
||||
// causes an error.
|
||||
TEST_F(TextureValidationTest, TransientAttachmentOnUnsupportedDevice) {
|
||||
wgpu::TextureDescriptor desc;
|
||||
desc.format = wgpu::TextureFormat::RGBA8Unorm;
|
||||
desc.size = {1, 1, 1};
|
||||
desc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TransientAttachment;
|
||||
|
||||
ASSERT_DEVICE_ERROR(device.CreateTexture(&desc));
|
||||
}
|
||||
|
||||
class TransientAttachmentValidationTest : public TextureValidationTest {
|
||||
protected:
|
||||
WGPUDevice CreateTestDevice(dawn::native::Adapter dawnAdapter) override {
|
||||
wgpu::DeviceDescriptor descriptor;
|
||||
wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::TransientAttachments};
|
||||
descriptor.requiredFeatures = requiredFeatures;
|
||||
descriptor.requiredFeaturesCount = 1;
|
||||
return dawnAdapter.CreateDevice(&descriptor);
|
||||
}
|
||||
};
|
||||
|
||||
// Tests that specification of the transient attachment with supported usage on
|
||||
// a supported device does not raise a validation error.
|
||||
TEST_F(TransientAttachmentValidationTest, Success) {
|
||||
wgpu::TextureDescriptor desc;
|
||||
desc.format = wgpu::TextureFormat::RGBA8Unorm;
|
||||
desc.size = {1, 1, 1};
|
||||
desc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TransientAttachment;
|
||||
|
||||
device.CreateTexture(&desc);
|
||||
}
|
||||
|
||||
// Tests that specification of the transient attachment without specification of
|
||||
// the render attachment causes an error.
|
||||
TEST_F(TransientAttachmentValidationTest, NoRenderAttachment) {
|
||||
wgpu::TextureDescriptor desc;
|
||||
desc.format = wgpu::TextureFormat::RGBA8Unorm;
|
||||
desc.size = {1, 1, 1};
|
||||
desc.usage = wgpu::TextureUsage::TransientAttachment;
|
||||
|
||||
ASSERT_DEVICE_ERROR(device.CreateTexture(&desc));
|
||||
}
|
||||
|
||||
// Tests that specification of the transient attachment with flags beyond just
|
||||
// render attachment causes an error.
|
||||
TEST_F(TransientAttachmentValidationTest, FlagsBeyondRenderAttachment) {
|
||||
wgpu::TextureDescriptor desc;
|
||||
desc.format = wgpu::TextureFormat::RGBA8Unorm;
|
||||
desc.size = {1, 1, 1};
|
||||
desc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TransientAttachment |
|
||||
wgpu::TextureUsage::CopySrc;
|
||||
|
||||
ASSERT_DEVICE_ERROR(device.CreateTexture(&desc));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -42,6 +42,7 @@ bool IsFeatureSupported(WGPUFeatureName feature) {
|
|||
case WGPUFeatureName_ShaderF16:
|
||||
case WGPUFeatureName_RG11B10UfloatRenderable:
|
||||
case WGPUFeatureName_BGRA8UnormStorage:
|
||||
case WGPUFeatureName_TransientAttachments:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue