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:
Colin Blundell 2023-04-27 14:55:09 +00:00 committed by Dawn LUCI CQ
parent c820dcc53e
commit a406959e50
14 changed files with 365 additions and 6 deletions

View File

@ -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": {

View File

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

View File

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

View File

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

View File

@ -47,6 +47,7 @@ enum class Feature {
DawnNative,
ImplicitDeviceSynchronization,
SurfaceCapabilities,
TransientAttachments,
EnumCount,
InvalidEnum = EnumCount,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
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"}));

View File

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

View File

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

View File

@ -42,6 +42,7 @@ bool IsFeatureSupported(WGPUFeatureName feature) {
case WGPUFeatureName_ShaderF16:
case WGPUFeatureName_RG11B10UfloatRenderable:
case WGPUFeatureName_BGRA8UnormStorage:
case WGPUFeatureName_TransientAttachments:
return true;
}