Implement WGPUTextureDescriptor.viewFormats and sampling reinterpretation

Reinterpretation for render/resolve attachments not yet implemented.

Bug: dawn:1276
Change-Id: I43d73ce5c943c4ba49df06c6865867f378f2de25
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/84280
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
Austin Eng 2022-03-31 03:53:04 +00:00 committed by Dawn LUCI CQ
parent 10ec4ee7ef
commit 8e02ebf6c6
18 changed files with 555 additions and 91 deletions

View File

@ -2536,8 +2536,8 @@
{"name": "format", "type": "texture format"},
{"name": "mip level count", "type": "uint32_t", "default": 1},
{"name": "sample count", "type": "uint32_t", "default": 1},
{"name": "view format count", "type": "uint32_t", "default": 0, "tags": ["upstream"]},
{"name": "view formats", "type": "texture format", "annotation": "const*", "length": "view format count", "tags": ["upstream"]}
{"name": "view format count", "type": "uint32_t", "default": 0},
{"name": "view formats", "type": "texture format", "annotation": "const*", "length": "view format count"}
]
},
"texture dimension": {

View File

@ -34,6 +34,8 @@ namespace ityp {
}
public:
using reference = typename Base::reference;
constexpr bitset() noexcept : Base() {
}

View File

@ -681,7 +681,7 @@ namespace dawn::native {
}
ResultOrError<const Format*> DeviceBase::GetInternalFormat(wgpu::TextureFormat format) const {
size_t index = ComputeFormatIndex(format);
FormatIndex index = ComputeFormatIndex(format);
DAWN_INVALID_IF(index >= mFormatTable.size(), "Unknown texture format %s.", format);
const Format* internalFormat = &mFormatTable[index];
@ -691,7 +691,13 @@ namespace dawn::native {
}
const Format& DeviceBase::GetValidInternalFormat(wgpu::TextureFormat format) const {
size_t index = ComputeFormatIndex(format);
FormatIndex index = ComputeFormatIndex(format);
ASSERT(index < mFormatTable.size());
ASSERT(mFormatTable[index].isSupported);
return mFormatTable[index];
}
const Format& DeviceBase::GetValidInternalFormat(FormatIndex index) const {
ASSERT(index < mFormatTable.size());
ASSERT(mFormatTable[index].isSupported);
return mFormatTable[index];

View File

@ -137,6 +137,7 @@ namespace dawn::native {
// valid and supported.
// The reference returned has the same lifetime as the device.
const Format& GetValidInternalFormat(wgpu::TextureFormat format) const;
const Format& GetValidInternalFormat(FormatIndex formatIndex) const;
virtual ResultOrError<Ref<CommandBufferBase>> CreateCommandBuffer(
CommandEncoder* encoder,

View File

@ -100,6 +100,12 @@ namespace dawn::native {
}
bool Format::CopyCompatibleWith(const Format& format) const {
// TODO(crbug.com/dawn/1332): Add a Format compatibility matrix.
return baseFormat == format.baseFormat;
}
bool Format::ViewCompatibleWith(const Format& format) const {
// TODO(crbug.com/dawn/1332): Add a Format compatibility matrix.
return baseFormat == format.baseFormat;
}
@ -115,31 +121,41 @@ namespace dawn::native {
return aspectInfo[aspectIndex];
}
size_t Format::GetIndex() const {
FormatIndex Format::GetIndex() const {
return ComputeFormatIndex(format);
}
// FormatSet implementation
bool FormatSet::operator[](const Format& format) const {
return Base::operator[](format.GetIndex());
}
typename std::bitset<kKnownFormatCount>::reference FormatSet::operator[](const Format& format) {
return Base::operator[](format.GetIndex());
}
// Implementation details of the format table of the DeviceBase
// For the enum for formats are packed but this might change when we have a broader feature
// mechanism for webgpu.h. Formats start at 1 because 0 is the undefined format.
size_t ComputeFormatIndex(wgpu::TextureFormat format) {
FormatIndex ComputeFormatIndex(wgpu::TextureFormat format) {
// This takes advantage of overflows to make the index of TextureFormat::Undefined outside
// of the range of the FormatTable.
static_assert(static_cast<uint32_t>(wgpu::TextureFormat::Undefined) - 1 >
kKnownFormatCount);
return static_cast<size_t>(static_cast<uint32_t>(format) - 1);
return static_cast<FormatIndex>(static_cast<uint32_t>(format) - 1);
}
FormatTable BuildFormatTable(const DeviceBase* device) {
FormatTable table;
std::bitset<kKnownFormatCount> formatsSet;
FormatSet formatsSet;
static constexpr SampleTypeBit kAnyFloat =
SampleTypeBit::Float | SampleTypeBit::UnfilterableFloat;
auto AddFormat = [&table, &formatsSet](Format format) {
size_t index = ComputeFormatIndex(format.format);
FormatIndex index = ComputeFormatIndex(format.format);
ASSERT(index < table.size());
// This checks that each format is set at most once, the first part of checking that all
@ -211,11 +227,11 @@ namespace dawn::native {
AddFormat(internalFormat);
};
auto AddDepthFormat = [&AddFormat](
wgpu::TextureFormat format, uint32_t byteSize, bool isSupported,
wgpu::TextureFormat baseFormat = wgpu::TextureFormat::Undefined) {
auto AddDepthFormat = [&AddFormat](wgpu::TextureFormat format, uint32_t byteSize,
bool isSupported) {
Format internalFormat;
internalFormat.format = format;
internalFormat.baseFormat = format;
internalFormat.isRenderable = true;
internalFormat.isCompressed = false;
internalFormat.isSupported = isSupported;
@ -225,13 +241,6 @@ namespace dawn::native {
internalFormat.aspects = Aspect::Depth;
internalFormat.componentCount = 1;
// Default baseFormat of each depth formats should be themselves.
if (baseFormat == wgpu::TextureFormat::Undefined) {
internalFormat.baseFormat = format;
} else {
internalFormat.baseFormat = baseFormat;
}
AspectInfo* firstAspect = internalFormat.aspectInfo.data();
firstAspect->block.byteSize = byteSize;
firstAspect->block.width = 1;
@ -242,11 +251,10 @@ namespace dawn::native {
AddFormat(internalFormat);
};
auto AddStencilFormat = [&AddFormat](wgpu::TextureFormat format, bool isSupported,
wgpu::TextureFormat baseFormat =
wgpu::TextureFormat::Undefined) {
auto AddStencilFormat = [&AddFormat](wgpu::TextureFormat format, bool isSupported) {
Format internalFormat;
internalFormat.format = format;
internalFormat.baseFormat = format;
internalFormat.isRenderable = true;
internalFormat.isCompressed = false;
internalFormat.isSupported = isSupported;
@ -255,14 +263,6 @@ namespace dawn::native {
internalFormat.supportsResolveTarget = false;
internalFormat.aspects = Aspect::Stencil;
internalFormat.componentCount = 1;
internalFormat.baseFormat = baseFormat;
// Default baseFormat of each stencil formats should be themselves.
if (baseFormat == wgpu::TextureFormat::Undefined) {
internalFormat.baseFormat = format;
} else {
internalFormat.baseFormat = baseFormat;
}
// Duplicate the data for the stencil aspect in both the first and second aspect info.
// - aspectInfo[0] is used by AddMultiAspectFormat to copy the info for the whole
@ -319,10 +319,10 @@ namespace dawn::native {
[&AddFormat, &table](wgpu::TextureFormat format, Aspect aspects,
wgpu::TextureFormat firstFormat, wgpu::TextureFormat secondFormat,
bool isRenderable, bool isSupported, bool supportsMultisample,
uint8_t componentCount,
wgpu::TextureFormat baseFormat = wgpu::TextureFormat::Undefined) {
uint8_t componentCount) {
Format internalFormat;
internalFormat.format = format;
internalFormat.baseFormat = format;
internalFormat.isRenderable = isRenderable;
internalFormat.isCompressed = false;
internalFormat.isSupported = isSupported;
@ -332,18 +332,11 @@ namespace dawn::native {
internalFormat.aspects = aspects;
internalFormat.componentCount = componentCount;
// Default baseFormat of each multi aspect formats should be themselves.
if (baseFormat == wgpu::TextureFormat::Undefined) {
internalFormat.baseFormat = format;
} else {
internalFormat.baseFormat = baseFormat;
}
// Multi aspect formats just copy information about single-aspect formats. This
// means that the single-plane formats must have been added before multi-aspect
// ones. (it is ASSERTed below).
const size_t firstFormatIndex = ComputeFormatIndex(firstFormat);
const size_t secondFormatIndex = ComputeFormatIndex(secondFormat);
const FormatIndex firstFormatIndex = ComputeFormatIndex(firstFormat);
const FormatIndex secondFormatIndex = ComputeFormatIndex(secondFormat);
ASSERT(table[firstFormatIndex].aspectInfo[0].format !=
wgpu::TextureFormat::Undefined);

View File

@ -17,6 +17,8 @@
#include "dawn/native/dawn_platform.h"
#include "dawn/common/TypedInteger.h"
#include "dawn/common/ityp_array.h"
#include "dawn/common/ityp_bitset.h"
#include "dawn/native/EnumClassBitmasks.h"
#include "dawn/native/Error.h"
@ -77,15 +79,18 @@ namespace dawn::native {
// The number of formats Dawn knows about. Asserts in BuildFormatTable ensure that this is the
// exact number of known format.
static constexpr size_t kKnownFormatCount = 96;
static constexpr uint32_t kKnownFormatCount = 96;
using FormatIndex = TypedInteger<struct FormatIndexT, uint32_t>;
struct Format;
using FormatTable = std::array<Format, kKnownFormatCount>;
using FormatTable = ityp::array<FormatIndex, Format, kKnownFormatCount>;
// A wgpu::TextureFormat along with all the information about it necessary for validation.
struct Format {
wgpu::TextureFormat format;
// TODO(crbug.com/dawn/1332): These members could be stored in a Format capability matrix.
bool isRenderable;
bool isCompressed;
// A format can be known but not supported because it is part of a disabled extension.
@ -111,16 +116,21 @@ namespace dawn::native {
// The index of the format in the list of all known formats: a unique number for each format
// in [0, kKnownFormatCount)
size_t GetIndex() const;
FormatIndex GetIndex() const;
// baseFormat represents the memory layout of the format.
// If two formats has the same baseFormat, they could copy to each other.
// If two formats has the same baseFormat, they could copy to and be viewed as the other
// format. Currently two formats have the same baseFormat if they differ only in sRGB-ness.
wgpu::TextureFormat baseFormat;
// CopyCompatibleWith() returns true if the input format has the same baseFormat
// with current format.
// Returns true if the formats are copy compatible.
// Currently means they differ only in sRGB-ness.
bool CopyCompatibleWith(const Format& format) const;
// Returns true if the formats are texture view format compatible.
// Currently means they differ only in sRGB-ness.
bool ViewCompatibleWith(const Format& format) const;
private:
// Used to store the aspectInfo for one or more planes. For single plane "color" formats,
// only the first aspect info or aspectInfo[0] is valid. For depth-stencil, the first aspect
@ -131,10 +141,21 @@ namespace dawn::native {
friend FormatTable BuildFormatTable(const DeviceBase* device);
};
class FormatSet : public ityp::bitset<FormatIndex, kKnownFormatCount> {
using Base = ityp::bitset<FormatIndex, kKnownFormatCount>;
public:
using Base::Base;
using Base::operator[];
bool operator[](const Format& format) const;
typename Base::reference operator[](const Format& format);
};
// Implementation details of the format table in the device.
// Returns the index of a format in the FormatTable.
size_t ComputeFormatIndex(wgpu::TextureFormat format);
FormatIndex ComputeFormatIndex(wgpu::TextureFormat format);
// Builds the format table with the extensions enabled on the device.
FormatTable BuildFormatTable(const DeviceBase* device);

View File

@ -29,21 +29,68 @@
namespace dawn::native {
namespace {
// WebGPU currently does not have texture format reinterpretation. If it does, the
// code to check for it might go here.
MaybeError ValidateTextureViewFormatCompatibility(const TextureBase* texture,
const TextureViewDescriptor* descriptor) {
if (texture->GetFormat().format != descriptor->format) {
if (descriptor->aspect != wgpu::TextureAspect::All &&
texture->GetFormat().GetAspectInfo(descriptor->aspect).format ==
descriptor->format) {
MaybeError ValidateTextureViewFormatCompatibility(const DeviceBase* device,
const Format& format,
wgpu::TextureFormat viewFormatEnum) {
const Format* viewFormat;
DAWN_TRY_ASSIGN(viewFormat, device->GetInternalFormat(viewFormatEnum));
DAWN_INVALID_IF(!format.ViewCompatibleWith(*viewFormat),
"The texture view format (%s) is not texture view format compatible "
"with the texture format (%s).",
viewFormatEnum, format.format);
return {};
}
return DAWN_VALIDATION_ERROR(
"The format of texture view is not compatible to the original texture");
MaybeError ValidateCanViewTextureAs(const DeviceBase* device,
const TextureBase* texture,
const Format& viewFormat,
wgpu::TextureAspect aspect) {
const Format& format = texture->GetFormat();
if (format.format == viewFormat.format) {
return {};
}
if (aspect != wgpu::TextureAspect::All) {
wgpu::TextureFormat aspectFormat = format.GetAspectInfo(aspect).format;
if (viewFormat.format == aspectFormat) {
return {};
} else {
return DAWN_FORMAT_VALIDATION_ERROR(
"The view format (%s) is not compatible with %s of %s (%s).",
viewFormat.format, aspect, format.format, aspectFormat);
}
}
const FormatSet& compatibleViewFormats = texture->GetViewFormats();
if (compatibleViewFormats[viewFormat]) {
// Validation of this list is done on texture creation, so we don't need to
// handle the case where a format is in the list, but not compatible.
return {};
}
// |viewFormat| is not in the list. Check compatibility to generate an error message
// depending on whether it could be compatible, but needs to be explicitly listed,
// or it could never be compatible.
if (!format.ViewCompatibleWith(viewFormat)) {
// The view format isn't compatible with the format at all. Return an error
// that indicates this, in addition to reporting that it's missing from the
// list.
return DAWN_FORMAT_VALIDATION_ERROR(
"The texture view format (%s) is not compatible with the "
"texture format (%s)."
"The formats must be compatible, and the view format "
"must be passed in the list of view formats on texture creation.",
viewFormat.format, format.format);
} else {
// The view format is compatible, but not in the list.
return DAWN_FORMAT_VALIDATION_ERROR(
"%s was not created with the texture view format (%s) "
"in the list of compatible view formats.",
texture, viewFormat.format);
}
return {};
}
@ -295,6 +342,12 @@ namespace dawn::native {
const Format* format;
DAWN_TRY_ASSIGN(format, device->GetInternalFormat(descriptor->format));
for (uint32_t i = 0; i < descriptor->viewFormatCount; ++i) {
DAWN_TRY_CONTEXT(
ValidateTextureViewFormatCompatibility(device, *format, descriptor->viewFormats[i]),
"validating viewFormats[%u]", i);
}
wgpu::TextureUsage usage = descriptor->usage;
if (internalUsageDesc != nullptr) {
usage |= internalUsageDesc->internalUsage;
@ -357,10 +410,13 @@ namespace dawn::native {
DAWN_TRY(ValidateTextureFormat(descriptor->format));
DAWN_TRY(ValidateTextureAspect(descriptor->aspect));
const Format& format = texture->GetFormat();
const Format& viewFormat = device->GetValidInternalFormat(descriptor->format);
DAWN_INVALID_IF(
SelectFormatAspects(texture->GetFormat(), descriptor->aspect) == Aspect::None,
SelectFormatAspects(format, descriptor->aspect) == Aspect::None,
"Texture format (%s) does not have the texture view's selected aspect (%s).",
texture->GetFormat().format, descriptor->aspect);
format.format, descriptor->aspect);
DAWN_INVALID_IF(descriptor->arrayLayerCount == 0 || descriptor->mipLevelCount == 0,
"The texture view's arrayLayerCount (%u) or mipLevelCount (%u) is zero.",
@ -380,7 +436,7 @@ namespace dawn::native {
"texture's mip level count (%u).",
descriptor->baseMipLevel, descriptor->mipLevelCount, texture->GetNumMipLevels());
DAWN_TRY(ValidateTextureViewFormatCompatibility(texture, descriptor));
DAWN_TRY(ValidateCanViewTextureAs(device, texture, viewFormat, descriptor->aspect));
DAWN_TRY(ValidateTextureViewDimensionCompatibility(texture, descriptor));
return {};
@ -476,6 +532,15 @@ namespace dawn::native {
mMipLevelCount * GetArrayLayers() * GetAspectCount(mFormat.aspects);
mIsSubresourceContentInitializedAtIndex = std::vector<bool>(subresourceCount, false);
for (uint32_t i = 0; i < descriptor->viewFormatCount; ++i) {
if (descriptor->viewFormats[i] == descriptor->format) {
// Skip our own format, so the backends don't allocate the texture for
// reinterpretation if it's not needed.
continue;
}
mViewFormats[device->GetValidInternalFormat(descriptor->viewFormats[i])] = true;
}
const DawnTextureInternalUsageDescriptor* internalUsageDesc = nullptr;
FindInChain(descriptor->nextInChain, &internalUsageDesc);
if (internalUsageDesc != nullptr) {
@ -517,6 +582,10 @@ namespace dawn::native {
ASSERT(!IsError());
return mFormat;
}
const FormatSet& TextureBase::GetViewFormats() const {
ASSERT(!IsError());
return mViewFormats;
}
const Extent3D& TextureBase::GetSize() const {
ASSERT(!IsError());
return mSize;

View File

@ -18,6 +18,7 @@
#include "dawn/common/ityp_array.h"
#include "dawn/common/ityp_bitset.h"
#include "dawn/native/Error.h"
#include "dawn/native/Format.h"
#include "dawn/native/Forward.h"
#include "dawn/native/ObjectBase.h"
#include "dawn/native/Subresource.h"
@ -55,6 +56,7 @@ namespace dawn::native {
wgpu::TextureDimension GetDimension() const;
const Format& GetFormat() const;
const FormatSet& GetViewFormats() const;
const Extent3D& GetSize() const;
uint32_t GetWidth() const;
uint32_t GetHeight() const;
@ -109,6 +111,7 @@ namespace dawn::native {
MaybeError ValidateDestroy() const;
wgpu::TextureDimension mDimension;
const Format& mFormat;
FormatSet mViewFormats;
Extent3D mSize;
uint32_t mMipLevelCount;
uint32_t mSampleCount;

View File

@ -23,7 +23,7 @@ namespace dawn::native::opengl {
auto AddFormat = [&table](wgpu::TextureFormat dawnFormat, GLenum internalFormat,
GLenum format, GLenum type, Type componentType) {
size_t index = ComputeFormatIndex(dawnFormat);
FormatIndex index = ComputeFormatIndex(dawnFormat);
ASSERT(index < table.size());
table[index].internalFormat = internalFormat;

View File

@ -34,7 +34,7 @@ namespace dawn::native::opengl {
ComponentType componentType;
};
using GLFormatTable = std::array<GLFormat, kKnownFormatCount>;
using GLFormatTable = ityp::array<FormatIndex, GLFormat, kKnownFormatCount>;
GLFormatTable BuildGLFormatTable();
} // namespace dawn::native::opengl

View File

@ -655,17 +655,33 @@ namespace dawn::native::vulkan {
VkImageCreateInfo createInfo = {};
FillVulkanCreateInfoSizesAndType(*this, &createInfo);
PNextChainBuilder createInfoChain(&createInfo);
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.format = VulkanImageFormat(device, GetFormat().format);
createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
createInfo.usage = VulkanImageUsage(GetInternalUsage(), GetFormat()) | extraUsages;
createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0;
createInfo.pQueueFamilyIndices = nullptr;
createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VkImageFormatListCreateInfo imageFormatListInfo = {};
std::vector<VkFormat> viewFormats;
if (GetViewFormats().any()) {
createInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
if (device->GetDeviceInfo().HasExt(DeviceExt::ImageFormatList)) {
createInfoChain.Add(&imageFormatListInfo,
VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO);
viewFormats.push_back(VulkanImageFormat(device, GetFormat().format));
for (FormatIndex i : IterateBitSet(GetViewFormats())) {
const Format& viewFormat = device->GetValidInternalFormat(i);
viewFormats.push_back(VulkanImageFormat(device, viewFormat.format));
}
imageFormatListInfo.viewFormatCount = viewFormats.size();
imageFormatListInfo.pViewFormats = viewFormats.data();
}
}
ASSERT(IsSampleCountSupported(device, createInfo));
if (GetArrayLayers() >= 6 && GetWidth() == GetHeight()) {
@ -707,7 +723,8 @@ namespace dawn::native::vulkan {
// Internally managed, but imported from external handle
MaybeError Texture::InitializeFromExternal(const ExternalImageDescriptorVk* descriptor,
external_memory::Service* externalMemoryService) {
VkFormat format = VulkanImageFormat(ToBackend(GetDevice()), GetFormat().format);
Device* device = ToBackend(GetDevice());
VkFormat format = VulkanImageFormat(device, GetFormat().format);
VkImageUsageFlags usage = VulkanImageUsage(GetInternalUsage(), GetFormat());
DAWN_INVALID_IF(!externalMemoryService->SupportsCreateImage(descriptor, format, usage,
&mSupportsDisjointVkImage),
@ -728,8 +745,9 @@ namespace dawn::native::vulkan {
VkImageCreateInfo baseCreateInfo = {};
FillVulkanCreateInfoSizesAndType(*this, &baseCreateInfo);
PNextChainBuilder createInfoChain(&baseCreateInfo);
baseCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
baseCreateInfo.pNext = nullptr;
baseCreateInfo.format = format;
baseCreateInfo.usage = usage;
baseCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
@ -741,6 +759,23 @@ namespace dawn::native::vulkan {
// also required for the implementation of robust resource initialization.
baseCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
VkImageFormatListCreateInfo imageFormatListInfo = {};
std::vector<VkFormat> viewFormats;
if (GetViewFormats().any()) {
baseCreateInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
if (device->GetDeviceInfo().HasExt(DeviceExt::ImageFormatList)) {
createInfoChain.Add(&imageFormatListInfo,
VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO);
for (FormatIndex i : IterateBitSet(GetViewFormats())) {
const Format& viewFormat = device->GetValidInternalFormat(i);
viewFormats.push_back(VulkanImageFormat(device, viewFormat.format));
}
imageFormatListInfo.viewFormatCount = viewFormats.size();
imageFormatListInfo.pViewFormats = viewFormats.data();
}
}
DAWN_TRY_ASSIGN(mHandle, externalMemoryService->CreateImage(descriptor, baseCreateInfo));
SetLabelHelper("Dawn_ExternalTexture");

View File

@ -59,7 +59,9 @@ namespace dawn::native::vulkan {
template <typename VK_STRUCT_TYPE>
explicit PNextChainBuilder(VK_STRUCT_TYPE* head)
: mCurrent(reinterpret_cast<VkBaseOutStructure*>(head)) {
ASSERT(head->pNext == nullptr);
while (mCurrent->pNext != nullptr) {
mCurrent = mCurrent->pNext;
}
}
// Add one item to the chain. |vk_struct| must be a Vulkan structure

View File

@ -305,8 +305,8 @@ namespace dawn::native { namespace vulkan::external_memory {
VkImageCreateInfo createInfo = baseCreateInfo;
createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
createInfo.flags = 0;
createInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
PNextChainBuilder createInfoChain(&createInfo);
VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo = {};

View File

@ -17,6 +17,7 @@
#include "dawn/native/vulkan/BackendVk.h"
#include "dawn/native/vulkan/DeviceVk.h"
#include "dawn/native/vulkan/TextureVk.h"
#include "dawn/native/vulkan/UtilsVulkan.h"
#include "dawn/native/vulkan/VulkanError.h"
#include "dawn/native/vulkan/external_memory/MemoryService.h"
@ -133,16 +134,19 @@ namespace dawn::native { namespace vulkan::external_memory {
ResultOrError<VkImage> Service::CreateImage(const ExternalImageDescriptor* descriptor,
const VkImageCreateInfo& baseCreateInfo) {
VkImageCreateInfo createInfo = baseCreateInfo;
createInfo.flags |= VK_IMAGE_CREATE_ALIAS_BIT_KHR;
createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo;
externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
externalMemoryImageCreateInfo.pNext = nullptr;
externalMemoryImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
VkImageCreateInfo createInfo = baseCreateInfo;
createInfo.pNext = &externalMemoryImageCreateInfo;
createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR;
createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
PNextChainBuilder createInfoChain(&createInfo);
createInfoChain.Add(&externalMemoryImageCreateInfo,
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO);
ASSERT(IsSampleCountSupported(mDevice, createInfo));

View File

@ -17,6 +17,7 @@
#include "dawn/native/vulkan/BackendVk.h"
#include "dawn/native/vulkan/DeviceVk.h"
#include "dawn/native/vulkan/TextureVk.h"
#include "dawn/native/vulkan/UtilsVulkan.h"
#include "dawn/native/vulkan/VulkanError.h"
#include "dawn/native/vulkan/external_memory/MemoryService.h"
@ -134,17 +135,20 @@ namespace dawn::native { namespace vulkan::external_memory {
ResultOrError<VkImage> Service::CreateImage(const ExternalImageDescriptor* descriptor,
const VkImageCreateInfo& baseCreateInfo) {
VkImageCreateInfo createInfo = baseCreateInfo;
createInfo.flags |= VK_IMAGE_CREATE_ALIAS_BIT_KHR;
createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo;
externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
externalMemoryImageCreateInfo.pNext = nullptr;
externalMemoryImageCreateInfo.handleTypes =
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA;
VkImageCreateInfo createInfo = baseCreateInfo;
createInfo.pNext = &externalMemoryImageCreateInfo;
createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR;
createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
PNextChainBuilder createInfoChain(&createInfo);
createInfoChain.Add(&externalMemoryImageCreateInfo,
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO);
ASSERT(IsSampleCountSupported(mDevice, createInfo));

View File

@ -438,6 +438,98 @@ TEST_P(TextureViewSamplingTest, Texture2DArrayViewOnOneLevelOf2DArrayTexture) {
Texture2DArrayViewTest(6, 6, 2, 4);
}
// Test that an RGBA8 texture may be interpreted as RGBA8UnormSrgb
// More extensive color value checks and format tests are left for the CTS.
TEST_P(TextureViewSamplingTest, SRGBReinterpretation) {
// TODO(crbug.com/dawn/593): This test requires glTextureView, which is unsupported on GLES.
DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
wgpu::TextureViewDescriptor viewDesc = {};
viewDesc.format = wgpu::TextureFormat::RGBA8UnormSrgb;
wgpu::TextureDescriptor textureDesc = {};
textureDesc.size = {2, 2, 1};
textureDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding;
textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
textureDesc.viewFormats = &viewDesc.format;
textureDesc.viewFormatCount = 1;
wgpu::Texture texture = device.CreateTexture(&textureDesc);
wgpu::ImageCopyTexture dst = {};
dst.texture = texture;
std::array<RGBA8, 4> rgbaTextureData = {
RGBA8(180, 0, 0, 255),
RGBA8(0, 84, 0, 127),
RGBA8(0, 0, 62, 100),
RGBA8(62, 180, 84, 90),
};
wgpu::TextureDataLayout dataLayout = {};
dataLayout.bytesPerRow = textureDesc.size.width * sizeof(RGBA8);
queue.WriteTexture(&dst, rgbaTextureData.data(), rgbaTextureData.size() * sizeof(RGBA8),
&dataLayout, &textureDesc.size);
wgpu::TextureView textureView = texture.CreateView(&viewDesc);
utils::ComboRenderPipelineDescriptor pipelineDesc;
pipelineDesc.vertex.module = utils::CreateShaderModule(device, R"(
@stage(vertex)
fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {
var pos = array<vec2<f32>, 6>(
vec2<f32>(-1.0, -1.0),
vec2<f32>(-1.0, 1.0),
vec2<f32>( 1.0, -1.0),
vec2<f32>(-1.0, 1.0),
vec2<f32>( 1.0, -1.0),
vec2<f32>( 1.0, 1.0));
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
}
)");
pipelineDesc.cFragment.module = utils::CreateShaderModule(device, R"(
@group(0) @binding(0) var texture : texture_2d<f32>;
@stage(fragment)
fn main(@builtin(position) coord: vec4<f32>) -> @location(0) vec4<f32> {
return textureLoad(texture, vec2<i32>(coord.xy), 0);
}
)");
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(
device, textureDesc.size.width, textureDesc.size.height, wgpu::TextureFormat::RGBA8Unorm);
pipelineDesc.cTargets[0].format = renderPass.colorFormat;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDesc);
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, textureView}});
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(6);
pass.End();
}
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
EXPECT_PIXEL_RGBA8_BETWEEN( //
RGBA8(116, 0, 0, 255), //
RGBA8(117, 0, 0, 255), renderPass.color, 0, 0);
EXPECT_PIXEL_RGBA8_BETWEEN( //
RGBA8(0, 23, 0, 127), //
RGBA8(0, 24, 0, 127), renderPass.color, 1, 0);
EXPECT_PIXEL_RGBA8_BETWEEN( //
RGBA8(0, 0, 12, 100), //
RGBA8(0, 0, 13, 100), renderPass.color, 0, 1);
EXPECT_PIXEL_RGBA8_BETWEEN( //
RGBA8(12, 116, 23, 90), //
RGBA8(13, 117, 24, 90), renderPass.color, 1, 1);
}
// Test sampling from a cube map texture view that covers a whole 2D array texture.
TEST_P(TextureViewSamplingTest, TextureCubeMapOnWholeTexture) {
constexpr uint32_t kTotalLayers = 6;

View File

@ -14,6 +14,8 @@
#include "dawn/tests/unittests/validation/ValidationTest.h"
#include <array>
namespace {
class TextureViewValidationTest : public ValidationTest {};
@ -667,18 +669,147 @@ namespace {
}
// Test the format compatibility rules when creating a texture view.
// TODO(jiawei.shao@intel.com): add more tests when the rules are fully implemented.
TEST_F(TextureViewValidationTest, TextureViewFormatCompatibility) {
wgpu::Texture texture = Create2DArrayTexture(device, 1);
wgpu::TextureDescriptor textureDesc = {};
textureDesc.size.width = 4;
textureDesc.size.height = 4;
textureDesc.usage = wgpu::TextureUsage::TextureBinding;
wgpu::TextureViewDescriptor base2DTextureViewDescriptor =
CreateDefaultViewDescriptor(wgpu::TextureViewDimension::e2D);
wgpu::TextureViewDescriptor viewDesc = {};
// It is an error to create a texture view in depth-stencil format on a RGBA texture.
// It is an error to create an sRGB texture view from an RGB texture, without viewFormats.
{
wgpu::TextureViewDescriptor descriptor = base2DTextureViewDescriptor;
descriptor.format = wgpu::TextureFormat::Depth24PlusStencil8;
ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor));
textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
viewDesc.format = wgpu::TextureFormat::RGBA8UnormSrgb;
wgpu::Texture texture = device.CreateTexture(&textureDesc);
ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
}
// It is an error to create an RGB texture view from an sRGB texture, without viewFormats.
{
textureDesc.format = wgpu::TextureFormat::BGRA8UnormSrgb;
viewDesc.format = wgpu::TextureFormat::BGRA8Unorm;
wgpu::Texture texture = device.CreateTexture(&textureDesc);
ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
}
// It is an error to create a texture view with a depth-stencil format of an RGBA texture.
{
textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
viewDesc.format = wgpu::TextureFormat::Depth24PlusStencil8;
wgpu::Texture texture = device.CreateTexture(&textureDesc);
ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
}
// It is an error to create a texture view with a depth format of a depth-stencil texture.
{
textureDesc.format = wgpu::TextureFormat::Depth24PlusStencil8;
viewDesc.format = wgpu::TextureFormat::Depth24Plus;
wgpu::Texture texture = device.CreateTexture(&textureDesc);
ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
}
// It is valid to create a texture view with a depth format of a depth-stencil texture
// if the depth only aspect is selected.
{
textureDesc.format = wgpu::TextureFormat::Depth24PlusStencil8;
viewDesc.format = wgpu::TextureFormat::Depth24Plus;
viewDesc.aspect = wgpu::TextureAspect::DepthOnly;
wgpu::Texture texture = device.CreateTexture(&textureDesc);
texture.CreateView(&viewDesc);
viewDesc = {};
}
// Prep for testing a single view format in viewFormats.
wgpu::TextureFormat viewFormat;
textureDesc.viewFormats = &viewFormat;
textureDesc.viewFormatCount = 1;
// An aspect format is not a valid view format of a depth-stencil texture.
{
textureDesc.format = wgpu::TextureFormat::Depth24PlusStencil8;
viewFormat = wgpu::TextureFormat::Depth24Plus;
ASSERT_DEVICE_ERROR(device.CreateTexture(&textureDesc));
}
// Test that a RGBA texture can be viewed as both RGBA and RGBASrgb, but not BGRA or
// BGRASrgb
{
textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
viewFormat = wgpu::TextureFormat::RGBA8UnormSrgb;
wgpu::Texture texture = device.CreateTexture(&textureDesc);
viewDesc.format = wgpu::TextureFormat::RGBA8UnormSrgb;
texture.CreateView(&viewDesc);
viewDesc.format = wgpu::TextureFormat::RGBA8Unorm;
texture.CreateView(&viewDesc);
viewDesc.format = wgpu::TextureFormat::BGRA8Unorm;
ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
viewDesc.format = wgpu::TextureFormat::BGRA8UnormSrgb;
ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
}
// Test that a BGRASrgb texture can be viewed as both BGRA and BGRASrgb, but not RGBA or
// RGBASrgb
{
textureDesc.format = wgpu::TextureFormat::BGRA8UnormSrgb;
viewFormat = wgpu::TextureFormat::BGRA8Unorm;
wgpu::Texture texture = device.CreateTexture(&textureDesc);
viewDesc.format = wgpu::TextureFormat::BGRA8Unorm;
texture.CreateView(&viewDesc);
viewDesc.format = wgpu::TextureFormat::BGRA8UnormSrgb;
texture.CreateView(&viewDesc);
viewDesc.format = wgpu::TextureFormat::RGBA8Unorm;
ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
viewDesc.format = wgpu::TextureFormat::RGBA8UnormSrgb;
ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
}
// Test an RGBA format may be viewed as RGBA (same)
{
textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
viewFormat = wgpu::TextureFormat::RGBA8Unorm;
wgpu::Texture texture = device.CreateTexture(&textureDesc);
viewDesc.format = wgpu::TextureFormat::RGBA8Unorm;
texture.CreateView(&viewDesc);
viewDesc.format = wgpu::TextureFormat::RGBA8UnormSrgb;
ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
}
// Test that duplicate, and multiple view formats are allowed.
{
std::array<wgpu::TextureFormat, 5> viewFormats = {
wgpu::TextureFormat::RGBA8UnormSrgb, wgpu::TextureFormat::RGBA8Unorm,
wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA8UnormSrgb,
wgpu::TextureFormat::RGBA8Unorm,
};
textureDesc.viewFormats = viewFormats.data();
textureDesc.viewFormatCount = viewFormats.size();
textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
wgpu::Texture texture = device.CreateTexture(&textureDesc);
viewDesc.format = wgpu::TextureFormat::RGBA8UnormSrgb;
texture.CreateView(&viewDesc);
viewDesc.format = wgpu::TextureFormat::RGBA8Unorm;
texture.CreateView(&viewDesc);
viewDesc.format = wgpu::TextureFormat::BGRA8Unorm;
ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
viewDesc.format = wgpu::TextureFormat::BGRA8UnormSrgb;
ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
}
}

View File

@ -18,6 +18,7 @@
#include "dawn/native/vulkan/AdapterVk.h"
#include "dawn/native/vulkan/DeviceVk.h"
#include "dawn/tests/DawnTest.h"
#include "dawn/utils/ComboRenderPipelineDescriptor.h"
#include "dawn/utils/WGPUHelpers.h"
namespace dawn::native { namespace vulkan {
@ -780,6 +781,106 @@ namespace dawn::native { namespace vulkan {
IgnoreSignalSemaphore(nextWrappedTexture);
}
// Test that texture descriptor view formats are passed to the backend for wrapped external
// textures, and that contents may be reinterpreted as sRGB.
TEST_P(VulkanImageWrappingUsageTests, SRGBReinterpretation) {
wgpu::TextureViewDescriptor viewDesc = {};
viewDesc.format = wgpu::TextureFormat::RGBA8UnormSrgb;
wgpu::TextureDescriptor textureDesc = {};
textureDesc.size = {2, 2, 1};
textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
textureDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding;
textureDesc.viewFormatCount = 1;
textureDesc.viewFormats = &viewDesc.format;
std::unique_ptr<ExternalTexture> backendTexture = mBackend->CreateTexture(
textureDesc.size.width, textureDesc.size.height, textureDesc.format, textureDesc.usage);
// Import the image on |device|
wgpu::Texture texture =
WrapVulkanImage(device, &textureDesc, backendTexture.get(), {},
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
ASSERT_NE(texture.Get(), nullptr);
wgpu::ImageCopyTexture dst = {};
dst.texture = texture;
std::array<RGBA8, 4> rgbaTextureData = {
RGBA8(180, 0, 0, 255),
RGBA8(0, 84, 0, 127),
RGBA8(0, 0, 62, 100),
RGBA8(62, 180, 84, 90),
};
wgpu::TextureDataLayout dataLayout = {};
dataLayout.bytesPerRow = textureDesc.size.width * sizeof(RGBA8);
queue.WriteTexture(&dst, rgbaTextureData.data(), rgbaTextureData.size() * sizeof(RGBA8),
&dataLayout, &textureDesc.size);
wgpu::TextureView textureView = texture.CreateView(&viewDesc);
utils::ComboRenderPipelineDescriptor pipelineDesc;
pipelineDesc.vertex.module = utils::CreateShaderModule(device, R"(
@stage(vertex)
fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {
var pos = array<vec2<f32>, 6>(
vec2<f32>(-1.0, -1.0),
vec2<f32>(-1.0, 1.0),
vec2<f32>( 1.0, -1.0),
vec2<f32>(-1.0, 1.0),
vec2<f32>( 1.0, -1.0),
vec2<f32>( 1.0, 1.0));
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
}
)");
pipelineDesc.cFragment.module = utils::CreateShaderModule(device, R"(
@group(0) @binding(0) var texture : texture_2d<f32>;
@stage(fragment)
fn main(@builtin(position) coord: vec4<f32>) -> @location(0) vec4<f32> {
return textureLoad(texture, vec2<i32>(coord.xy), 0);
}
)");
utils::BasicRenderPass renderPass =
utils::CreateBasicRenderPass(device, textureDesc.size.width, textureDesc.size.height,
wgpu::TextureFormat::RGBA8Unorm);
pipelineDesc.cTargets[0].format = renderPass.colorFormat;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDesc);
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, textureView}});
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(6);
pass.End();
}
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
EXPECT_PIXEL_RGBA8_BETWEEN( //
RGBA8(116, 0, 0, 255), //
RGBA8(117, 0, 0, 255), renderPass.color, 0, 0);
EXPECT_PIXEL_RGBA8_BETWEEN( //
RGBA8(0, 23, 0, 127), //
RGBA8(0, 24, 0, 127), renderPass.color, 1, 0);
EXPECT_PIXEL_RGBA8_BETWEEN( //
RGBA8(0, 0, 12, 100), //
RGBA8(0, 0, 13, 100), renderPass.color, 0, 1);
EXPECT_PIXEL_RGBA8_BETWEEN( //
RGBA8(12, 116, 23, 90), //
RGBA8(13, 117, 24, 90), renderPass.color, 1, 1);
IgnoreSignalSemaphore(texture);
}
DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend());
DAWN_INSTANTIATE_TEST(VulkanImageWrappingUsageTests, VulkanBackend());