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": 1005, "name": "chromium experimental dp4a", "tags": ["dawn"]},
|
||||||
{"value": 1006, "name": "timestamp query inside passes", "tags": ["dawn"]},
|
{"value": 1006, "name": "timestamp query inside passes", "tags": ["dawn"]},
|
||||||
{"value": 1007, "name": "implicit device synchronization", "tags": ["dawn", "native"]},
|
{"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": {
|
"filter mode": {
|
||||||
|
@ -2829,7 +2830,8 @@
|
||||||
{"value": 4, "name": "texture binding"},
|
{"value": 4, "name": "texture binding"},
|
||||||
{"value": 8, "name": "storage binding"},
|
{"value": 8, "name": "storage binding"},
|
||||||
{"value": 16, "name": "render attachment"},
|
{"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": {
|
"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_TRY(ValidateStoreOp(colorAttachment.storeOp));
|
||||||
DAWN_INVALID_IF(colorAttachment.loadOp == wgpu::LoadOp::Undefined, "loadOp must be set.");
|
DAWN_INVALID_IF(colorAttachment.loadOp == wgpu::LoadOp::Undefined, "loadOp must be set.");
|
||||||
DAWN_INVALID_IF(colorAttachment.storeOp == wgpu::StoreOp::Undefined, "storeOp 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;
|
const dawn::native::Color& clearValue = colorAttachment.clearValue;
|
||||||
if (colorAttachment.loadOp == wgpu::LoadOp::Clear) {
|
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 "
|
"Support querying Surface's capabilities such as supported usage flags. This feature also "
|
||||||
"enables swap chain to be created with usage other than RenderAttachment.",
|
"enables swap chain to be created with usage other than RenderAttachment.",
|
||||||
"https://bugs.chromium.org/p/dawn/issues/detail?id=1760", FeatureInfo::FeatureState::Stable}},
|
"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) {
|
Feature FromAPIFeature(wgpu::FeatureName feature) {
|
||||||
|
@ -153,6 +158,8 @@ Feature FromAPIFeature(wgpu::FeatureName feature) {
|
||||||
return Feature::ImplicitDeviceSynchronization;
|
return Feature::ImplicitDeviceSynchronization;
|
||||||
case wgpu::FeatureName::SurfaceCapabilities:
|
case wgpu::FeatureName::SurfaceCapabilities:
|
||||||
return Feature::SurfaceCapabilities;
|
return Feature::SurfaceCapabilities;
|
||||||
|
case wgpu::FeatureName::TransientAttachments:
|
||||||
|
return Feature::TransientAttachments;
|
||||||
}
|
}
|
||||||
return Feature::InvalidEnum;
|
return Feature::InvalidEnum;
|
||||||
}
|
}
|
||||||
|
@ -195,6 +202,8 @@ wgpu::FeatureName ToAPIFeature(Feature feature) {
|
||||||
return wgpu::FeatureName::ImplicitDeviceSynchronization;
|
return wgpu::FeatureName::ImplicitDeviceSynchronization;
|
||||||
case Feature::SurfaceCapabilities:
|
case Feature::SurfaceCapabilities:
|
||||||
return wgpu::FeatureName::SurfaceCapabilities;
|
return wgpu::FeatureName::SurfaceCapabilities;
|
||||||
|
case Feature::TransientAttachments:
|
||||||
|
return wgpu::FeatureName::TransientAttachments;
|
||||||
|
|
||||||
case Feature::EnumCount:
|
case Feature::EnumCount:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -47,6 +47,7 @@ enum class Feature {
|
||||||
DawnNative,
|
DawnNative,
|
||||||
ImplicitDeviceSynchronization,
|
ImplicitDeviceSynchronization,
|
||||||
SurfaceCapabilities,
|
SurfaceCapabilities,
|
||||||
|
TransientAttachments,
|
||||||
|
|
||||||
EnumCount,
|
EnumCount,
|
||||||
InvalidEnum = EnumCount,
|
InvalidEnum = EnumCount,
|
||||||
|
|
|
@ -286,7 +286,8 @@ MaybeError ValidateTextureSize(const DeviceBase* device,
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError ValidateTextureUsage(const TextureDescriptor* descriptor,
|
MaybeError ValidateTextureUsage(const DeviceBase* device,
|
||||||
|
const TextureDescriptor* descriptor,
|
||||||
wgpu::TextureUsage usage,
|
wgpu::TextureUsage usage,
|
||||||
const Format* format) {
|
const Format* format) {
|
||||||
DAWN_TRY(dawn::native::ValidateTextureUsage(usage));
|
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,
|
"The texture usage (%s) includes %s, which is incompatible with the format (%s).", usage,
|
||||||
wgpu::TextureUsage::StorageBinding, format->format);
|
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.
|
// Only allows simple readonly texture usages.
|
||||||
constexpr wgpu::TextureUsage kValidMultiPlanarUsages =
|
constexpr wgpu::TextureUsage kValidMultiPlanarUsages =
|
||||||
wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopySrc;
|
wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopySrc;
|
||||||
|
@ -356,7 +372,7 @@ MaybeError ValidateTextureDescriptor(const DeviceBase* device,
|
||||||
usage |= internalUsageDesc->internalUsage;
|
usage |= internalUsageDesc->internalUsage;
|
||||||
}
|
}
|
||||||
|
|
||||||
DAWN_TRY(ValidateTextureUsage(descriptor, usage, format));
|
DAWN_TRY(ValidateTextureUsage(device, descriptor, usage, format));
|
||||||
DAWN_TRY(ValidateTextureDimension(descriptor->dimension));
|
DAWN_TRY(ValidateTextureDimension(descriptor->dimension));
|
||||||
DAWN_TRY(ValidateSampleCount(descriptor, usage, format));
|
DAWN_TRY(ValidateSampleCount(descriptor, usage, format));
|
||||||
|
|
||||||
|
|
|
@ -513,6 +513,14 @@ class Adapter : public AdapterBase {
|
||||||
EnableFeature(Feature::MultiPlanarFormats);
|
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::IndirectFirstInstance);
|
||||||
EnableFeature(Feature::ShaderF16);
|
EnableFeature(Feature::ShaderF16);
|
||||||
EnableFeature(Feature::RG11B10UfloatRenderable);
|
EnableFeature(Feature::RG11B10UfloatRenderable);
|
||||||
|
|
|
@ -677,7 +677,16 @@ NSRef<MTLTextureDescriptor> Texture::CreateMetalTextureDescriptor() const {
|
||||||
mtlDesc.usage |= MTLTextureUsagePixelFormatView;
|
mtlDesc.usage |= MTLTextureUsagePixelFormatView;
|
||||||
}
|
}
|
||||||
mtlDesc.mipmapLevelCount = GetNumMipLevels();
|
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;
|
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
|
// Choose the correct MTLTextureType and paper over differences in how the array layer count
|
||||||
// is specified.
|
// is specified.
|
||||||
|
@ -778,6 +787,11 @@ MaybeError Texture::InitializeFromIOSurface(const ExternalImageDescriptor* descr
|
||||||
const TextureDescriptor* textureDescriptor,
|
const TextureDescriptor* textureDescriptor,
|
||||||
IOSurfaceRef ioSurface,
|
IOSurfaceRef ioSurface,
|
||||||
std::vector<MTLSharedEventAndSignalValue> waitEvents) {
|
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;
|
mIOSurface = ioSurface;
|
||||||
mWaitEvents = std::move(waitEvents);
|
mWaitEvents = std::move(waitEvents);
|
||||||
|
|
||||||
|
|
|
@ -494,6 +494,8 @@ VkImageUsageFlags VulkanImageUsage(wgpu::TextureUsage usage, const Format& forma
|
||||||
} else {
|
} else {
|
||||||
flags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
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
|
// 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:
|
case kPresentTextureUsage:
|
||||||
return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
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:
|
case wgpu::TextureUsage::None:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,6 +209,33 @@ TEST_P(IOSurfaceValidationTests, InvalidFormat) {
|
||||||
ASSERT_EQ(texture.Get(), nullptr);
|
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.
|
// Fixture to test using IOSurfaces through different usages.
|
||||||
// These tests are skipped if the harness is using the wire.
|
// These tests are skipped if the harness is using the wire.
|
||||||
class IOSurfaceUsageTests : public IOSurfaceTestBase {
|
class IOSurfaceUsageTests : public IOSurfaceTestBase {
|
||||||
|
@ -653,5 +680,6 @@ TEST_P(IOSurfaceMultithreadTests, WrapAndClear_OnMultipleThreads) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DAWN_INSTANTIATE_TEST(IOSurfaceValidationTests, MetalBackend());
|
DAWN_INSTANTIATE_TEST(IOSurfaceValidationTests, MetalBackend());
|
||||||
|
DAWN_INSTANTIATE_TEST(IOSurfaceTransientAttachmentValidationTests, MetalBackend());
|
||||||
DAWN_INSTANTIATE_TEST(IOSurfaceUsageTests, MetalBackend());
|
DAWN_INSTANTIATE_TEST(IOSurfaceUsageTests, MetalBackend());
|
||||||
DAWN_INSTANTIATE_TEST(IOSurfaceMultithreadTests, MetalBackend());
|
DAWN_INSTANTIATE_TEST(IOSurfaceMultithreadTests, MetalBackend());
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "dawn/common/Assert.h"
|
#include "dawn/common/Assert.h"
|
||||||
#include "dawn/tests/DawnTest.h"
|
#include "dawn/tests/DawnTest.h"
|
||||||
|
@ -109,7 +110,8 @@ class MultisampledRenderingTest : public DawnTest {
|
||||||
wgpu::Texture CreateTextureForRenderAttachment(wgpu::TextureFormat format,
|
wgpu::Texture CreateTextureForRenderAttachment(wgpu::TextureFormat format,
|
||||||
uint32_t sampleCount,
|
uint32_t sampleCount,
|
||||||
uint32_t mipLevelCount = 1,
|
uint32_t mipLevelCount = 1,
|
||||||
uint32_t arrayLayerCount = 1) {
|
uint32_t arrayLayerCount = 1,
|
||||||
|
bool transientAttachment = false) {
|
||||||
wgpu::TextureDescriptor descriptor;
|
wgpu::TextureDescriptor descriptor;
|
||||||
descriptor.dimension = wgpu::TextureDimension::e2D;
|
descriptor.dimension = wgpu::TextureDimension::e2D;
|
||||||
descriptor.size.width = kWidth << (mipLevelCount - 1);
|
descriptor.size.width = kWidth << (mipLevelCount - 1);
|
||||||
|
@ -118,7 +120,12 @@ class MultisampledRenderingTest : public DawnTest {
|
||||||
descriptor.sampleCount = sampleCount;
|
descriptor.sampleCount = sampleCount;
|
||||||
descriptor.format = format;
|
descriptor.format = format;
|
||||||
descriptor.mipLevelCount = mipLevelCount;
|
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);
|
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,
|
DAWN_INSTANTIATE_TEST(MultisampledRenderingTest,
|
||||||
D3D12Backend(),
|
D3D12Backend(),
|
||||||
D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}),
|
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"}),
|
||||||
MetalBackend({"always_resolve_into_zero_level_and_layer",
|
MetalBackend({"always_resolve_into_zero_level_and_layer",
|
||||||
"emulate_store_and_msaa_resolve"}));
|
"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));
|
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
|
} // namespace
|
||||||
|
|
|
@ -42,6 +42,7 @@ bool IsFeatureSupported(WGPUFeatureName feature) {
|
||||||
case WGPUFeatureName_ShaderF16:
|
case WGPUFeatureName_ShaderF16:
|
||||||
case WGPUFeatureName_RG11B10UfloatRenderable:
|
case WGPUFeatureName_RG11B10UfloatRenderable:
|
||||||
case WGPUFeatureName_BGRA8UnormStorage:
|
case WGPUFeatureName_BGRA8UnormStorage:
|
||||||
|
case WGPUFeatureName_TransientAttachments:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue