[YCbCr Samplers] Add support for TextureViewDescriptor
Add YCbCrVulkanDescriptor to TextureViewDescriptor and necessary
support needed for creating a vkImageView out of it. Also add some
validation and tests.
Change-Id: I38a5019febdcc9c6cd9c38832719fa0f7eaed6a5
Bug: dawn:2476
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/184621
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Saifuddin Hitawala <hitawala@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/include/dawn/native/VulkanBackend.h b/include/dawn/native/VulkanBackend.h
index d5f31b4..141e4b5 100644
--- a/include/dawn/native/VulkanBackend.h
+++ b/include/dawn/native/VulkanBackend.h
@@ -82,7 +82,7 @@
using ExternalImageExportInfo::ExternalImageExportInfo;
};
-// Can be chained in WGPUSamplerDescriptor
+// Can be chained in WGPUSamplerDescriptor and WGPUTextureViewDescriptor
struct DAWN_NATIVE_EXPORT YCbCrVulkanDescriptor : wgpu::ChainedStruct {
YCbCrVulkanDescriptor();
diff --git a/src/dawn/native/ChainUtilsImpl.inl b/src/dawn/native/ChainUtilsImpl.inl
index 6850ad3..c94d4da 100644
--- a/src/dawn/native/ChainUtilsImpl.inl
+++ b/src/dawn/native/ChainUtilsImpl.inl
@@ -113,6 +113,12 @@
AdditionalExtensionsList<const vulkan::YCbCrVulkanDescriptor*>;
};
+template <>
+struct AdditionalExtensions<TextureViewDescriptor> {
+ using List =
+ AdditionalExtensionsList<const vulkan::YCbCrVulkanDescriptor*>;
+};
+
} // namespace detail
} // namespace dawn::native
diff --git a/src/dawn/native/CommandBufferStateTracker.cpp b/src/dawn/native/CommandBufferStateTracker.cpp
index 4ee8e11..cbc445c 100644
--- a/src/dawn/native/CommandBufferStateTracker.cpp
+++ b/src/dawn/native/CommandBufferStateTracker.cpp
@@ -613,6 +613,8 @@
}
if (aspects[VALIDATION_ASPECT_BIND_GROUPS]) {
+ // TODO(crbug.com/dawn/2476): Validate TextureViewDescriptor YCbCrInfo matches with that in
+ // SamplerDescriptor.
for (BindGroupIndex i : IterateBitSet(mLastPipelineLayout->GetBindGroupLayoutsMask())) {
DAWN_ASSERT(HasPipeline());
diff --git a/src/dawn/native/Subresource.cpp b/src/dawn/native/Subresource.cpp
index 974cde3..2429029 100644
--- a/src/dawn/native/Subresource.cpp
+++ b/src/dawn/native/Subresource.cpp
@@ -82,6 +82,8 @@
}
Aspect SelectFormatAspects(const Format& format, wgpu::TextureAspect aspect) {
+ // TODO(crbug.com/dawn/2476): Return Aspect::Color for TextureFormat::External if aspect is
+ // present else None.
switch (aspect) {
case wgpu::TextureAspect::All:
return format.aspects;
diff --git a/src/dawn/native/Texture.cpp b/src/dawn/native/Texture.cpp
index 76fb4af..c3ce8af 100644
--- a/src/dawn/native/Texture.cpp
+++ b/src/dawn/native/Texture.cpp
@@ -46,6 +46,11 @@
#include "dawn/native/ValidationUtils_autogen.h"
namespace dawn::native {
+
+namespace vulkan {
+struct YCbCrVulkanDescriptor;
+}
+
namespace {
MaybeError ValidateTextureViewFormatCompatibility(const DeviceBase* device,
@@ -589,13 +594,12 @@
MaybeError ValidateTextureViewDescriptor(const DeviceBase* device,
const TextureBase* texture,
const UnpackedPtr<TextureViewDescriptor>& descriptor) {
- DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr.");
-
// Parent texture should have been already validated.
DAWN_ASSERT(texture);
DAWN_ASSERT(!texture->IsError());
DAWN_TRY(ValidateTextureViewDimension(descriptor->dimension));
+ // TODO(crbug.com/dawn/2476): Add necessary validation for TextureFormat::External.
DAWN_TRY(ValidateTextureFormat(descriptor->format));
DAWN_TRY(ValidateTextureAspect(descriptor->aspect));
@@ -626,9 +630,16 @@
"texture's mip level count (%u).",
descriptor->baseMipLevel, descriptor->mipLevelCount, texture->GetNumMipLevels());
+ // TODO(crbug.com/dawn/2476): Add necessary validations to CanViewTextureAs for
+ // TextureFormat::External.
DAWN_TRY(ValidateCanViewTextureAs(device, texture, *viewFormat, descriptor->aspect));
DAWN_TRY(ValidateTextureViewDimensionCompatibility(device, texture, descriptor));
+ if (descriptor.Get<vulkan::YCbCrVulkanDescriptor>()) {
+ DAWN_INVALID_IF(!device->HasFeature(Feature::YCbCrVulkanSamplers), "%s is not enabled.",
+ wgpu::FeatureName::YCbCrVulkanSamplers);
+ }
+
return {};
}
@@ -667,6 +678,7 @@
}
}
+ // TODO(crbug.com/dawn/2476): Add TextureFormat::External validation.
if (desc.format == wgpu::TextureFormat::Undefined) {
const Format& format = texture->GetFormat();
diff --git a/src/dawn/native/vulkan/SamplerVk.cpp b/src/dawn/native/vulkan/SamplerVk.cpp
index 4f3e334..f6c3c2e 100644
--- a/src/dawn/native/vulkan/SamplerVk.cpp
+++ b/src/dawn/native/vulkan/SamplerVk.cpp
@@ -125,22 +125,7 @@
if (auto* vulkanYCbCrDescriptor = Unpack(descriptor).Get<vulkan::YCbCrVulkanDescriptor>()) {
const VkSamplerYcbcrConversionCreateInfo& vulkanYCbCrInfo =
vulkanYCbCrDescriptor->vulkanYCbCrInfo;
-#if DAWN_PLATFORM_IS(ANDROID)
- const VkBaseInStructure* chain =
- static_cast<const VkBaseInStructure*>(vulkanYCbCrInfo.pNext);
- while (chain != nullptr) {
- if (chain->sType == VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID) {
- const VkExternalFormatANDROID* vkExternalFormat =
- reinterpret_cast<const VkExternalFormatANDROID*>(chain);
- DAWN_INVALID_IF((vkExternalFormat->externalFormat == 0 &&
- vulkanYCbCrInfo.format == VK_FORMAT_UNDEFINED),
- "Both VkFormat and VkExternalFormatANDROID are undefined.");
- break;
- }
- chain = chain->pNext;
- }
-#endif // DAWN_PLATFORM_IS(ANDROID)
-
+ DAWN_TRY(ValidateCanCreateSamplerYCbCrConversion(vulkanYCbCrInfo));
DAWN_TRY(CheckVkSuccess(
device->fn.CreateSamplerYcbcrConversion(device->GetVkDevice(), &vulkanYCbCrInfo,
nullptr, &*mSamplerYCbCrConversion),
diff --git a/src/dawn/native/vulkan/TextureVk.cpp b/src/dawn/native/vulkan/TextureVk.cpp
index df558e5..5a11158 100644
--- a/src/dawn/native/vulkan/TextureVk.cpp
+++ b/src/dawn/native/vulkan/TextureVk.cpp
@@ -1760,6 +1760,24 @@
usageInfo.usage = VulkanImageUsage(usage, GetFormat());
createInfo.pNext = &usageInfo;
+ VkSamplerYcbcrConversionInfo samplerYCbCrInfo = {};
+ if (auto* vulkanYCbCrDescriptor = descriptor.Get<vulkan::YCbCrVulkanDescriptor>()) {
+ // TODO(crbug.com/dawn/2476): Validate mSamplerYcbcrConversionCreateInfo matches with that
+ // in SamplerDescriptor.
+ mSamplerYcbcrConversionCreateInfo = vulkanYCbCrDescriptor->vulkanYCbCrInfo;
+ DAWN_TRY(ValidateCanCreateSamplerYCbCrConversion(mSamplerYcbcrConversionCreateInfo));
+ DAWN_TRY(CheckVkSuccess(device->fn.CreateSamplerYcbcrConversion(
+ device->GetVkDevice(), &mSamplerYcbcrConversionCreateInfo,
+ nullptr, &*mSamplerYCbCrConversion),
+ "CreateSamplerYcbcrConversion for vkImageView"));
+
+ samplerYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO;
+ samplerYCbCrInfo.pNext = nullptr;
+ samplerYCbCrInfo.conversion = mSamplerYCbCrConversion;
+
+ createInfo.pNext = &samplerYCbCrInfo;
+ }
+
DAWN_TRY(CheckVkSuccess(
device->fn.CreateImageView(device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
"CreateImageView"));
@@ -1784,6 +1802,11 @@
void TextureView::DestroyImpl() {
Device* device = ToBackend(GetTexture()->GetDevice());
+ if (mSamplerYCbCrConversion != VK_NULL_HANDLE) {
+ device->GetFencedDeleter()->DeleteWhenUnused(mSamplerYCbCrConversion);
+ mSamplerYCbCrConversion = VK_NULL_HANDLE;
+ }
+
if (mHandle != VK_NULL_HANDLE) {
device->GetFencedDeleter()->DeleteWhenUnused(mHandle);
mHandle = VK_NULL_HANDLE;
diff --git a/src/dawn/native/vulkan/TextureVk.h b/src/dawn/native/vulkan/TextureVk.h
index f38fd7d..9923e86 100644
--- a/src/dawn/native/vulkan/TextureVk.h
+++ b/src/dawn/native/vulkan/TextureVk.h
@@ -257,6 +257,8 @@
VkImageView mHandle = VK_NULL_HANDLE;
VkImageView mHandleForBGRA8UnormStorage = VK_NULL_HANDLE;
+ VkSamplerYcbcrConversion mSamplerYCbCrConversion = VK_NULL_HANDLE;
+ VkSamplerYcbcrConversionCreateInfo mSamplerYcbcrConversionCreateInfo;
std::vector<VkImageView> mHandlesFor2DViewOn3D;
};
diff --git a/src/dawn/native/vulkan/UtilsVulkan.cpp b/src/dawn/native/vulkan/UtilsVulkan.cpp
index e8c1533..5dc630c 100644
--- a/src/dawn/native/vulkan/UtilsVulkan.cpp
+++ b/src/dawn/native/vulkan/UtilsVulkan.cpp
@@ -327,4 +327,23 @@
return DAWN_VALIDATION_ERROR("DRM format modifier %u not supported.", modifier);
}
+MaybeError ValidateCanCreateSamplerYCbCrConversion(
+ const VkSamplerYcbcrConversionCreateInfo& vulkanYCbCrInfo) {
+#if DAWN_PLATFORM_IS(ANDROID)
+ const VkBaseInStructure* chain = static_cast<const VkBaseInStructure*>(vulkanYCbCrInfo.pNext);
+ while (chain != nullptr) {
+ if (chain->sType == VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID) {
+ const VkExternalFormatANDROID* vkExternalFormat =
+ reinterpret_cast<const VkExternalFormatANDROID*>(chain);
+ DAWN_INVALID_IF((vkExternalFormat->externalFormat == 0 &&
+ vulkanYCbCrInfo.format == VK_FORMAT_UNDEFINED),
+ "Both VkFormat and VkExternalFormatANDROID are undefined.");
+ break;
+ }
+ chain = chain->pNext;
+ }
+#endif // DAWN_PLATFORM_IS(ANDROID)
+ return {};
+}
+
} // namespace dawn::native::vulkan
diff --git a/src/dawn/native/vulkan/UtilsVulkan.h b/src/dawn/native/vulkan/UtilsVulkan.h
index 8bd149a..6e06e199c 100644
--- a/src/dawn/native/vulkan/UtilsVulkan.h
+++ b/src/dawn/native/vulkan/UtilsVulkan.h
@@ -178,6 +178,10 @@
VkPhysicalDevice vkPhysicalDevice,
VkFormat format,
uint64_t modifier);
+
+MaybeError ValidateCanCreateSamplerYCbCrConversion(
+ const VkSamplerYcbcrConversionCreateInfo& vulkanYCbCrInfo);
+
} // namespace dawn::native::vulkan
#endif // SRC_DAWN_NATIVE_VULKAN_UTILSVULKAN_H_
diff --git a/src/dawn/tests/end2end/YCbCrSamplerTests.cpp b/src/dawn/tests/end2end/YCbCrSamplerTests.cpp
index 795b3f1..0707640 100644
--- a/src/dawn/tests/end2end/YCbCrSamplerTests.cpp
+++ b/src/dawn/tests/end2end/YCbCrSamplerTests.cpp
@@ -37,7 +37,40 @@
namespace dawn {
namespace {
-class YCbCrSamplerTest : public DawnTest {
+constexpr uint32_t kWidth = 32u;
+constexpr uint32_t kHeight = 32u;
+constexpr uint32_t kDefaultMipLevels = 6u;
+constexpr uint32_t kDefaultLayerCount = 1u;
+constexpr uint32_t kDefaultSampleCount = 1u;
+constexpr wgpu::TextureFormat kDefaultTextureFormat = wgpu::TextureFormat::RGBA8Unorm;
+
+wgpu::Texture Create2DTexture(wgpu::Device& device) {
+ wgpu::TextureDescriptor descriptor;
+ descriptor.dimension = wgpu::TextureDimension::e2D;
+ descriptor.size.width = kWidth;
+ descriptor.size.height = kHeight;
+ descriptor.size.depthOrArrayLayers = kDefaultLayerCount;
+ descriptor.sampleCount = kDefaultSampleCount;
+ descriptor.format = kDefaultTextureFormat;
+ descriptor.mipLevelCount = kDefaultMipLevels;
+ descriptor.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment;
+ return device.CreateTexture(&descriptor);
+}
+
+wgpu::TextureViewDescriptor CreateDefaultViewDescriptor(wgpu::TextureViewDimension dimension) {
+ wgpu::TextureViewDescriptor descriptor;
+ descriptor.format = kDefaultTextureFormat;
+ descriptor.dimension = dimension;
+ descriptor.baseMipLevel = 0;
+ if (dimension != wgpu::TextureViewDimension::e1D) {
+ descriptor.mipLevelCount = kDefaultMipLevels;
+ }
+ descriptor.baseArrayLayer = 0;
+ descriptor.arrayLayerCount = kDefaultLayerCount;
+ return descriptor;
+}
+
+class YCbCrInfoTest : public DawnTest {
protected:
void SetUp() override {
DawnTest::SetUp();
@@ -56,27 +89,27 @@
}
};
-// Test that it is possible to create the sampler with ycbcr sampler descriptor.
-TEST_P(YCbCrSamplerTest, YCbCrSamplerValidWhenFeatureEnabled) {
+// Test that it is possible to create the sampler with ycbcr vulkan descriptor.
+TEST_P(YCbCrInfoTest, YCbCrSamplerValidWhenFeatureEnabled) {
wgpu::SamplerDescriptor samplerDesc = {};
- native::vulkan::YCbCrVulkanDescriptor samplerYCbCrDesc = {};
- samplerYCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
- samplerYCbCrDesc.vulkanYCbCrInfo.pNext = nullptr;
- samplerYCbCrDesc.vulkanYCbCrInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
+ native::vulkan::YCbCrVulkanDescriptor yCbCrDesc = {};
+ yCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
+ yCbCrDesc.vulkanYCbCrInfo.pNext = nullptr;
+ yCbCrDesc.vulkanYCbCrInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
- samplerDesc.nextInChain = &samplerYCbCrDesc;
+ samplerDesc.nextInChain = &yCbCrDesc;
device.CreateSampler(&samplerDesc);
}
-// Test that it is possible to create the sampler with ycbcr sampler descriptor with only vulkan
+// Test that it is possible to create the sampler with ycbcr vulkan descriptor with only vulkan
// format set.
-TEST_P(YCbCrSamplerTest, YCbCrSamplerValidWithOnlyVkFormat) {
+TEST_P(YCbCrInfoTest, YCbCrSamplerValidWithOnlyVkFormat) {
wgpu::SamplerDescriptor samplerDesc = {};
- native::vulkan::YCbCrVulkanDescriptor samplerYCbCrDesc = {};
- samplerYCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
- samplerYCbCrDesc.vulkanYCbCrInfo.pNext = nullptr;
- samplerYCbCrDesc.vulkanYCbCrInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
+ native::vulkan::YCbCrVulkanDescriptor yCbCrDesc = {};
+ yCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
+ yCbCrDesc.vulkanYCbCrInfo.pNext = nullptr;
+ yCbCrDesc.vulkanYCbCrInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
#if DAWN_PLATFORM_IS(ANDROID)
VkExternalFormatANDROID vulkanExternalFormat = {};
@@ -85,23 +118,23 @@
// format is set as VK_FORMAT.
vulkanExternalFormat.externalFormat = 0;
- samplerYCbCrDesc.vulkanYCbCrInfo.pNext = &vulkanExternalFormat;
+ yCbCrDesc.vulkanYCbCrInfo.pNext = &vulkanExternalFormat;
#endif // DAWN_PLATFORM_IS(ANDROID)
- samplerDesc.nextInChain = &samplerYCbCrDesc;
+ samplerDesc.nextInChain = &yCbCrDesc;
device.CreateSampler(&samplerDesc);
}
-// Test that it is possible to create the sampler with ycbcr sampler descriptor with only external
+// Test that it is possible to create the sampler with ycbcr vulkan descriptor with only external
// format set.
-TEST_P(YCbCrSamplerTest, YCbCrSamplerValidWithOnlyExternalFormat) {
+TEST_P(YCbCrInfoTest, YCbCrSamplerValidWithOnlyExternalFormat) {
wgpu::SamplerDescriptor samplerDesc = {};
- native::vulkan::YCbCrVulkanDescriptor samplerYCbCrDesc = {};
- samplerYCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
- samplerYCbCrDesc.vulkanYCbCrInfo.pNext = nullptr;
+ native::vulkan::YCbCrVulkanDescriptor yCbCrDesc = {};
+ yCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
+ yCbCrDesc.vulkanYCbCrInfo.pNext = nullptr;
// format is set as externalFormat.
- samplerYCbCrDesc.vulkanYCbCrInfo.format = VK_FORMAT_UNDEFINED;
+ yCbCrDesc.vulkanYCbCrInfo.format = VK_FORMAT_UNDEFINED;
#if DAWN_PLATFORM_IS(ANDROID)
VkExternalFormatANDROID vulkanExternalFormat = {};
@@ -109,22 +142,22 @@
vulkanExternalFormat.pNext = nullptr;
vulkanExternalFormat.externalFormat = 5;
- samplerYCbCrDesc.vulkanYCbCrInfo.pNext = &vulkanExternalFormat;
+ yCbCrDesc.vulkanYCbCrInfo.pNext = &vulkanExternalFormat;
#endif // DAWN_PLATFORM_IS(ANDROID)
- samplerDesc.nextInChain = &samplerYCbCrDesc;
+ samplerDesc.nextInChain = &yCbCrDesc;
device.CreateSampler(&samplerDesc);
}
-// Test that it is not possible to create the sampler with ycbcr sampler descriptor and no format
+// Test that it is NOT possible to create the sampler with ycbcr vulkan descriptor and no format
// set.
-TEST_P(YCbCrSamplerTest, YCbCrSamplerInvalidWithNoFormat) {
+TEST_P(YCbCrInfoTest, YCbCrSamplerInvalidWithNoFormat) {
wgpu::SamplerDescriptor samplerDesc = {};
- native::vulkan::YCbCrVulkanDescriptor samplerYCbCrDesc = {};
- samplerYCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
- samplerYCbCrDesc.vulkanYCbCrInfo.pNext = nullptr;
- samplerYCbCrDesc.vulkanYCbCrInfo.format = VK_FORMAT_UNDEFINED;
+ native::vulkan::YCbCrVulkanDescriptor yCbCrDesc = {};
+ yCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
+ yCbCrDesc.vulkanYCbCrInfo.pNext = nullptr;
+ yCbCrDesc.vulkanYCbCrInfo.format = VK_FORMAT_UNDEFINED;
#if DAWN_PLATFORM_IS(ANDROID)
VkExternalFormatANDROID vulkanExternalFormat = {};
@@ -132,15 +165,119 @@
vulkanExternalFormat.pNext = nullptr;
vulkanExternalFormat.externalFormat = 0;
- samplerYCbCrDesc.vulkanYCbCrInfo.pNext = &vulkanExternalFormat;
+ yCbCrDesc.vulkanYCbCrInfo.pNext = &vulkanExternalFormat;
#endif // DAWN_PLATFORM_IS(ANDROID)
- samplerDesc.nextInChain = &samplerYCbCrDesc;
+ samplerDesc.nextInChain = &yCbCrDesc;
ASSERT_DEVICE_ERROR(device.CreateSampler(&samplerDesc));
}
-DAWN_INSTANTIATE_TEST(YCbCrSamplerTest, VulkanBackend());
+// Test that it is possible to create texture view with ycbcr vulkan descriptor.
+TEST_P(YCbCrInfoTest, YCbCrTextureViewValidWhenFeatureEnabled) {
+ wgpu::Texture texture = Create2DTexture(device);
+
+ wgpu::TextureViewDescriptor descriptor =
+ CreateDefaultViewDescriptor(wgpu::TextureViewDimension::e2D);
+ descriptor.arrayLayerCount = 1;
+
+ native::vulkan::YCbCrVulkanDescriptor yCbCrDesc = {};
+ yCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
+ yCbCrDesc.vulkanYCbCrInfo.pNext = nullptr;
+ yCbCrDesc.vulkanYCbCrInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
+
+ descriptor.nextInChain = &yCbCrDesc;
+
+ texture.CreateView(&descriptor);
+}
+
+// Test that it is possible to create texture view with ycbcr vulkan descriptor with only vulkan
+// format set.
+TEST_P(YCbCrInfoTest, YCbCrTextureViewValidWithOnlyVkFormat) {
+ wgpu::Texture texture = Create2DTexture(device);
+
+ wgpu::TextureViewDescriptor descriptor =
+ CreateDefaultViewDescriptor(wgpu::TextureViewDimension::e2D);
+ descriptor.arrayLayerCount = 1;
+
+ native::vulkan::YCbCrVulkanDescriptor yCbCrDesc = {};
+ yCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
+ yCbCrDesc.vulkanYCbCrInfo.pNext = nullptr;
+ yCbCrDesc.vulkanYCbCrInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
+
+#if DAWN_PLATFORM_IS(ANDROID)
+ VkExternalFormatANDROID vulkanExternalFormat = {};
+ vulkanExternalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID;
+ vulkanExternalFormat.pNext = nullptr;
+ // format is set as VK_FORMAT.
+ vulkanExternalFormat.externalFormat = 0;
+
+ yCbCrDesc.vulkanYCbCrInfo.pNext = &vulkanExternalFormat;
+#endif // DAWN_PLATFORM_IS(ANDROID)
+
+ descriptor.nextInChain = &yCbCrDesc;
+
+ texture.CreateView(&descriptor);
+}
+
+// Test that it is possible to create texture view with ycbcr vulkan descriptor with only external
+// format set.
+TEST_P(YCbCrInfoTest, YCbCrTextureViewValidWithOnlyExternalFormat) {
+ wgpu::Texture texture = Create2DTexture(device);
+
+ wgpu::TextureViewDescriptor descriptor =
+ CreateDefaultViewDescriptor(wgpu::TextureViewDimension::e2D);
+ descriptor.arrayLayerCount = 1;
+
+ native::vulkan::YCbCrVulkanDescriptor yCbCrDesc = {};
+ yCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
+ yCbCrDesc.vulkanYCbCrInfo.pNext = nullptr;
+ // format is set as externalFormat.
+ yCbCrDesc.vulkanYCbCrInfo.format = VK_FORMAT_UNDEFINED;
+
+#if DAWN_PLATFORM_IS(ANDROID)
+ VkExternalFormatANDROID vulkanExternalFormat = {};
+ vulkanExternalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID;
+ vulkanExternalFormat.pNext = nullptr;
+ vulkanExternalFormat.externalFormat = 5;
+
+ yCbCrDesc.vulkanYCbCrInfo.pNext = &vulkanExternalFormat;
+#endif // DAWN_PLATFORM_IS(ANDROID)
+
+ descriptor.nextInChain = &yCbCrDesc;
+
+ texture.CreateView(&descriptor);
+}
+
+// Test that it is NOT possible to create texture view with ycbcr vulkan descriptor and no format
+// set.
+TEST_P(YCbCrInfoTest, YCbCrTextureViewInvalidWithNoFormat) {
+ wgpu::Texture texture = Create2DTexture(device);
+
+ wgpu::TextureViewDescriptor descriptor =
+ CreateDefaultViewDescriptor(wgpu::TextureViewDimension::e2D);
+ descriptor.arrayLayerCount = 1;
+
+ native::vulkan::YCbCrVulkanDescriptor yCbCrDesc = {};
+ yCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
+ yCbCrDesc.vulkanYCbCrInfo.pNext = nullptr;
+ yCbCrDesc.vulkanYCbCrInfo.format = VK_FORMAT_UNDEFINED;
+
+#if DAWN_PLATFORM_IS(ANDROID)
+ VkExternalFormatANDROID vulkanExternalFormat = {};
+ vulkanExternalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID;
+ vulkanExternalFormat.pNext = nullptr;
+ vulkanExternalFormat.externalFormat = 0;
+
+ yCbCrDesc.vulkanYCbCrInfo.pNext = &vulkanExternalFormat;
+#endif // DAWN_PLATFORM_IS(ANDROID)
+
+ descriptor.nextInChain = &yCbCrDesc;
+
+ ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor));
+}
+
+DAWN_INSTANTIATE_TEST(YCbCrInfoTest, VulkanBackend());
} // anonymous namespace
} // namespace dawn
diff --git a/src/dawn/tests/unittests/ChainUtilsTests.cpp b/src/dawn/tests/unittests/ChainUtilsTests.cpp
index 9a0ed6e..b78d21f 100644
--- a/src/dawn/tests/unittests/ChainUtilsTests.cpp
+++ b/src/dawn/tests/unittests/ChainUtilsTests.cpp
@@ -40,11 +40,9 @@
// values should be nullptr.
TEST(ChainUtilsTests, ValidateAndUnpackEmpty) {
{
- // TextureViewDescriptor (as of when this test was written) does not have any valid chains
- // in the JSON nor via additional extensions.
+ // TextureViewDescriptor has at least 1 valid chain extension..
TextureViewDescriptor desc;
auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess();
- static_assert(std::tuple_size_v<decltype(unpacked)::TupleType> == 0);
EXPECT_TRUE(unpacked.Empty());
}
{
diff --git a/src/dawn/tests/unittests/validation/YCbCrSamplerValidationTests.cpp b/src/dawn/tests/unittests/validation/YCbCrSamplerValidationTests.cpp
index 8711f4c..43f384e 100644
--- a/src/dawn/tests/unittests/validation/YCbCrSamplerValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/YCbCrSamplerValidationTests.cpp
@@ -35,24 +35,71 @@
namespace dawn {
namespace {
-class YCbCrSamplerWithoutFeatureValidationTest : public ValidationTest {
+constexpr uint32_t kWidth = 32u;
+constexpr uint32_t kHeight = 32u;
+constexpr uint32_t kDefaultMipLevels = 1u;
+constexpr uint32_t kDefaultLayerCount = 1u;
+constexpr uint32_t kDefaultSampleCount = 1u;
+constexpr wgpu::TextureFormat kDefaultTextureFormat = wgpu::TextureFormat::RGBA8Unorm;
+
+wgpu::Texture Create2DTexture(wgpu::Device& device) {
+ wgpu::TextureDescriptor descriptor;
+ descriptor.dimension = wgpu::TextureDimension::e2D;
+ descriptor.size.width = kWidth;
+ descriptor.size.height = kHeight;
+ descriptor.size.depthOrArrayLayers = kDefaultLayerCount;
+ descriptor.sampleCount = kDefaultSampleCount;
+ descriptor.format = kDefaultTextureFormat;
+ descriptor.mipLevelCount = kDefaultMipLevels;
+ descriptor.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment;
+ return device.CreateTexture(&descriptor);
+}
+
+wgpu::TextureViewDescriptor CreateDefaultViewDescriptor(wgpu::TextureViewDimension dimension) {
+ wgpu::TextureViewDescriptor descriptor;
+ descriptor.format = kDefaultTextureFormat;
+ descriptor.dimension = dimension;
+ descriptor.baseMipLevel = 0;
+ if (dimension != wgpu::TextureViewDimension::e1D) {
+ descriptor.mipLevelCount = kDefaultMipLevels;
+ }
+ descriptor.baseArrayLayer = 0;
+ descriptor.arrayLayerCount = kDefaultLayerCount;
+ return descriptor;
+}
+
+class YCbCrInfoWithoutFeatureValidationTest : public ValidationTest {
void SetUp() override {
ValidationTest::SetUp();
DAWN_SKIP_TEST_IF(UsesWire());
}
};
-// Tests that creating a sampler with a valid ycbcr sampler descriptor raises an error
+// Tests that creating a sampler with a valid ycbcr vulkan descriptor raises an error
// if the required feature is not enabled.
-TEST_F(YCbCrSamplerWithoutFeatureValidationTest, YCbCrSamplerNotSupported) {
+TEST_F(YCbCrInfoWithoutFeatureValidationTest, YCbCrSamplerNotSupported) {
wgpu::SamplerDescriptor samplerDesc = {};
- native::vulkan::YCbCrVulkanDescriptor samplerYCbCrDesc = {};
- samplerDesc.nextInChain = &samplerYCbCrDesc;
+ native::vulkan::YCbCrVulkanDescriptor yCbCrDesc = {};
+ samplerDesc.nextInChain = &yCbCrDesc;
ASSERT_DEVICE_ERROR(device.CreateSampler(&samplerDesc));
}
-class YCbCrSamplerWithFeatureValidationTest : public YCbCrSamplerWithoutFeatureValidationTest {
+// Tests that creating a texture view with a valid ycbcr vulkan descriptor raises an error
+// if the required feature is not enabled.
+TEST_F(YCbCrInfoWithoutFeatureValidationTest, YCbCrTextureViewNotSupported) {
+ wgpu::Texture texture = Create2DTexture(device);
+
+ wgpu::TextureViewDescriptor descriptor =
+ CreateDefaultViewDescriptor(wgpu::TextureViewDimension::e2D);
+ descriptor.arrayLayerCount = 1;
+ native::vulkan::YCbCrVulkanDescriptor yCbCrDesc = {};
+ descriptor.nextInChain = &yCbCrDesc;
+
+ ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor));
+}
+
+class YCbCrInfoWithFeatureValidationTest : public YCbCrInfoWithoutFeatureValidationTest {
WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
wgpu::DeviceDescriptor descriptor) override {
wgpu::FeatureName requiredFeatures[2] = {wgpu::FeatureName::StaticSamplers,
@@ -63,20 +110,20 @@
}
};
-// Tests that creating a sampler with a valid ycbcr sampler descriptor succeeds if the
+// Tests that creating a sampler with a valid ycbcr vulkan descriptor succeeds if the
// required feature is enabled.
-TEST_F(YCbCrSamplerWithFeatureValidationTest, YCbCrSamplerSupported) {
+TEST_F(YCbCrInfoWithFeatureValidationTest, YCbCrSamplerSupported) {
wgpu::SamplerDescriptor samplerDesc = {};
- native::vulkan::YCbCrVulkanDescriptor samplerYCbCrDesc = {};
- samplerYCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
- samplerDesc.nextInChain = &samplerYCbCrDesc;
+ native::vulkan::YCbCrVulkanDescriptor yCbCrDesc = {};
+ yCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
+ samplerDesc.nextInChain = &yCbCrDesc;
device.CreateSampler(&samplerDesc);
}
// Tests that creating a bind group layout with a valid static sampler succeeds if the
// required feature is enabled.
-TEST_F(YCbCrSamplerWithFeatureValidationTest, CreateBindGroupWithYCbCrSamplerSupported) {
+TEST_F(YCbCrInfoWithFeatureValidationTest, CreateBindGroupWithYCbCrSamplerSupported) {
wgpu::BindGroupLayoutEntry binding = {};
binding.binding = 0;
wgpu::StaticSamplerBindingLayout staticSamplerBinding = {};
@@ -104,7 +151,7 @@
// Verifies that creation of a correctly-specified bind group for a layout that
// has a sampler and a static sampler succeeds.
-TEST_F(YCbCrSamplerWithFeatureValidationTest, CreateBindGroupWithSamplerAndStaticSamplerSupported) {
+TEST_F(YCbCrInfoWithFeatureValidationTest, CreateBindGroupWithSamplerAndStaticSamplerSupported) {
std::vector<wgpu::BindGroupLayoutEntry> entries;
wgpu::BindGroupLayoutEntry binding0 = {};
@@ -139,7 +186,7 @@
// Verifies that creation of a bind group with the correct number of entries for a layout that has a
// sampler and a static sampler raises an error if the entry is specified at the
// index of the static sampler rather than that of the sampler.
-TEST_F(YCbCrSamplerWithFeatureValidationTest, BindGroupCreationForSamplerBindingTypeCausesError) {
+TEST_F(YCbCrInfoWithFeatureValidationTest, BindGroupCreationForSamplerBindingTypeCausesError) {
std::vector<wgpu::BindGroupLayoutEntry> entries;
wgpu::BindGroupLayoutEntry binding0 = {};
@@ -172,5 +219,33 @@
utils::MakeBindGroup(device, layout, {{1, device.CreateSampler(&samplerDesc0)}}));
}
+// Tests that creating a texture view with a valid ycbcr vulkan descriptor succeeds if the
+// required feature is enabled.
+TEST_F(YCbCrInfoWithFeatureValidationTest, YCbCrTextureViewSupported) {
+ wgpu::Texture texture = Create2DTexture(device);
+
+ wgpu::TextureViewDescriptor descriptor =
+ CreateDefaultViewDescriptor(wgpu::TextureViewDimension::e2D);
+ descriptor.arrayLayerCount = 1;
+ native::vulkan::YCbCrVulkanDescriptor yCbCrDesc = {};
+ yCbCrDesc.vulkanYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
+ descriptor.nextInChain = &yCbCrDesc;
+
+ texture.CreateView(&descriptor);
+}
+
+// TODO(crbug.com/dawn/2476): Add test validating binding fails if sampler or texture view is not
+// YCbCr
+// TODO(crbug.com/dawn/2476): Add test validating binding passes if sampler and texture view is
+// YCbCr
+// TODO(crbug.com/dawn/2476): Add test validating binding fails if texture view ycbcr info is
+// different from that on sampler
+// TODO(crbug.com/dawn/2476): Add test validating binding passes if texture view ycbcr info is same
+// as that on sampler
+// TODO(crbug.com/dawn/2476): Add validation that mipLevel, arrayLayers are always 1 along with 2D
+// view dimension (see
+// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCreateInfo.html) with
+// YCbCr and tests for it.
+
} // anonymous namespace
} // namespace dawn