mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 16:02:37 +00:00
graphics: add 1D image descriptor support
This commit is contained in:
1 parent
125287dc30
commit
7351eae117
20 files changed
+1148
-531
No files matched your search
@@ -22,9 +22,13 @@ vk::ShaderStageFlags StageFlags(DescriptorCache::Stage stage) {
|
||||
|
||||
bool IsSampledImage(BindingKind kind) {
|
||||
switch (kind) {
|
||||
case BindingKind::Sampled1D:
|
||||
case BindingKind::Sampled1DArray:
|
||||
case BindingKind::Sampled2D:
|
||||
case BindingKind::Sampled2DArray:
|
||||
case BindingKind::Sampled3D:
|
||||
case BindingKind::SampledUint1D:
|
||||
case BindingKind::SampledUint1DArray:
|
||||
case BindingKind::SampledUint2D:
|
||||
case BindingKind::SampledUint2DArray:
|
||||
case BindingKind::SampledUint3D: return true;
|
||||
@@ -34,9 +38,13 @@ bool IsSampledImage(BindingKind kind) {
|
||||
|
||||
bool IsStorageImage(BindingKind kind) {
|
||||
switch (kind) {
|
||||
case BindingKind::Storage1D:
|
||||
case BindingKind::Storage1DArray:
|
||||
case BindingKind::Storage2D:
|
||||
case BindingKind::Storage2DArray:
|
||||
case BindingKind::Storage3D:
|
||||
case BindingKind::StorageUint1D:
|
||||
case BindingKind::StorageUint1DArray:
|
||||
case BindingKind::StorageUint2D:
|
||||
case BindingKind::StorageUint2DArray:
|
||||
case BindingKind::StorageUint3D: return true;
|
||||
|
||||
@@ -155,6 +155,8 @@ NativeAddressBuffer(RenderContext& context, CommandBuffer& command_buffer,
|
||||
static bool IsSupportedSampledColorResource(const ShaderRecompiler::IR::ImageResource& resource) {
|
||||
bool supported_dimension = false;
|
||||
switch (resource.dimension) {
|
||||
case ShaderRecompiler::Decoder::ImageDimension::Dim1D:
|
||||
case ShaderRecompiler::Decoder::ImageDimension::Dim1DArray:
|
||||
case ShaderRecompiler::Decoder::ImageDimension::Dim2D:
|
||||
case ShaderRecompiler::Decoder::ImageDimension::Dim2DArray:
|
||||
supported_dimension = true;
|
||||
@@ -295,14 +297,32 @@ static void ValidateDepthTargetBinding(const ShaderRecompiler::IR::ImageResource
|
||||
|
||||
static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
const ShaderTextureResource& descriptor) {
|
||||
const auto tile = descriptor.TileMode();
|
||||
const auto tile = descriptor.TileMode();
|
||||
const bool is_color_1d =
|
||||
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor1D);
|
||||
const bool is_color_1d_array =
|
||||
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor1DArray);
|
||||
const bool valid_1d_slice =
|
||||
(is_color_1d && descriptor.Depth() == 0 && descriptor.BaseArray5() == 0) ||
|
||||
(is_color_1d_array && descriptor.BaseArray5() <= descriptor.Depth());
|
||||
const bool is_1d = resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1D &&
|
||||
descriptor.Height5() == 0 && valid_1d_slice;
|
||||
const bool is_1d_array =
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1DArray &&
|
||||
is_color_1d_array && descriptor.Height5() == 0 &&
|
||||
descriptor.BaseArray5() <= descriptor.Depth();
|
||||
const bool is_color_2d =
|
||||
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D);
|
||||
const bool is_color_2d_array =
|
||||
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor2DArray);
|
||||
const bool valid_2d_slice =
|
||||
(is_color_2d && descriptor.Depth() == 0 && descriptor.BaseArray5() == 0) ||
|
||||
(is_color_2d_array && descriptor.BaseArray5() <= descriptor.Depth());
|
||||
const bool is_2d = resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D &&
|
||||
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) &&
|
||||
descriptor.Depth() == 0 && descriptor.BaseArray5() == 0;
|
||||
valid_2d_slice;
|
||||
const bool is_2d_array =
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray &&
|
||||
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor2DArray) &&
|
||||
descriptor.BaseArray5() <= descriptor.Depth();
|
||||
is_color_2d_array && descriptor.BaseArray5() <= descriptor.Depth();
|
||||
const bool is_3d = resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D &&
|
||||
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor3D) &&
|
||||
descriptor.BaseArray5() == 0;
|
||||
@@ -321,8 +341,9 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
|
||||
const bool supported_swizzle =
|
||||
IsValidImageSwizzle(descriptor.DstSelXYZW()) &&
|
||||
(descriptor.DstSelXYZW() == DstSel(4, 5, 6, 7) || !resource.read);
|
||||
const bool supported_mip_view = descriptor.BaseLevel() == 0 || is_2d;
|
||||
return (is_2d || is_2d_array || is_3d) && supported_tile && supported_mip_view &&
|
||||
const bool supported_mip_view = descriptor.BaseLevel() == 0 || is_1d || is_2d;
|
||||
return (is_1d || is_1d_array || is_2d || is_2d_array || is_3d) && supported_tile &&
|
||||
supported_mip_view &&
|
||||
descriptor.BaseLevel() == descriptor.LastLevel() &&
|
||||
descriptor.LastLevel() <= descriptor.MaxMip() && descriptor.MinLod() == 0 &&
|
||||
supported_swizzle && descriptor.BCSwizzle() == 0 && !descriptor.MsaaDepth();
|
||||
@@ -471,6 +492,22 @@ static ImageViewInfo TextureViewInfo(const ShaderRecompiler::IR::ImageResource&
|
||||
view.mapping =
|
||||
storage ? vk::ComponentMapping {} : TextureGetComponentMapping(descriptor.DstSelXYZW());
|
||||
switch (resource.dimension) {
|
||||
case ShaderRecompiler::Decoder::ImageDimension::Dim1D:
|
||||
view.type = vk::ImageViewType::e1D;
|
||||
view.base_layer = descriptor.BaseArray5();
|
||||
if (view.base_layer >= image_layers) {
|
||||
EXIT("texture base layer is out of bounds\n");
|
||||
}
|
||||
view.layer_count = 1;
|
||||
break;
|
||||
case ShaderRecompiler::Decoder::ImageDimension::Dim1DArray:
|
||||
view.type = vk::ImageViewType::e1DArray;
|
||||
view.base_layer = descriptor.BaseArray5();
|
||||
if (view.base_layer >= image_layers) {
|
||||
EXIT("texture array base layer is out of bounds\n");
|
||||
}
|
||||
view.layer_count = image_layers - view.base_layer;
|
||||
break;
|
||||
case ShaderRecompiler::Decoder::ImageDimension::Dim3D:
|
||||
view.type = vk::ImageViewType::e3D;
|
||||
view.base_layer = 0;
|
||||
@@ -484,11 +521,15 @@ static ImageViewInfo TextureViewInfo(const ShaderRecompiler::IR::ImageResource&
|
||||
}
|
||||
view.layer_count = image_layers - view.base_layer;
|
||||
break;
|
||||
default:
|
||||
view.type = vk::ImageViewType::e2D;
|
||||
view.base_layer = 0;
|
||||
case ShaderRecompiler::Decoder::ImageDimension::Dim2D:
|
||||
view.type = vk::ImageViewType::e2D;
|
||||
view.base_layer = descriptor.BaseArray5();
|
||||
if (view.base_layer >= image_layers) {
|
||||
EXIT("texture base layer is out of bounds\n");
|
||||
}
|
||||
view.layer_count = 1;
|
||||
break;
|
||||
default: EXIT("unsupported texture view dimension\n");
|
||||
}
|
||||
return view;
|
||||
}
|
||||
@@ -576,6 +617,10 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
}
|
||||
|
||||
const auto pixel_format = TextureGetFormat(format);
|
||||
const auto storage_view_format = SrgbStorageViewFormat(pixel_format);
|
||||
const auto view_format =
|
||||
storage && storage_view_format != vk::Format::eUndefined ? storage_view_format
|
||||
: pixel_format;
|
||||
const auto block_bytes = Prospero::BlockCompressedBytesPerBlock(format);
|
||||
TextureCache::ImageDesc desc {};
|
||||
desc.info.data = {address, size.size};
|
||||
@@ -594,7 +639,7 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
} else {
|
||||
PopulateTextureMipLayout(desc.info);
|
||||
}
|
||||
desc.view_info = TextureViewInfo(resource, descriptor, pixel_format, storage, view_levels,
|
||||
desc.view_info = TextureViewInfo(resource, descriptor, view_format, storage, view_levels,
|
||||
desc.info.resources.layers);
|
||||
desc.type = storage ? TextureCache::BindingType::Storage : TextureCache::BindingType::Texture;
|
||||
|
||||
@@ -612,7 +657,7 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
(void)SelectSampledDepthView(image->info.pixel_format, pixel_format,
|
||||
descriptor.DstSelXYZW());
|
||||
} else if (storage) {
|
||||
ValidateStorageColorView(image->info.pixel_format, pixel_format, descriptor.DstSelXYZW());
|
||||
ValidateStorageColorView(image->info.pixel_format, view_format, descriptor.DstSelXYZW());
|
||||
} else {
|
||||
(void)SelectSampledColorView(image->info.pixel_format, pixel_format,
|
||||
descriptor.DstSelXYZW());
|
||||
|
||||
@@ -69,8 +69,19 @@ namespace {
|
||||
default: return false;
|
||||
}
|
||||
case vk::ImageType::e3D:
|
||||
return info.type == vk::ImageViewType::e3D && info.base_layer == 0 &&
|
||||
info.layer_count == 1;
|
||||
switch (info.type) {
|
||||
case vk::ImageViewType::e3D:
|
||||
return info.base_layer == 0 && info.layer_count == 1;
|
||||
case vk::ImageViewType::e2D:
|
||||
return static_cast<bool>(
|
||||
image.flags & vk::ImageCreateFlagBits::e2DArrayCompatible) &&
|
||||
info.level_count == 1 && info.layer_count == 1;
|
||||
case vk::ImageViewType::e2DArray:
|
||||
return static_cast<bool>(
|
||||
image.flags & vk::ImageCreateFlagBits::e2DArrayCompatible) &&
|
||||
info.level_count == 1;
|
||||
default: return false;
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -333,11 +344,18 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
|
||||
is_storage ? vk::ImageUsageFlagBits::eStorage : vk::ImageUsageFlags {};
|
||||
const bool format_compatible = normalized.format != vk::Format::eUndefined &&
|
||||
IsCompatibleViewFormat(image.format, normalized.format);
|
||||
const bool ranges_valid = normalized.level_count != 0 &&
|
||||
const bool slice_view = image.image_type == vk::ImageType::e3D &&
|
||||
(normalized.type == vk::ImageViewType::e2D ||
|
||||
normalized.type == vk::ImageViewType::e2DArray);
|
||||
const bool levels_valid = normalized.level_count != 0 &&
|
||||
normalized.base_level < image.mip_levels &&
|
||||
normalized.level_count <= image.mip_levels - normalized.base_level &&
|
||||
normalized.layer_count != 0 && normalized.base_layer < image.layers &&
|
||||
normalized.layer_count <= image.layers - normalized.base_layer;
|
||||
normalized.level_count <= image.mip_levels - normalized.base_level;
|
||||
const auto view_layers = slice_view && levels_valid
|
||||
? std::max(image.extent.depth >> normalized.base_level, 1u)
|
||||
: image.layers;
|
||||
const bool ranges_valid = levels_valid &&
|
||||
normalized.layer_count != 0 && normalized.base_layer < view_layers &&
|
||||
normalized.layer_count <= view_layers - normalized.base_layer;
|
||||
const bool mapping_valid =
|
||||
IsComponentSwizzle(normalized.mapping.r) && IsComponentSwizzle(normalized.mapping.g) &&
|
||||
IsComponentSwizzle(normalized.mapping.b) && IsComponentSwizzle(normalized.mapping.a);
|
||||
|
||||
@@ -38,19 +38,12 @@ namespace ImageViewOps {
|
||||
static_cast<int>(image_format), static_cast<int>(view_format), swizzle);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline vk::Format BgraSrgbStorageViewFormat(vk::Format image_format) noexcept {
|
||||
return image_format == vk::Format::eB8G8R8A8Srgb ? vk::Format::eR8G8B8A8Unorm
|
||||
: vk::Format::eUndefined;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline vk::Format SrgbStorageViewFormat(vk::Format image_format) noexcept {
|
||||
return image_format == vk::Format::eR8G8B8A8Srgb ? vk::Format::eR8G8B8A8Unorm
|
||||
: BgraSrgbStorageViewFormat(image_format);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool IsBgraSrgbStorageView(vk::Format image_format, vk::Format view_format,
|
||||
uint32_t swizzle) noexcept {
|
||||
return view_format == BgraSrgbStorageViewFormat(image_format) && swizzle == DstSel(6, 5, 4, 7);
|
||||
switch (image_format) {
|
||||
case vk::Format::eR8G8B8A8Srgb:
|
||||
case vk::Format::eB8G8R8A8Srgb: return vk::Format::eR8G8B8A8Unorm;
|
||||
default: return vk::Format::eUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool IsSupportedSampledColorView(vk::Format image_format,
|
||||
@@ -110,8 +103,10 @@ IsSupportedSampledDepthUintResource(const ShaderRecompiler::IR::ImageResource& r
|
||||
|
||||
inline void ValidateStorageColorView(vk::Format image_format, vk::Format view_format,
|
||||
uint32_t swizzle) noexcept {
|
||||
if ((image_format != view_format &&
|
||||
!IsBgraSrgbStorageView(image_format, view_format, swizzle)) ||
|
||||
const auto srgb_view = SrgbStorageViewFormat(image_format);
|
||||
const bool srgb_storage_view =
|
||||
srgb_view != vk::Format::eUndefined && view_format == srgb_view;
|
||||
if ((image_format != view_format && !srgb_storage_view) ||
|
||||
!IsValidImageSwizzle(swizzle)) {
|
||||
UnsupportedColorView("storage", image_format, view_format, swizzle);
|
||||
}
|
||||
@@ -121,7 +116,9 @@ inline void ValidateStorageColorView(vk::Format image_format, vk::Format view_fo
|
||||
IsSupportedStorageImageResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
|
||||
return (resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImage ||
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint) &&
|
||||
(resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D ||
|
||||
(resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1D ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1DArray ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray) &&
|
||||
resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.written &&
|
||||
|
||||
@@ -13,11 +13,15 @@ namespace {
|
||||
constexpr uint32_t MaxPushConstantBytes = 128;
|
||||
|
||||
constexpr std::array ImageBindingKinds = {
|
||||
DescriptorBindingKind::Sampled1D, DescriptorBindingKind::Sampled1DArray,
|
||||
DescriptorBindingKind::Sampled2D, DescriptorBindingKind::Sampled2DArray,
|
||||
DescriptorBindingKind::Sampled3D, DescriptorBindingKind::SampledUint2D,
|
||||
DescriptorBindingKind::Sampled3D, DescriptorBindingKind::SampledUint1D,
|
||||
DescriptorBindingKind::SampledUint1DArray, DescriptorBindingKind::SampledUint2D,
|
||||
DescriptorBindingKind::SampledUint2DArray, DescriptorBindingKind::SampledUint3D,
|
||||
DescriptorBindingKind::Storage1D, DescriptorBindingKind::Storage1DArray,
|
||||
DescriptorBindingKind::Storage2D, DescriptorBindingKind::Storage2DArray,
|
||||
DescriptorBindingKind::Storage3D, DescriptorBindingKind::StorageUint2D,
|
||||
DescriptorBindingKind::Storage3D, DescriptorBindingKind::StorageUint1D,
|
||||
DescriptorBindingKind::StorageUint1DArray, DescriptorBindingKind::StorageUint2D,
|
||||
DescriptorBindingKind::StorageUint2DArray, DescriptorBindingKind::StorageUint3D,
|
||||
};
|
||||
|
||||
@@ -28,6 +32,8 @@ bool ImageBinding(const ImageResource& image, DescriptorBindingKind& result) {
|
||||
switch (image.kind) {
|
||||
case ResourceKind::Image:
|
||||
switch (image.dimension) {
|
||||
case Dimension::Dim1D: result = Kind::Sampled1D; return true;
|
||||
case Dimension::Dim1DArray: result = Kind::Sampled1DArray; return true;
|
||||
case Dimension::Dim2D: result = Kind::Sampled2D; return true;
|
||||
case Dimension::Dim2DArray: result = Kind::Sampled2DArray; return true;
|
||||
case Dimension::Dim3D: result = Kind::Sampled3D; return true;
|
||||
@@ -35,6 +41,8 @@ bool ImageBinding(const ImageResource& image, DescriptorBindingKind& result) {
|
||||
}
|
||||
case ResourceKind::ImageUint:
|
||||
switch (image.dimension) {
|
||||
case Dimension::Dim1D: result = Kind::SampledUint1D; return true;
|
||||
case Dimension::Dim1DArray: result = Kind::SampledUint1DArray; return true;
|
||||
case Dimension::Dim2D: result = Kind::SampledUint2D; return true;
|
||||
case Dimension::Dim2DArray: result = Kind::SampledUint2DArray; return true;
|
||||
case Dimension::Dim3D: result = Kind::SampledUint3D; return true;
|
||||
@@ -42,6 +50,8 @@ bool ImageBinding(const ImageResource& image, DescriptorBindingKind& result) {
|
||||
}
|
||||
case ResourceKind::StorageImage:
|
||||
switch (image.dimension) {
|
||||
case Dimension::Dim1D: result = Kind::Storage1D; return true;
|
||||
case Dimension::Dim1DArray: result = Kind::Storage1DArray; return true;
|
||||
case Dimension::Dim2D: result = Kind::Storage2D; return true;
|
||||
case Dimension::Dim2DArray: result = Kind::Storage2DArray; return true;
|
||||
case Dimension::Dim3D: result = Kind::Storage3D; return true;
|
||||
@@ -49,6 +59,8 @@ bool ImageBinding(const ImageResource& image, DescriptorBindingKind& result) {
|
||||
}
|
||||
case ResourceKind::StorageImageUint:
|
||||
switch (image.dimension) {
|
||||
case Dimension::Dim1D: result = Kind::StorageUint1D; return true;
|
||||
case Dimension::Dim1DArray: result = Kind::StorageUint1DArray; return true;
|
||||
case Dimension::Dim2D: result = Kind::StorageUint2D; return true;
|
||||
case Dimension::Dim2DArray: result = Kind::StorageUint2DArray; return true;
|
||||
case Dimension::Dim3D: result = Kind::StorageUint3D; return true;
|
||||
|
||||
@@ -30,9 +30,11 @@ struct MimgAtomicInfo {
|
||||
|
||||
constexpr ImageDimension DecodeImageDimension(uint32_t dim) {
|
||||
switch (dim) {
|
||||
case 0u: return ImageDimension::Dim1D;
|
||||
case 1u: return ImageDimension::Dim2D;
|
||||
case 2u: return ImageDimension::Dim3D;
|
||||
case 3u: return ImageDimension::Dim2DArray;
|
||||
case 4u: return ImageDimension::Dim1DArray;
|
||||
case 5u:
|
||||
case 7u: return ImageDimension::Dim2DArray;
|
||||
case 6u: return ImageDimension::Dim2D;
|
||||
@@ -42,12 +44,23 @@ constexpr ImageDimension DecodeImageDimension(uint32_t dim) {
|
||||
|
||||
constexpr uint32_t ImageCoordComponents(ImageDimension dimension) {
|
||||
switch (dimension) {
|
||||
case ImageDimension::Dim1D: return 1u;
|
||||
case ImageDimension::Dim1DArray: return 2u;
|
||||
case ImageDimension::Dim3D:
|
||||
case ImageDimension::Dim2DArray: return 3u;
|
||||
default: return 2u;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr uint32_t ImageGradientComponents(ImageDimension dimension) {
|
||||
switch (dimension) {
|
||||
case ImageDimension::Dim1D:
|
||||
case ImageDimension::Dim1DArray: return 1u;
|
||||
case ImageDimension::Dim3D: return 3u;
|
||||
default: return 2u;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr uint32_t ImageSampleAddressComponents(uint32_t flags, ImageDimension dimension) {
|
||||
const auto coord_components = ImageCoordComponents(dimension);
|
||||
uint32_t components = coord_components;
|
||||
@@ -64,7 +77,7 @@ constexpr uint32_t ImageSampleAddressComponents(uint32_t flags, ImageDimension d
|
||||
components++;
|
||||
}
|
||||
if ((flags & ImageSampleFlagDerivative) != 0) {
|
||||
components += 4u;
|
||||
components += ImageGradientComponents(dimension) * 2u;
|
||||
}
|
||||
return components;
|
||||
}
|
||||
@@ -255,7 +268,7 @@ uint32_t DecodeMimgAddressComponents(uint32_t opcode, ImageDimension dimension,
|
||||
return ImageSampleAddressComponents(gather->flags, dimension);
|
||||
}
|
||||
if (atomic != nullptr) {
|
||||
return 3u;
|
||||
return ImageCoordComponents(dimension);
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
|
||||
@@ -12,14 +12,25 @@ namespace {
|
||||
|
||||
constexpr uint64_t AddressMask = 0x0000ffffffffffffull;
|
||||
|
||||
Decoder::ImageDimension DescriptorDimension(const DescriptorValue& descriptor) {
|
||||
Decoder::ImageDimension DescriptorDimension(const DescriptorValue& descriptor,
|
||||
Decoder::ImageDimension requested) {
|
||||
const bool is_array = requested == Decoder::ImageDimension::Dim1DArray ||
|
||||
requested == Decoder::ImageDimension::Dim2DArray;
|
||||
switch (static_cast<Prospero::ImageType>((descriptor.dwords[3] >> 28u) & 0xfu)) {
|
||||
case Prospero::ImageType::kColor3D: return Decoder::ImageDimension::Dim3D;
|
||||
case Prospero::ImageType::kCube:
|
||||
case Prospero::ImageType::kColor1D: return Decoder::ImageDimension::Dim1D;
|
||||
case Prospero::ImageType::kColor1DArray:
|
||||
if (is_array) {
|
||||
return Decoder::ImageDimension::Dim1DArray;
|
||||
}
|
||||
return Decoder::ImageDimension::Dim1D;
|
||||
case Prospero::ImageType::kColor3D: return Decoder::ImageDimension::Dim3D;
|
||||
case Prospero::ImageType::kCube: return Decoder::ImageDimension::Dim2DArray;
|
||||
case Prospero::ImageType::kColor2DArray:
|
||||
case Prospero::ImageType::kColor2DMsaaArray: return Decoder::ImageDimension::Dim2DArray;
|
||||
case Prospero::ImageType::kColor1D:
|
||||
case Prospero::ImageType::kColor2DMsaaArray:
|
||||
if (is_array) {
|
||||
return Decoder::ImageDimension::Dim2DArray;
|
||||
}
|
||||
return Decoder::ImageDimension::Dim2D;
|
||||
case Prospero::ImageType::kColor2D:
|
||||
case Prospero::ImageType::kColor2DMsaa: return Decoder::ImageDimension::Dim2D;
|
||||
default: return Decoder::ImageDimension::Unknown;
|
||||
@@ -160,9 +171,21 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
||||
const auto& image = program.info.images[i];
|
||||
const auto& descriptor = snapshot.images[i];
|
||||
if (NullImageDescriptor(descriptor)) {
|
||||
bool canonical_kind = image.kind == ResourceKind::Image ||
|
||||
image.kind == ResourceKind::StorageImage;
|
||||
if (image.atomic) {
|
||||
canonical_kind = image.kind == ResourceKind::StorageImageUint;
|
||||
}
|
||||
if (image.dimension != Decoder::ImageDimension::Dim2D || !canonical_kind) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
"image descriptor {} no longer matches canonical null specialization", i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const auto dimension = DescriptorDimension(descriptor);
|
||||
const auto dimension = DescriptorDimension(descriptor, image.dimension);
|
||||
if (dimension == Decoder::ImageDimension::Unknown || dimension != image.dimension) {
|
||||
if (error != nullptr) {
|
||||
*error =
|
||||
@@ -337,9 +360,19 @@ bool SpecializeResources(Program& program, const ResourceSnapshot& snapshot, std
|
||||
const auto& descriptor = snapshot.images[i];
|
||||
auto& image = next.images[i];
|
||||
if (NullImageDescriptor(descriptor)) {
|
||||
image.dimension = Decoder::ImageDimension::Dim2D;
|
||||
switch (image.kind) {
|
||||
case ResourceKind::ImageUint: image.kind = ResourceKind::Image; break;
|
||||
case ResourceKind::StorageImageUint:
|
||||
if (!image.atomic) {
|
||||
image.kind = ResourceKind::StorageImage;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const auto descriptor_dimension = DescriptorDimension(descriptor);
|
||||
const auto descriptor_dimension = DescriptorDimension(descriptor, image.dimension);
|
||||
if (descriptor_dimension == Decoder::ImageDimension::Unknown) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
|
||||
@@ -134,15 +134,6 @@ std::string ImageSampleFlagsToString(uint32_t flags) {
|
||||
return text;
|
||||
}
|
||||
|
||||
const char* ImageDimensionToString(ImageDimension dimension) {
|
||||
switch (dimension) {
|
||||
case ImageDimension::Dim2D: return "2d";
|
||||
case ImageDimension::Dim3D: return "3d";
|
||||
case ImageDimension::Dim2DArray: return "2d_array";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
std::string FormatMimg(const Instruction& inst) {
|
||||
const char* sample_name =
|
||||
inst.opcode == Opcode::ImageSample ? MimgSampleOpcodeName(inst.opcode_id) : nullptr;
|
||||
@@ -196,6 +187,17 @@ std::string FormatExp(const Instruction& inst) {
|
||||
|
||||
} // namespace
|
||||
|
||||
const char* ImageDimensionToString(ImageDimension dimension) {
|
||||
switch (dimension) {
|
||||
case ImageDimension::Dim1D: return "1d";
|
||||
case ImageDimension::Dim1DArray: return "1d_array";
|
||||
case ImageDimension::Dim2D: return "2d";
|
||||
case ImageDimension::Dim3D: return "3d";
|
||||
case ImageDimension::Dim2DArray: return "2d_array";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
bool DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand, std::string* error) {
|
||||
operand = {};
|
||||
|
||||
|
||||
@@ -570,6 +570,8 @@ enum ImageSampleFlag : uint32_t {
|
||||
|
||||
enum class ImageDimension : uint32_t {
|
||||
Unknown,
|
||||
Dim1D,
|
||||
Dim1DArray,
|
||||
Dim2D,
|
||||
Dim3D,
|
||||
Dim2DArray,
|
||||
@@ -669,6 +671,7 @@ void SetUnsupported(Instruction& inst, Family family, uint32_t opcode_id, const
|
||||
std::string FamilyToString(Family family);
|
||||
std::string OpcodeToString(Opcode opcode);
|
||||
std::string OperandToString(const Operand& operand);
|
||||
const char* ImageDimensionToString(ImageDimension dimension);
|
||||
std::string InstructionToString(const Instruction& inst);
|
||||
std::string ProgramToString(const Program& program);
|
||||
|
||||
|
||||
@@ -673,15 +673,23 @@ struct StageOutput {
|
||||
|
||||
enum class DescriptorBindingKind {
|
||||
Buffers,
|
||||
Sampled1D,
|
||||
Sampled1DArray,
|
||||
Sampled2D,
|
||||
Sampled2DArray,
|
||||
Sampled3D,
|
||||
SampledUint1D,
|
||||
SampledUint1DArray,
|
||||
SampledUint2D,
|
||||
SampledUint2DArray,
|
||||
SampledUint3D,
|
||||
Storage1D,
|
||||
Storage1DArray,
|
||||
Storage2D,
|
||||
Storage2DArray,
|
||||
Storage3D,
|
||||
StorageUint1D,
|
||||
StorageUint1DArray,
|
||||
StorageUint2D,
|
||||
StorageUint2DArray,
|
||||
StorageUint3D,
|
||||
|
||||
@@ -25,6 +25,10 @@ bool ImageBinding(const IR::ImageResource& image, IR::DescriptorBindingKind& kin
|
||||
if (image.kind == IR::ResourceKind::Image || image.kind == IR::ResourceKind::ImageUint) {
|
||||
const bool integer = image.kind == IR::ResourceKind::ImageUint;
|
||||
switch (image.dimension) {
|
||||
case Dim::Dim1D: kind = integer ? Kind::SampledUint1D : Kind::Sampled1D; return true;
|
||||
case Dim::Dim1DArray:
|
||||
kind = integer ? Kind::SampledUint1DArray : Kind::Sampled1DArray;
|
||||
return true;
|
||||
case Dim::Dim2D: kind = integer ? Kind::SampledUint2D : Kind::Sampled2D; return true;
|
||||
case Dim::Dim3D: kind = integer ? Kind::SampledUint3D : Kind::Sampled3D; return true;
|
||||
case Dim::Dim2DArray:
|
||||
@@ -38,6 +42,10 @@ bool ImageBinding(const IR::ImageResource& image, IR::DescriptorBindingKind& kin
|
||||
return false;
|
||||
}
|
||||
switch (image.dimension) {
|
||||
case Dim::Dim1D: kind = uint_image ? Kind::StorageUint1D : Kind::Storage1D; return true;
|
||||
case Dim::Dim1DArray:
|
||||
kind = uint_image ? Kind::StorageUint1DArray : Kind::Storage1DArray;
|
||||
return true;
|
||||
case Dim::Dim2D: kind = uint_image ? Kind::StorageUint2D : Kind::Storage2D; return true;
|
||||
case Dim::Dim3D: kind = uint_image ? Kind::StorageUint3D : Kind::Storage3D; return true;
|
||||
case Dim::Dim2DArray:
|
||||
@@ -175,6 +183,11 @@ bool ValidateInstructionContract(const IR::Instruction& inst, std::string* error
|
||||
inst.src_count != 1)) {
|
||||
return Fail(error, "sampled image instruction has an invalid resource class");
|
||||
}
|
||||
if (inst.op == IR::Opcode::ImageGather4 &&
|
||||
(inst.memory.image_dimension == Decoder::ImageDimension::Dim1D ||
|
||||
inst.memory.image_dimension == Decoder::ImageDimension::Dim1DArray)) {
|
||||
return Fail(error, "1D image gather is not supported by SPIR-V");
|
||||
}
|
||||
if (inst.op == IR::Opcode::ImageStore &&
|
||||
((kind != IR::ResourceKind::StorageImage && kind != IR::ResourceKind::StorageImageUint) ||
|
||||
inst.src_count != 2)) {
|
||||
|
||||
@@ -86,15 +86,6 @@ std::string ResourceKindToString(ResourceKind kind) {
|
||||
}
|
||||
}
|
||||
|
||||
const char* ImageDimensionToString(Decoder::ImageDimension dimension) {
|
||||
switch (dimension) {
|
||||
case Decoder::ImageDimension::Dim2D: return "2d";
|
||||
case Decoder::ImageDimension::Dim3D: return "3d";
|
||||
case Decoder::ImageDimension::Dim2DArray: return "2d_array";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
std::string MemoryInfoToString(const MemoryInfo& mem) {
|
||||
if (mem.kind == ResourceKind::None) {
|
||||
return "";
|
||||
@@ -109,7 +100,7 @@ std::string MemoryInfoToString(const MemoryInfo& mem) {
|
||||
mem.component_count, mem.data_format, mem.number_format, mem.data_signed ? 1u : 0u,
|
||||
mem.typed ? 1u : 0u, mem.formatted ? 1u : 0u, mem.memory_segment, mem.glc ? 1u : 0u,
|
||||
mem.slc ? 1u : 0u, mem.idxen ? 1u : 0u, mem.offen ? 1u : 0u, mem.image_sample_flags,
|
||||
ImageDimensionToString(mem.image_dimension), mem.image_address_components,
|
||||
Decoder::ImageDimensionToString(mem.image_dimension), mem.image_address_components,
|
||||
mem.image_has_mip ? 1u : 0u);
|
||||
if (mem.image_nsa_dwords != 0) {
|
||||
text += fmt::format(" image_nsa_dwords={} image_nsa_addr=", mem.image_nsa_dwords);
|
||||
|
||||
@@ -572,6 +572,8 @@ uint32_t DescriptorElementPointer(EmitterState& state, uint32_t result_ptr_type,
|
||||
|
||||
ImageViewKind ImageViewKindFromDimension(Decoder::ImageDimension dimension) {
|
||||
switch (dimension) {
|
||||
case Decoder::ImageDimension::Dim1D: return ImageViewKind::Dim1D;
|
||||
case Decoder::ImageDimension::Dim1DArray: return ImageViewKind::Dim1DArray;
|
||||
case Decoder::ImageDimension::Dim2DArray: return ImageViewKind::Dim2DArray;
|
||||
case Decoder::ImageDimension::Dim3D: return ImageViewKind::Dim3D;
|
||||
default: return ImageViewKind::Dim2D;
|
||||
@@ -595,9 +597,23 @@ ImageViewKind StorageImageViewKind(const EmitterState& state, const IR::MemoryIn
|
||||
|
||||
uint32_t ImageViewCoordinateComponents(ImageViewKind view) {
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim1D: return 1u;
|
||||
case ImageViewKind::Dim1DArray:
|
||||
case ImageViewKind::Dim2D: return 2u;
|
||||
case ImageViewKind::Dim2DArray:
|
||||
case ImageViewKind::Dim3D: return 3u;
|
||||
default: return 2u;
|
||||
default: return 0u;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ImageViewSpatialComponents(ImageViewKind view) {
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim1D:
|
||||
case ImageViewKind::Dim1DArray: return 1u;
|
||||
case ImageViewKind::Dim2D:
|
||||
case ImageViewKind::Dim2DArray: return 2u;
|
||||
case ImageViewKind::Dim3D: return 3u;
|
||||
default: return 0u;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -610,67 +626,24 @@ uint32_t ImageViewSampledImageType(const EmitterState& state, ImageViewKind view
|
||||
}
|
||||
|
||||
uint32_t ImageViewSizeType(const EmitterState& state, ImageViewKind view) {
|
||||
return ImageViewCoordinateComponents(view) == 3u ? state.vec3_uint_type : state.vec2_uint_type;
|
||||
switch (ImageViewCoordinateComponents(view)) {
|
||||
case 1u: return state.uint_type;
|
||||
case 2u: return state.vec2_uint_type;
|
||||
case 3u: return state.vec3_uint_type;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t StorageImageType(const EmitterState& state, bool uint_image, ImageViewKind view) {
|
||||
if (uint_image) {
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim2DArray: return state.storage_image_uint_2d_array_type;
|
||||
case ImageViewKind::Dim3D: return state.storage_image_uint_3d_type;
|
||||
default: return state.storage_image_uint_type;
|
||||
}
|
||||
}
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim2DArray: return state.storage_image_2d_array_type;
|
||||
case ImageViewKind::Dim3D: return state.storage_image_3d_type;
|
||||
default: return state.storage_image_type;
|
||||
}
|
||||
return state.storage_images[StorageImageIndex(uint_image, view)].image_type;
|
||||
}
|
||||
|
||||
uint32_t StorageImagePointerType(const EmitterState& state, bool uint_image, ImageViewKind view) {
|
||||
if (uint_image) {
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim2DArray: return state.ptr_uniform_storage_image_uint_2d_array;
|
||||
case ImageViewKind::Dim3D: return state.ptr_uniform_storage_image_uint_3d;
|
||||
default: return state.ptr_uniform_storage_image_uint;
|
||||
}
|
||||
}
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim2DArray: return state.ptr_uniform_storage_image_2d_array;
|
||||
case ImageViewKind::Dim3D: return state.ptr_uniform_storage_image_3d;
|
||||
default: return state.ptr_uniform_storage_image;
|
||||
}
|
||||
return state.storage_images[StorageImageIndex(uint_image, view)].pointer_type;
|
||||
}
|
||||
|
||||
uint32_t StorageImageVariable(const EmitterState& state, bool uint_image, ImageViewKind view) {
|
||||
if (uint_image) {
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim2DArray: return state.storage_image_uint_2d_array_variable;
|
||||
case ImageViewKind::Dim3D: return state.storage_image_uint_3d_variable;
|
||||
default: return state.storage_image_uint_variable;
|
||||
}
|
||||
}
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim2DArray: return state.storage_image_2d_array_variable;
|
||||
case ImageViewKind::Dim3D: return state.storage_image_3d_variable;
|
||||
default: return state.storage_image_variable;
|
||||
}
|
||||
}
|
||||
|
||||
IR::DescriptorBindingKind StorageBindingKind(bool uint_image, ImageViewKind view) {
|
||||
if (uint_image) {
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim2DArray: return IR::DescriptorBindingKind::StorageUint2DArray;
|
||||
case ImageViewKind::Dim3D: return IR::DescriptorBindingKind::StorageUint3D;
|
||||
default: return IR::DescriptorBindingKind::StorageUint2D;
|
||||
}
|
||||
}
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim2DArray: return IR::DescriptorBindingKind::Storage2DArray;
|
||||
case ImageViewKind::Dim3D: return IR::DescriptorBindingKind::Storage3D;
|
||||
default: return IR::DescriptorBindingKind::Storage2D;
|
||||
}
|
||||
return state.storage_images[StorageImageIndex(uint_image, view)].variable;
|
||||
}
|
||||
|
||||
uint32_t LoadSampledImageDescriptor(EmitterState& state, const IR::MemoryInfo& mem, uint32_t use_pc,
|
||||
|
||||
@@ -20,10 +20,11 @@ ImageSampleLayout MakeImageSampleLayout(const IR::Instruction& inst, ImageViewKi
|
||||
layout.dref = cursor++;
|
||||
}
|
||||
if (HasImageSampleFlag(inst, Decoder::ImageSampleFlagDerivative)) {
|
||||
const auto components = ImageViewSpatialComponents(view);
|
||||
layout.grad_x = cursor;
|
||||
cursor += 2u;
|
||||
cursor += components;
|
||||
layout.grad_y = cursor;
|
||||
cursor += 2u;
|
||||
cursor += components;
|
||||
}
|
||||
layout.coord = cursor;
|
||||
cursor += ImageViewCoordinateComponents(view);
|
||||
@@ -35,12 +36,16 @@ ImageSampleLayout MakeImageSampleLayout(const IR::Instruction& inst, ImageViewKi
|
||||
|
||||
uint32_t EmitImageCoordF32(EmitterState& state, const IR::Instruction& inst,
|
||||
const ImageSampleLayout& layout, ImageViewKind view) {
|
||||
const auto x = EmitImageAddressFloatLoad(state, inst, inst.src[0], layout.coord);
|
||||
const auto y = inst.memory.image_address_components > layout.coord + 1u
|
||||
const auto x = EmitImageAddressFloatLoad(state, inst, inst.src[0], layout.coord);
|
||||
const auto components = ImageViewCoordinateComponents(view);
|
||||
if (components == 1u) {
|
||||
return x;
|
||||
}
|
||||
const auto y = inst.memory.image_address_components > layout.coord + 1u
|
||||
? EmitImageAddressFloatLoad(state, inst, inst.src[0], layout.coord + 1u)
|
||||
: EmitZeroF32(state);
|
||||
const auto coord = state.builder.AllocateId();
|
||||
if (ImageViewCoordinateComponents(view) == 3u) {
|
||||
if (components == 3u) {
|
||||
const auto z = inst.memory.image_address_components > layout.coord + 2u
|
||||
? EmitImageAddressFloatLoad(state, inst, inst.src[0], layout.coord + 2u)
|
||||
: EmitZeroF32(state);
|
||||
@@ -77,108 +82,87 @@ uint32_t EmitImageBiasF32(EmitterState& state, const IR::Instruction& inst,
|
||||
}
|
||||
|
||||
uint32_t EmitImageGradientF32(EmitterState& state, const IR::Instruction& inst,
|
||||
uint32_t first_component) {
|
||||
uint32_t first_component, ImageViewKind view) {
|
||||
const auto x = inst.memory.image_address_components > first_component
|
||||
? EmitImageAddressFloatLoad(state, inst, inst.src[0], first_component)
|
||||
: EmitZeroF32(state);
|
||||
const auto components = ImageViewSpatialComponents(view);
|
||||
if (components == 1u) {
|
||||
return x;
|
||||
}
|
||||
const auto y = inst.memory.image_address_components > first_component + 1u
|
||||
? EmitImageAddressFloatLoad(state, inst, inst.src[0], first_component + 1u)
|
||||
: EmitZeroF32(state);
|
||||
const auto grad = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeConstruct, state.vec2_float_type, grad, x, y});
|
||||
if (components == 3u) {
|
||||
const auto z = inst.memory.image_address_components > first_component + 2u
|
||||
? EmitImageAddressFloatLoad(state, inst, inst.src[0],
|
||||
first_component + 2u)
|
||||
: EmitZeroF32(state);
|
||||
state.builder.AddFunction({OpCompositeConstruct, state.vec3_float_type, grad, x, y, z});
|
||||
} else {
|
||||
state.builder.AddFunction({OpCompositeConstruct, state.vec2_float_type, grad, x, y});
|
||||
}
|
||||
return grad;
|
||||
}
|
||||
|
||||
uint32_t EmitImagePackedOffset2I32(EmitterState& state, const IR::Instruction& inst,
|
||||
const ImageSampleLayout& layout) {
|
||||
uint32_t EmitImagePackedOffsetI32(EmitterState& state, const IR::Instruction& inst,
|
||||
const ImageSampleLayout& layout, ImageViewKind view) {
|
||||
const auto components = ImageViewSpatialComponents(view);
|
||||
if (layout.offset == NoImageComponent ||
|
||||
inst.memory.image_address_components <= layout.offset) {
|
||||
const auto zero = ConstantI32(state, 0);
|
||||
const auto ret = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeConstruct, state.vec2_int_type, ret, zero, zero});
|
||||
if (components == 1u) {
|
||||
return zero;
|
||||
}
|
||||
const auto ret = state.builder.AllocateId();
|
||||
if (components == 3u) {
|
||||
state.builder.AddFunction(
|
||||
{OpCompositeConstruct, state.vec3_int_type, ret, zero, zero, zero});
|
||||
} else {
|
||||
state.builder.AddFunction(
|
||||
{OpCompositeConstruct, state.vec2_int_type, ret, zero, zero});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const auto packed_bits = EmitImageAddressValueLoad(state, inst, inst.src[0], layout.offset);
|
||||
const auto packed_i32 = state.builder.AllocateId();
|
||||
const auto offset_x = state.builder.AllocateId();
|
||||
const auto offset_y = state.builder.AllocateId();
|
||||
const auto offset = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitcast, state.int_type, packed_i32, packed_bits});
|
||||
state.builder.AddFunction({OpBitFieldSExtract, state.int_type, offset_x, packed_i32,
|
||||
ConstantI32(state, 0), ConstantI32(state, 6)});
|
||||
if (components == 1u) {
|
||||
return offset_x;
|
||||
}
|
||||
const auto offset_y = state.builder.AllocateId();
|
||||
const auto offset = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitFieldSExtract, state.int_type, offset_y, packed_i32,
|
||||
ConstantI32(state, 8), ConstantI32(state, 6)});
|
||||
state.builder.AddFunction(
|
||||
{OpCompositeConstruct, state.vec2_int_type, offset, offset_x, offset_y});
|
||||
if (components == 3u) {
|
||||
const auto offset_z = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitFieldSExtract, state.int_type, offset_z, packed_i32,
|
||||
ConstantI32(state, 16), ConstantI32(state, 6)});
|
||||
state.builder.AddFunction(
|
||||
{OpCompositeConstruct, state.vec3_int_type, offset, offset_x, offset_y, offset_z});
|
||||
} else {
|
||||
state.builder.AddFunction(
|
||||
{OpCompositeConstruct, state.vec2_int_type, offset, offset_x, offset_y});
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
uint32_t EmitImageOffsetCoordF32(EmitterState& state, const IR::Instruction& inst,
|
||||
const ImageSampleLayout& layout, uint32_t sampled_image,
|
||||
uint32_t coord, ImageViewKind view) {
|
||||
if (!HasImageSampleFlag(inst, Decoder::ImageSampleFlagOffset)) {
|
||||
return coord;
|
||||
}
|
||||
|
||||
const auto offset_i32 = EmitImagePackedOffset2I32(state, inst, layout);
|
||||
const auto offset_f32 = state.builder.AllocateId();
|
||||
const auto image = state.builder.AllocateId();
|
||||
const auto size_u32 = state.builder.AllocateId();
|
||||
const auto size_xy_u32 =
|
||||
ImageViewCoordinateComponents(view) == 3u ? state.builder.AllocateId() : size_u32;
|
||||
const auto size_f32 = state.builder.AllocateId();
|
||||
const auto normalized = state.builder.AllocateId();
|
||||
const auto adjusted = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpConvertSToF, state.vec2_float_type, offset_f32, offset_i32});
|
||||
state.builder.AddFunction(
|
||||
{OpImage, ImageViewImageType(state, view, inst.memory.kind == IR::ResourceKind::ImageUint),
|
||||
image, sampled_image});
|
||||
state.builder.AddFunction({OpImageQuerySizeLod, ImageViewSizeType(state, view), size_u32,
|
||||
image, ConstantU32(state, 0)});
|
||||
if (ImageViewCoordinateComponents(view) == 3u) {
|
||||
const auto width = state.builder.AllocateId();
|
||||
const auto height = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeExtract, state.uint_type, width, size_u32, 0});
|
||||
state.builder.AddFunction({OpCompositeExtract, state.uint_type, height, size_u32, 1});
|
||||
state.builder.AddFunction(
|
||||
{OpCompositeConstruct, state.vec2_uint_type, size_xy_u32, width, height});
|
||||
}
|
||||
state.builder.AddFunction({OpConvertUToF, state.vec2_float_type, size_f32, size_xy_u32});
|
||||
state.builder.AddFunction({OpFDiv, state.vec2_float_type, normalized, offset_f32, size_f32});
|
||||
if (ImageViewCoordinateComponents(view) == 2u) {
|
||||
state.builder.AddFunction({OpFAdd, state.vec2_float_type, adjusted, coord, normalized});
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
const auto x = state.builder.AllocateId();
|
||||
const auto y = state.builder.AllocateId();
|
||||
const auto z = state.builder.AllocateId();
|
||||
const auto normalized_x = state.builder.AllocateId();
|
||||
const auto normalized_y = state.builder.AllocateId();
|
||||
const auto adjusted_x = state.builder.AllocateId();
|
||||
const auto adjusted_y = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeExtract, state.float_type, x, coord, 0});
|
||||
state.builder.AddFunction({OpCompositeExtract, state.float_type, y, coord, 1});
|
||||
state.builder.AddFunction({OpCompositeExtract, state.float_type, z, coord, 2});
|
||||
state.builder.AddFunction(
|
||||
{OpCompositeExtract, state.float_type, normalized_x, normalized, 0});
|
||||
state.builder.AddFunction(
|
||||
{OpCompositeExtract, state.float_type, normalized_y, normalized, 1});
|
||||
state.builder.AddFunction({OpFAdd, state.float_type, adjusted_x, x, normalized_x});
|
||||
state.builder.AddFunction({OpFAdd, state.float_type, adjusted_y, y, normalized_y});
|
||||
state.builder.AddFunction(
|
||||
{OpCompositeConstruct, state.vec3_float_type, adjusted, adjusted_x, adjusted_y, z});
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
uint32_t EmitImageCoordU32(EmitterState& state, const IR::Instruction& inst, ImageViewKind view) {
|
||||
const auto x = EmitImageAddressValueLoad(state, inst, inst.src[1], 0);
|
||||
const auto y = inst.memory.image_address_components > 1u
|
||||
const auto x = EmitImageAddressValueLoad(state, inst, inst.src[1], 0);
|
||||
const auto components = ImageViewCoordinateComponents(view);
|
||||
if (components == 1u) {
|
||||
return x;
|
||||
}
|
||||
const auto y = inst.memory.image_address_components > 1u
|
||||
? EmitImageAddressValueLoad(state, inst, inst.src[1], 1)
|
||||
: ConstantU32(state, 0);
|
||||
const auto coord = state.builder.AllocateId();
|
||||
if (ImageViewCoordinateComponents(view) == 3u) {
|
||||
if (components == 3u) {
|
||||
const auto z = inst.memory.image_address_components > 2u
|
||||
? EmitImageAddressValueLoad(state, inst, inst.src[1], 2)
|
||||
: ConstantU32(state, 0);
|
||||
@@ -191,12 +175,16 @@ uint32_t EmitImageCoordU32(EmitterState& state, const IR::Instruction& inst, Ima
|
||||
|
||||
uint32_t EmitImageLoadCoordU32(EmitterState& state, const IR::Instruction& inst,
|
||||
ImageViewKind view) {
|
||||
const auto x = EmitImageAddressValueLoad(state, inst, inst.src[0], 0);
|
||||
const auto y = inst.memory.image_address_components > 1u
|
||||
const auto x = EmitImageAddressValueLoad(state, inst, inst.src[0], 0);
|
||||
const auto components = ImageViewCoordinateComponents(view);
|
||||
if (components == 1u) {
|
||||
return x;
|
||||
}
|
||||
const auto y = inst.memory.image_address_components > 1u
|
||||
? EmitImageAddressValueLoad(state, inst, inst.src[0], 1)
|
||||
: ConstantU32(state, 0);
|
||||
const auto coord = state.builder.AllocateId();
|
||||
if (ImageViewCoordinateComponents(view) == 3u) {
|
||||
if (components == 3u) {
|
||||
const auto z = inst.memory.image_address_components > 2u
|
||||
? EmitImageAddressValueLoad(state, inst, inst.src[0], 2)
|
||||
: ConstantU32(state, 0);
|
||||
@@ -221,12 +209,16 @@ uint32_t EmitImageMipLodU32(EmitterState& state, const IR::Instruction& inst,
|
||||
|
||||
uint32_t EmitImageQueryCoordF32(EmitterState& state, const IR::Instruction& inst,
|
||||
ImageViewKind view) {
|
||||
const auto x = EmitImageAddressFloatLoad(state, inst, inst.src[0], 0);
|
||||
const auto y = inst.memory.image_address_components > 1u
|
||||
const auto x = EmitImageAddressFloatLoad(state, inst, inst.src[0], 0);
|
||||
const auto components = ImageViewCoordinateComponents(view);
|
||||
if (components == 1u) {
|
||||
return x;
|
||||
}
|
||||
const auto y = inst.memory.image_address_components > 1u
|
||||
? EmitImageAddressFloatLoad(state, inst, inst.src[0], 1)
|
||||
: EmitZeroF32(state);
|
||||
const auto coord = state.builder.AllocateId();
|
||||
if (ImageViewCoordinateComponents(view) == 3u) {
|
||||
if (components == 3u) {
|
||||
const auto z = inst.memory.image_address_components > 2u
|
||||
? EmitImageAddressFloatLoad(state, inst, inst.src[0], 2)
|
||||
: EmitZeroF32(state);
|
||||
|
||||
@@ -11,7 +11,16 @@ uint32_t ConstantVec2I32(EmitterState& state, int32_t x, int32_t y) {
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t ConstantImageGatherHorizontalOffsets(EmitterState& state) {
|
||||
uint32_t ConstantImageGatherHorizontalOffsets(EmitterState& state, ImageViewKind view) {
|
||||
if (ImageViewSpatialComponents(view) == 1u) {
|
||||
const auto type = state.builder.AllocateId();
|
||||
state.builder.AddType({OpTypeArray, type, state.int_type, ConstantU32(state, 4)});
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddType({OpConstantComposite, type, value, ConstantI32(state, -1),
|
||||
ConstantI32(state, 0), ConstantI32(state, 1),
|
||||
ConstantI32(state, 2)});
|
||||
return value;
|
||||
}
|
||||
const auto offset0 = ConstantVec2I32(state, -1, 0);
|
||||
const auto offset1 = ConstantVec2I32(state, 0, 0);
|
||||
const auto offset2 = ConstantVec2I32(state, 1, 0);
|
||||
@@ -23,56 +32,16 @@ uint32_t ConstantImageGatherHorizontalOffsets(EmitterState& state) {
|
||||
return value;
|
||||
}
|
||||
|
||||
IR::DescriptorBindingKind StorageImageBindingKind(ImageViewKind view, bool uint_image) {
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim2D:
|
||||
return uint_image ? IR::DescriptorBindingKind::StorageUint2D
|
||||
: IR::DescriptorBindingKind::Storage2D;
|
||||
case ImageViewKind::Dim2DArray:
|
||||
return uint_image ? IR::DescriptorBindingKind::StorageUint2DArray
|
||||
: IR::DescriptorBindingKind::Storage2DArray;
|
||||
case ImageViewKind::Dim3D:
|
||||
return uint_image ? IR::DescriptorBindingKind::StorageUint3D
|
||||
: IR::DescriptorBindingKind::Storage3D;
|
||||
default:
|
||||
return uint_image ? IR::DescriptorBindingKind::StorageUint2D
|
||||
: IR::DescriptorBindingKind::Storage2D;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t LoadStorageImageDescriptorAtIndex(EmitterState& state, uint32_t resource,
|
||||
uint32_t array_index, bool uint_image,
|
||||
ImageViewKind view) {
|
||||
uint32_t pointer_type =
|
||||
uint_image ? state.ptr_uniform_storage_image_uint : state.ptr_uniform_storage_image;
|
||||
uint32_t variable =
|
||||
uint_image ? state.storage_image_uint_variable : state.storage_image_variable;
|
||||
uint32_t image_type = uint_image ? state.storage_image_uint_type : state.storage_image_type;
|
||||
const auto kind = StorageImageBindingKind(view, uint_image);
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim2D: break;
|
||||
case ImageViewKind::Dim2DArray:
|
||||
pointer_type = uint_image ? state.ptr_uniform_storage_image_uint_2d_array
|
||||
: state.ptr_uniform_storage_image_2d_array;
|
||||
variable = uint_image ? state.storage_image_uint_2d_array_variable
|
||||
: state.storage_image_2d_array_variable;
|
||||
image_type = uint_image ? state.storage_image_uint_2d_array_type
|
||||
: state.storage_image_2d_array_type;
|
||||
break;
|
||||
case ImageViewKind::Dim3D:
|
||||
pointer_type = uint_image ? state.ptr_uniform_storage_image_uint_3d
|
||||
: state.ptr_uniform_storage_image_3d;
|
||||
variable =
|
||||
uint_image ? state.storage_image_uint_3d_variable : state.storage_image_3d_variable;
|
||||
image_type =
|
||||
uint_image ? state.storage_image_uint_3d_type : state.storage_image_3d_type;
|
||||
break;
|
||||
}
|
||||
const auto kind = StorageBindingKind(uint_image, view);
|
||||
const auto& descriptors = state.storage_images[StorageImageIndex(uint_image, view)];
|
||||
const auto pointer =
|
||||
DescriptorElementPointer(state, pointer_type, variable, array_index, kind, resource,
|
||||
"storage image descriptor array was not emitted");
|
||||
DescriptorElementPointer(state, descriptors.pointer_type, descriptors.variable, array_index,
|
||||
kind, resource, "storage image descriptor array was not emitted");
|
||||
const auto image = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpLoad, image_type, image, pointer});
|
||||
state.builder.AddFunction({OpLoad, descriptors.image_type, image, pointer});
|
||||
return image;
|
||||
}
|
||||
|
||||
@@ -80,24 +49,31 @@ uint32_t LoadStorageImageDescriptorAtIndex(EmitterState& state, uint32_t resourc
|
||||
|
||||
uint32_t EmitImageGetResinfoComponent(EmitterState& state, uint32_t image, uint32_t size,
|
||||
uint32_t component_index, ImageViewKind view) {
|
||||
const auto components = ImageViewCoordinateComponents(view);
|
||||
switch (component_index) {
|
||||
case 0: {
|
||||
if (components == 1u) {
|
||||
return size;
|
||||
}
|
||||
const auto width = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeExtract, state.uint_type, width, size, 0});
|
||||
return width;
|
||||
}
|
||||
case 1: {
|
||||
if (components < 2u) {
|
||||
return ConstantU32(state, 0);
|
||||
}
|
||||
const auto height = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeExtract, state.uint_type, height, size, 1});
|
||||
return height;
|
||||
}
|
||||
case 2:
|
||||
if (ImageViewCoordinateComponents(view) == 3u) {
|
||||
if (components == 3u) {
|
||||
const auto depth = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeExtract, state.uint_type, depth, size, 2});
|
||||
return depth;
|
||||
}
|
||||
return ConstantU32(state, 1);
|
||||
return ConstantU32(state, 0);
|
||||
case 3: {
|
||||
const auto levels = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpImageQueryLevels, state.uint_type, levels, image});
|
||||
@@ -182,7 +158,7 @@ void EmitImageLoad(EmitterState& state, const IR::Instruction& inst) {
|
||||
void EmitImageStore(EmitterState& state, const IR::Instruction& inst) {
|
||||
const auto uint_image = inst.memory.kind == IR::ResourceKind::StorageImageUint;
|
||||
const auto view = StorageImageViewKind(state, inst.memory, uint_image, inst.pc);
|
||||
const auto binding = ResourceForDescriptor(state, StorageImageBindingKind(view, uint_image),
|
||||
const auto binding = ResourceForDescriptor(state, StorageBindingKind(uint_image, view),
|
||||
inst.memory.resource);
|
||||
const auto image = LoadStorageImageDescriptorAtIndex(state, inst.memory.resource,
|
||||
binding.array_index, uint_image, view);
|
||||
@@ -244,8 +220,9 @@ void AddImageSampleOperands(EmitterState& state, const IR::Instruction& inst,
|
||||
// sampling because it modifies the hardware-computed LOD.
|
||||
if (HasImageSampleFlag(inst, Decoder::ImageSampleFlagDerivative)) {
|
||||
mask |= ImageOperandsGradMask;
|
||||
operands.push_back(EmitImageGradientF32(state, inst, layout.grad_x));
|
||||
operands.push_back(EmitImageGradientF32(state, inst, layout.grad_y));
|
||||
const auto view = SampledImageViewKind(state, inst.memory, inst.pc);
|
||||
operands.push_back(EmitImageGradientF32(state, inst, layout.grad_x, view));
|
||||
operands.push_back(EmitImageGradientF32(state, inst, layout.grad_y, view));
|
||||
} else if (explicit_lod) {
|
||||
mask |= ImageOperandsLodMask;
|
||||
operands.push_back(EmitImageLodF32(state, inst, layout));
|
||||
@@ -286,10 +263,7 @@ void EmitImageSample(EmitterState& state, const IR::Instruction& inst) {
|
||||
}
|
||||
const auto explicit_lod = ImageSampleNeedsExplicitLod(state, inst);
|
||||
const auto opcode = ImageSampleOpcode(state, inst);
|
||||
const auto coord =
|
||||
EmitImageOffsetCoordF32(state, inst, layout, sampled_image, base_coord, view);
|
||||
|
||||
std::vector<uint32_t> words = {opcode, result_type, sample, sampled_image, coord};
|
||||
std::vector<uint32_t> words = {opcode, result_type, sample, sampled_image, base_coord};
|
||||
if (dref) {
|
||||
words.push_back(EmitImageDrefF32(state, inst, layout));
|
||||
}
|
||||
@@ -308,15 +282,16 @@ uint32_t ImageGatherComponent(uint32_t dmask) {
|
||||
}
|
||||
|
||||
void AddImageGatherOperands(EmitterState& state, const IR::Instruction& inst,
|
||||
const ImageSampleLayout& layout, std::vector<uint32_t>& words) {
|
||||
const ImageSampleLayout& layout, ImageViewKind view,
|
||||
std::vector<uint32_t>& words) {
|
||||
if (HasImageSampleFlag(inst, Decoder::ImageSampleFlagGatherHorizontal)) {
|
||||
words.push_back(ImageOperandsConstOffsetsMask);
|
||||
words.push_back(ConstantImageGatherHorizontalOffsets(state));
|
||||
words.push_back(ConstantImageGatherHorizontalOffsets(state, view));
|
||||
return;
|
||||
}
|
||||
if (HasImageSampleFlag(inst, Decoder::ImageSampleFlagOffset)) {
|
||||
words.push_back(ImageOperandsOffsetMask);
|
||||
words.push_back(EmitImagePackedOffset2I32(state, inst, layout));
|
||||
words.push_back(EmitImagePackedOffsetI32(state, inst, layout, view));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,7 +316,7 @@ void EmitImageGather4(EmitterState& state, const IR::Instruction& inst) {
|
||||
OpImageGather, result_type,
|
||||
texels, sampled_image,
|
||||
coord, ConstantU32(state, ImageGatherComponent(inst.memory.dmask))};
|
||||
AddImageGatherOperands(state, inst, layout, words);
|
||||
AddImageGatherOperands(state, inst, layout, view, words);
|
||||
state.builder.AddFunction(words);
|
||||
|
||||
for (uint32_t i = 0; i < inst.memory.data_dwords; i++) {
|
||||
|
||||
@@ -35,6 +35,8 @@ enum : uint32_t {
|
||||
MemoryModelGLSL450 = 1,
|
||||
CapabilityShader = 1,
|
||||
CapabilityImageGatherExtended = 25,
|
||||
CapabilitySampled1D = 43,
|
||||
CapabilityImage1D = 44,
|
||||
CapabilityImageQuery = 50,
|
||||
CapabilityStorageImageReadWithoutFormat = 55,
|
||||
CapabilityStorageImageWriteWithoutFormat = 56,
|
||||
@@ -83,6 +85,7 @@ enum : uint32_t {
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
Dim1D = 0,
|
||||
Dim3D = 2,
|
||||
Dim2D = 1,
|
||||
ImageFormatUnknown = 0,
|
||||
@@ -297,6 +300,14 @@ struct SampledImageDescriptors {
|
||||
uint32_t variable = 0;
|
||||
};
|
||||
|
||||
struct StorageImageDescriptors {
|
||||
uint32_t image_type = 0;
|
||||
uint32_t pointer_type = 0;
|
||||
uint32_t array_type = 0;
|
||||
uint32_t array_pointer_type = 0;
|
||||
uint32_t variable = 0;
|
||||
};
|
||||
|
||||
struct EmitterState {
|
||||
EmitterState(const IR::Program& program_, const IR::ResourceSnapshot& resources_)
|
||||
: program(program_), resources(resources_) {}
|
||||
@@ -371,42 +382,13 @@ struct EmitterState {
|
||||
uint32_t ptr_workgroup_array = 0;
|
||||
uint32_t ptr_workgroup_uint = 0;
|
||||
uint32_t lds_variable = 0;
|
||||
std::array<SampledImageDescriptors, 6> sampled_images;
|
||||
std::array<SampledImageDescriptors, 10> sampled_images;
|
||||
std::array<StorageImageDescriptors, 10> storage_images;
|
||||
uint32_t sampler_type = 0;
|
||||
uint32_t sampler_array_type = 0;
|
||||
uint32_t ptr_uniform_sampler = 0;
|
||||
uint32_t ptr_uniform_sampler_array = 0;
|
||||
uint32_t sampler_variable = 0;
|
||||
uint32_t storage_image_type = 0;
|
||||
uint32_t ptr_uniform_storage_image = 0;
|
||||
uint32_t storage_image_array_type = 0;
|
||||
uint32_t ptr_uniform_storage_image_array = 0;
|
||||
uint32_t storage_image_variable = 0;
|
||||
uint32_t storage_image_2d_array_type = 0;
|
||||
uint32_t ptr_uniform_storage_image_2d_array = 0;
|
||||
uint32_t storage_image_2d_array_array_type = 0;
|
||||
uint32_t ptr_uniform_storage_image_2d_array_array = 0;
|
||||
uint32_t storage_image_2d_array_variable = 0;
|
||||
uint32_t storage_image_3d_type = 0;
|
||||
uint32_t ptr_uniform_storage_image_3d = 0;
|
||||
uint32_t storage_image_3d_array_type = 0;
|
||||
uint32_t ptr_uniform_storage_image_3d_array = 0;
|
||||
uint32_t storage_image_3d_variable = 0;
|
||||
uint32_t storage_image_uint_type = 0;
|
||||
uint32_t ptr_uniform_storage_image_uint = 0;
|
||||
uint32_t storage_image_uint_array_type = 0;
|
||||
uint32_t ptr_uniform_storage_image_uint_array = 0;
|
||||
uint32_t storage_image_uint_variable = 0;
|
||||
uint32_t storage_image_uint_2d_array_type = 0;
|
||||
uint32_t ptr_uniform_storage_image_uint_2d_array = 0;
|
||||
uint32_t storage_image_uint_2d_array_array_type = 0;
|
||||
uint32_t ptr_uniform_storage_image_uint_2d_array_array = 0;
|
||||
uint32_t storage_image_uint_2d_array_variable = 0;
|
||||
uint32_t storage_image_uint_3d_type = 0;
|
||||
uint32_t ptr_uniform_storage_image_uint_3d = 0;
|
||||
uint32_t storage_image_uint_3d_array_type = 0;
|
||||
uint32_t ptr_uniform_storage_image_uint_3d_array = 0;
|
||||
uint32_t storage_image_uint_3d_variable = 0;
|
||||
uint32_t ptr_image_uint = 0;
|
||||
uint32_t func_type = 0;
|
||||
uint32_t main_func = 0;
|
||||
@@ -466,28 +448,82 @@ struct ImageSampleLayout {
|
||||
};
|
||||
|
||||
enum class ImageViewKind {
|
||||
Dim1D,
|
||||
Dim1DArray,
|
||||
Dim2D,
|
||||
Dim2DArray,
|
||||
Dim3D,
|
||||
Count,
|
||||
};
|
||||
|
||||
constexpr uint32_t ImageViewKindCount = static_cast<uint32_t>(ImageViewKind::Count);
|
||||
|
||||
constexpr uint32_t SampledImageIndex(bool integer, ImageViewKind view) {
|
||||
return static_cast<uint32_t>(view) + (integer ? 3u : 0u);
|
||||
return static_cast<uint32_t>(view) + (integer ? ImageViewKindCount : 0u);
|
||||
}
|
||||
|
||||
constexpr uint32_t StorageImageIndex(bool integer, ImageViewKind view) {
|
||||
return static_cast<uint32_t>(view) + (integer ? ImageViewKindCount : 0u);
|
||||
}
|
||||
|
||||
constexpr IR::DescriptorBindingKind SampledBindingKind(bool integer, ImageViewKind view) {
|
||||
if (integer) {
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim1D: return IR::DescriptorBindingKind::SampledUint1D;
|
||||
case ImageViewKind::Dim1DArray: return IR::DescriptorBindingKind::SampledUint1DArray;
|
||||
case ImageViewKind::Dim2D: return IR::DescriptorBindingKind::SampledUint2D;
|
||||
case ImageViewKind::Dim2DArray: return IR::DescriptorBindingKind::SampledUint2DArray;
|
||||
case ImageViewKind::Dim3D: return IR::DescriptorBindingKind::SampledUint3D;
|
||||
default: return IR::DescriptorBindingKind::SampledUint2D;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim1D: return IR::DescriptorBindingKind::Sampled1D;
|
||||
case ImageViewKind::Dim1DArray: return IR::DescriptorBindingKind::Sampled1DArray;
|
||||
case ImageViewKind::Dim2D: return IR::DescriptorBindingKind::Sampled2D;
|
||||
case ImageViewKind::Dim2DArray: return IR::DescriptorBindingKind::Sampled2DArray;
|
||||
case ImageViewKind::Dim3D: return IR::DescriptorBindingKind::Sampled3D;
|
||||
default: return IR::DescriptorBindingKind::Sampled2D;
|
||||
default: break;
|
||||
}
|
||||
return IR::DescriptorBindingKind::Count;
|
||||
}
|
||||
|
||||
constexpr IR::DescriptorBindingKind StorageBindingKind(bool integer, ImageViewKind view) {
|
||||
if (integer) {
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim1D: return IR::DescriptorBindingKind::StorageUint1D;
|
||||
case ImageViewKind::Dim1DArray: return IR::DescriptorBindingKind::StorageUint1DArray;
|
||||
case ImageViewKind::Dim2D: return IR::DescriptorBindingKind::StorageUint2D;
|
||||
case ImageViewKind::Dim2DArray: return IR::DescriptorBindingKind::StorageUint2DArray;
|
||||
case ImageViewKind::Dim3D: return IR::DescriptorBindingKind::StorageUint3D;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim1D: return IR::DescriptorBindingKind::Storage1D;
|
||||
case ImageViewKind::Dim1DArray: return IR::DescriptorBindingKind::Storage1DArray;
|
||||
case ImageViewKind::Dim2D: return IR::DescriptorBindingKind::Storage2D;
|
||||
case ImageViewKind::Dim2DArray: return IR::DescriptorBindingKind::Storage2DArray;
|
||||
case ImageViewKind::Dim3D: return IR::DescriptorBindingKind::Storage3D;
|
||||
default: break;
|
||||
}
|
||||
return IR::DescriptorBindingKind::Count;
|
||||
}
|
||||
|
||||
constexpr uint32_t ImageSpirvDimension(ImageViewKind view) {
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim1D:
|
||||
case ImageViewKind::Dim1DArray: return Dim1D;
|
||||
case ImageViewKind::Dim2D:
|
||||
case ImageViewKind::Dim2DArray:
|
||||
case ImageViewKind::Count: return Dim2D;
|
||||
case ImageViewKind::Dim3D: return Dim3D;
|
||||
}
|
||||
return Dim2D;
|
||||
}
|
||||
|
||||
constexpr uint32_t ImageSpirvArrayed(ImageViewKind view) {
|
||||
return view == ImageViewKind::Dim1DArray || view == ImageViewKind::Dim2DArray ? 1u : 0u;
|
||||
}
|
||||
|
||||
struct AddCarryResult {
|
||||
@@ -632,6 +668,8 @@ ImageViewKind SampledImageViewKind(const EmitterState& state, const IR::MemoryIn
|
||||
|
||||
uint32_t ImageViewCoordinateComponents(ImageViewKind view);
|
||||
|
||||
uint32_t ImageViewSpatialComponents(ImageViewKind view);
|
||||
|
||||
uint32_t ImageViewImageType(const EmitterState& state, ImageViewKind view, bool integer);
|
||||
|
||||
uint32_t ImageViewSampledImageType(const EmitterState& state, ImageViewKind view, bool integer);
|
||||
@@ -792,14 +830,10 @@ uint32_t EmitImageBiasF32(EmitterState& state, const IR::Instruction& inst,
|
||||
const ImageSampleLayout& layout);
|
||||
|
||||
uint32_t EmitImageGradientF32(EmitterState& state, const IR::Instruction& inst,
|
||||
uint32_t first_component);
|
||||
uint32_t first_component, ImageViewKind view);
|
||||
|
||||
uint32_t EmitImagePackedOffset2I32(EmitterState& state, const IR::Instruction& inst,
|
||||
const ImageSampleLayout& layout);
|
||||
|
||||
uint32_t EmitImageOffsetCoordF32(EmitterState& state, const IR::Instruction& inst,
|
||||
const ImageSampleLayout& layout, uint32_t sampled_image,
|
||||
uint32_t coord, ImageViewKind view);
|
||||
uint32_t EmitImagePackedOffsetI32(EmitterState& state, const IR::Instruction& inst,
|
||||
const ImageSampleLayout& layout, ImageViewKind view);
|
||||
|
||||
uint32_t EmitImageCoordU32(EmitterState& state, const IR::Instruction& inst,
|
||||
ImageViewKind view = ImageViewKind::Dim2D);
|
||||
|
||||
@@ -324,36 +324,22 @@ void AddDescriptorAnnotationsAndNames(EmitterState& state) {
|
||||
IR::DescriptorBindingKind::AddressMemory);
|
||||
}
|
||||
constexpr const char* SampledNames[] = {
|
||||
"sampled_2d", "sampled_2d_array", "sampled_3d",
|
||||
"sampled_1d", "sampled_1d_array", "sampled_2d", "sampled_2d_array",
|
||||
"sampled_3d", "sampled_uint_1d", "sampled_uint_1d_array",
|
||||
"sampled_uint_2d", "sampled_uint_2d_array", "sampled_uint_3d"};
|
||||
for (uint32_t i = 0; i < state.sampled_images.size(); i++) {
|
||||
const auto view = static_cast<ImageViewKind>(i % 3u);
|
||||
const auto view = static_cast<ImageViewKind>(i % ImageViewKindCount);
|
||||
Decorate(state.sampled_images[i].variable, SampledNames[i],
|
||||
SampledBindingKind(i >= 3u, view));
|
||||
SampledBindingKind(i >= ImageViewKindCount, view));
|
||||
}
|
||||
if (state.storage_image_variable != 0) {
|
||||
Decorate(state.storage_image_variable, "textures2D_L",
|
||||
IR::DescriptorBindingKind::Storage2D);
|
||||
}
|
||||
if (state.storage_image_uint_variable != 0) {
|
||||
Decorate(state.storage_image_uint_variable, "textures2D_L_U",
|
||||
IR::DescriptorBindingKind::StorageUint2D);
|
||||
}
|
||||
if (state.storage_image_2d_array_variable != 0) {
|
||||
Decorate(state.storage_image_2d_array_variable, "textures2D_L_A",
|
||||
IR::DescriptorBindingKind::Storage2DArray);
|
||||
}
|
||||
if (state.storage_image_uint_2d_array_variable != 0) {
|
||||
Decorate(state.storage_image_uint_2d_array_variable, "textures2D_L_U_A",
|
||||
IR::DescriptorBindingKind::StorageUint2DArray);
|
||||
}
|
||||
if (state.storage_image_3d_variable != 0) {
|
||||
Decorate(state.storage_image_3d_variable, "textures2D_L_3D",
|
||||
IR::DescriptorBindingKind::Storage3D);
|
||||
}
|
||||
if (state.storage_image_uint_3d_variable != 0) {
|
||||
Decorate(state.storage_image_uint_3d_variable, "textures2D_L_U_3D",
|
||||
IR::DescriptorBindingKind::StorageUint3D);
|
||||
constexpr const char* StorageNames[] = {
|
||||
"storage_1d", "storage_1d_array", "storage_2d", "storage_2d_array",
|
||||
"storage_3d", "storage_uint_1d", "storage_uint_1d_array",
|
||||
"storage_uint_2d", "storage_uint_2d_array", "storage_uint_3d"};
|
||||
for (uint32_t i = 0; i < state.storage_images.size(); i++) {
|
||||
const auto view = static_cast<ImageViewKind>(i % ImageViewKindCount);
|
||||
Decorate(state.storage_images[i].variable, StorageNames[i],
|
||||
StorageBindingKind(i >= ImageViewKindCount, view));
|
||||
}
|
||||
if (state.sampler_variable != 0) {
|
||||
Decorate(state.sampler_variable, "samplers", IR::DescriptorBindingKind::Samplers);
|
||||
@@ -452,34 +438,16 @@ void EmitHeaderAndTypes(EmitterState& state) {
|
||||
image.array_type = state.builder.AllocateId();
|
||||
image.array_pointer_type = state.builder.AllocateId();
|
||||
}
|
||||
for (auto& image: state.storage_images) {
|
||||
image.image_type = state.builder.AllocateId();
|
||||
image.pointer_type = state.builder.AllocateId();
|
||||
image.array_type = state.builder.AllocateId();
|
||||
image.array_pointer_type = state.builder.AllocateId();
|
||||
}
|
||||
state.sampler_type = state.builder.AllocateId();
|
||||
state.sampler_array_type = state.builder.AllocateId();
|
||||
state.ptr_uniform_sampler = state.builder.AllocateId();
|
||||
state.ptr_uniform_sampler_array = state.builder.AllocateId();
|
||||
state.storage_image_type = state.builder.AllocateId();
|
||||
state.ptr_uniform_storage_image = state.builder.AllocateId();
|
||||
state.storage_image_array_type = state.builder.AllocateId();
|
||||
state.ptr_uniform_storage_image_array = state.builder.AllocateId();
|
||||
state.storage_image_2d_array_type = state.builder.AllocateId();
|
||||
state.ptr_uniform_storage_image_2d_array = state.builder.AllocateId();
|
||||
state.storage_image_2d_array_array_type = state.builder.AllocateId();
|
||||
state.ptr_uniform_storage_image_2d_array_array = state.builder.AllocateId();
|
||||
state.storage_image_3d_type = state.builder.AllocateId();
|
||||
state.ptr_uniform_storage_image_3d = state.builder.AllocateId();
|
||||
state.storage_image_3d_array_type = state.builder.AllocateId();
|
||||
state.ptr_uniform_storage_image_3d_array = state.builder.AllocateId();
|
||||
state.storage_image_uint_type = state.builder.AllocateId();
|
||||
state.ptr_uniform_storage_image_uint = state.builder.AllocateId();
|
||||
state.storage_image_uint_array_type = state.builder.AllocateId();
|
||||
state.ptr_uniform_storage_image_uint_array = state.builder.AllocateId();
|
||||
state.storage_image_uint_2d_array_type = state.builder.AllocateId();
|
||||
state.ptr_uniform_storage_image_uint_2d_array = state.builder.AllocateId();
|
||||
state.storage_image_uint_2d_array_array_type = state.builder.AllocateId();
|
||||
state.ptr_uniform_storage_image_uint_2d_array_array = state.builder.AllocateId();
|
||||
state.storage_image_uint_3d_type = state.builder.AllocateId();
|
||||
state.ptr_uniform_storage_image_uint_3d = state.builder.AllocateId();
|
||||
state.storage_image_uint_3d_array_type = state.builder.AllocateId();
|
||||
state.ptr_uniform_storage_image_uint_3d_array = state.builder.AllocateId();
|
||||
state.ptr_image_uint = state.builder.AllocateId();
|
||||
state.func_type = state.builder.AllocateId();
|
||||
state.main_func = state.builder.AllocateId();
|
||||
@@ -487,12 +455,15 @@ void EmitHeaderAndTypes(EmitterState& state) {
|
||||
state.glsl_std450 = state.builder.AllocateId();
|
||||
|
||||
state.builder.AddCapability({CapabilityShader});
|
||||
state.builder.AddCapability({CapabilitySampled1D});
|
||||
state.builder.AddCapability({CapabilityImage1D});
|
||||
state.builder.AddCapability({CapabilityImageQuery});
|
||||
if (state.needs_image_gather_extended) {
|
||||
state.builder.AddCapability({CapabilityImageGatherExtended});
|
||||
}
|
||||
if (state.storage_image_variable != 0 || state.storage_image_2d_array_variable != 0 ||
|
||||
state.storage_image_3d_variable != 0) {
|
||||
if (std::any_of(state.storage_images.begin(),
|
||||
state.storage_images.begin() + ImageViewKindCount,
|
||||
[](const auto& image) { return image.variable != 0; })) {
|
||||
state.builder.AddCapability({CapabilityStorageImageReadWithoutFormat});
|
||||
state.builder.AddCapability({CapabilityStorageImageWriteWithoutFormat});
|
||||
}
|
||||
@@ -729,17 +700,17 @@ void EmitHeaderAndTypes(EmitterState& state) {
|
||||
}
|
||||
for (uint32_t i = 0; i < state.sampled_images.size(); i++) {
|
||||
auto& image = state.sampled_images[i];
|
||||
const auto view = static_cast<ImageViewKind>(i % 3u);
|
||||
const auto component = i >= 3u ? state.uint_type : state.float_type;
|
||||
const auto dimension = view == ImageViewKind::Dim3D ? Dim3D : Dim2D;
|
||||
const auto arrayed = view == ImageViewKind::Dim2DArray ? 1u : 0u;
|
||||
state.builder.AddType({OpTypeImage, image.image_type, component, dimension, 0, arrayed, 0,
|
||||
1, ImageFormatUnknown});
|
||||
const auto view = static_cast<ImageViewKind>(i % ImageViewKindCount);
|
||||
const bool integer = i >= ImageViewKindCount;
|
||||
const auto component = integer ? state.uint_type : state.float_type;
|
||||
state.builder.AddType({OpTypeImage, image.image_type, component,
|
||||
ImageSpirvDimension(view), 0, ImageSpirvArrayed(view), 0, 1,
|
||||
ImageFormatUnknown});
|
||||
state.builder.AddType({OpTypeSampledImage, image.sampled_image_type, image.image_type});
|
||||
state.builder.AddType(
|
||||
{OpTypePointer, image.pointer_type, StorageClassUniformConstant, image.image_type});
|
||||
if (image.variable != 0) {
|
||||
const auto kind = SampledBindingKind(i >= 3u, view);
|
||||
const auto kind = SampledBindingKind(integer, view);
|
||||
const auto count = ConstantU32(state, DescriptorCount(state, kind));
|
||||
state.builder.AddType({OpTypeArray, image.array_type, image.image_type, count});
|
||||
state.builder.AddType({OpTypePointer, image.array_pointer_type,
|
||||
@@ -760,93 +731,26 @@ void EmitHeaderAndTypes(EmitterState& state) {
|
||||
state.builder.AddType({OpVariable, state.ptr_uniform_sampler_array, state.sampler_variable,
|
||||
StorageClassUniformConstant});
|
||||
}
|
||||
state.builder.AddType({OpTypeImage, state.storage_image_type, state.float_type, Dim2D, 0, 0, 0,
|
||||
2, ImageFormatUnknown});
|
||||
state.builder.AddType({OpTypePointer, state.ptr_uniform_storage_image,
|
||||
StorageClassUniformConstant, state.storage_image_type});
|
||||
if (state.storage_image_variable != 0) {
|
||||
const auto count =
|
||||
ConstantU32(state, DescriptorCount(state, IR::DescriptorBindingKind::Storage2D));
|
||||
for (uint32_t i = 0; i < state.storage_images.size(); i++) {
|
||||
auto& image = state.storage_images[i];
|
||||
const auto view = static_cast<ImageViewKind>(i % ImageViewKindCount);
|
||||
const bool integer = i >= ImageViewKindCount;
|
||||
const auto component = integer ? state.uint_type : state.float_type;
|
||||
const auto format = integer ? ImageFormatR32ui : ImageFormatUnknown;
|
||||
state.builder.AddType({OpTypeImage, image.image_type, component,
|
||||
ImageSpirvDimension(view), 0, ImageSpirvArrayed(view), 0, 2,
|
||||
format});
|
||||
state.builder.AddType(
|
||||
{OpTypeArray, state.storage_image_array_type, state.storage_image_type, count});
|
||||
state.builder.AddType({OpTypePointer, state.ptr_uniform_storage_image_array,
|
||||
StorageClassUniformConstant, state.storage_image_array_type});
|
||||
state.builder.AddType({OpVariable, state.ptr_uniform_storage_image_array,
|
||||
state.storage_image_variable, StorageClassUniformConstant});
|
||||
}
|
||||
state.builder.AddType({OpTypeImage, state.storage_image_2d_array_type, state.float_type, Dim2D,
|
||||
0, 1, 0, 2, ImageFormatUnknown});
|
||||
state.builder.AddType({OpTypePointer, state.ptr_uniform_storage_image_2d_array,
|
||||
StorageClassUniformConstant, state.storage_image_2d_array_type});
|
||||
if (state.storage_image_2d_array_variable != 0) {
|
||||
const auto count =
|
||||
ConstantU32(state, DescriptorCount(state, IR::DescriptorBindingKind::Storage2DArray));
|
||||
state.builder.AddType({OpTypeArray, state.storage_image_2d_array_array_type,
|
||||
state.storage_image_2d_array_type, count});
|
||||
state.builder.AddType({OpTypePointer, state.ptr_uniform_storage_image_2d_array_array,
|
||||
StorageClassUniformConstant,
|
||||
state.storage_image_2d_array_array_type});
|
||||
state.builder.AddType({OpVariable, state.ptr_uniform_storage_image_2d_array_array,
|
||||
state.storage_image_2d_array_variable, StorageClassUniformConstant});
|
||||
}
|
||||
state.builder.AddType({OpTypeImage, state.storage_image_3d_type, state.float_type, Dim3D, 0, 0,
|
||||
0, 2, ImageFormatUnknown});
|
||||
state.builder.AddType({OpTypePointer, state.ptr_uniform_storage_image_3d,
|
||||
StorageClassUniformConstant, state.storage_image_3d_type});
|
||||
if (state.storage_image_3d_variable != 0) {
|
||||
const auto count =
|
||||
ConstantU32(state, DescriptorCount(state, IR::DescriptorBindingKind::Storage3D));
|
||||
state.builder.AddType(
|
||||
{OpTypeArray, state.storage_image_3d_array_type, state.storage_image_3d_type, count});
|
||||
state.builder.AddType({OpTypePointer, state.ptr_uniform_storage_image_3d_array,
|
||||
StorageClassUniformConstant, state.storage_image_3d_array_type});
|
||||
state.builder.AddType({OpVariable, state.ptr_uniform_storage_image_3d_array,
|
||||
state.storage_image_3d_variable, StorageClassUniformConstant});
|
||||
}
|
||||
state.builder.AddType({OpTypeImage, state.storage_image_uint_type, state.uint_type, Dim2D, 0, 0,
|
||||
0, 2, ImageFormatR32ui});
|
||||
state.builder.AddType({OpTypePointer, state.ptr_uniform_storage_image_uint,
|
||||
StorageClassUniformConstant, state.storage_image_uint_type});
|
||||
if (state.storage_image_uint_variable != 0) {
|
||||
const auto count =
|
||||
ConstantU32(state, DescriptorCount(state, IR::DescriptorBindingKind::StorageUint2D));
|
||||
state.builder.AddType({OpTypeArray, state.storage_image_uint_array_type,
|
||||
state.storage_image_uint_type, count});
|
||||
state.builder.AddType({OpTypePointer, state.ptr_uniform_storage_image_uint_array,
|
||||
StorageClassUniformConstant, state.storage_image_uint_array_type});
|
||||
state.builder.AddType({OpVariable, state.ptr_uniform_storage_image_uint_array,
|
||||
state.storage_image_uint_variable, StorageClassUniformConstant});
|
||||
}
|
||||
state.builder.AddType({OpTypeImage, state.storage_image_uint_2d_array_type, state.uint_type,
|
||||
Dim2D, 0, 1, 0, 2, ImageFormatR32ui});
|
||||
state.builder.AddType({OpTypePointer, state.ptr_uniform_storage_image_uint_2d_array,
|
||||
StorageClassUniformConstant, state.storage_image_uint_2d_array_type});
|
||||
if (state.storage_image_uint_2d_array_variable != 0) {
|
||||
const auto count = ConstantU32(
|
||||
state, DescriptorCount(state, IR::DescriptorBindingKind::StorageUint2DArray));
|
||||
state.builder.AddType({OpTypeArray, state.storage_image_uint_2d_array_array_type,
|
||||
state.storage_image_uint_2d_array_type, count});
|
||||
state.builder.AddType({OpTypePointer, state.ptr_uniform_storage_image_uint_2d_array_array,
|
||||
StorageClassUniformConstant,
|
||||
state.storage_image_uint_2d_array_array_type});
|
||||
state.builder.AddType({OpVariable, state.ptr_uniform_storage_image_uint_2d_array_array,
|
||||
state.storage_image_uint_2d_array_variable,
|
||||
StorageClassUniformConstant});
|
||||
}
|
||||
state.builder.AddType({OpTypeImage, state.storage_image_uint_3d_type, state.uint_type, Dim3D, 0,
|
||||
0, 0, 2, ImageFormatR32ui});
|
||||
state.builder.AddType({OpTypePointer, state.ptr_uniform_storage_image_uint_3d,
|
||||
StorageClassUniformConstant, state.storage_image_uint_3d_type});
|
||||
if (state.storage_image_uint_3d_variable != 0) {
|
||||
const auto count =
|
||||
ConstantU32(state, DescriptorCount(state, IR::DescriptorBindingKind::StorageUint3D));
|
||||
state.builder.AddType({OpTypeArray, state.storage_image_uint_3d_array_type,
|
||||
state.storage_image_uint_3d_type, count});
|
||||
state.builder.AddType({OpTypePointer, state.ptr_uniform_storage_image_uint_3d_array,
|
||||
StorageClassUniformConstant,
|
||||
state.storage_image_uint_3d_array_type});
|
||||
state.builder.AddType({OpVariable, state.ptr_uniform_storage_image_uint_3d_array,
|
||||
state.storage_image_uint_3d_variable, StorageClassUniformConstant});
|
||||
{OpTypePointer, image.pointer_type, StorageClassUniformConstant, image.image_type});
|
||||
if (image.variable != 0) {
|
||||
const auto kind = StorageBindingKind(integer, view);
|
||||
const auto count = ConstantU32(state, DescriptorCount(state, kind));
|
||||
state.builder.AddType({OpTypeArray, image.array_type, image.image_type, count});
|
||||
state.builder.AddType({OpTypePointer, image.array_pointer_type,
|
||||
StorageClassUniformConstant, image.array_type});
|
||||
state.builder.AddType({OpVariable, image.array_pointer_type, image.variable,
|
||||
StorageClassUniformConstant});
|
||||
}
|
||||
}
|
||||
state.builder.AddType(
|
||||
{OpTypePointer, state.ptr_image_uint, StorageClassImage, state.uint_type});
|
||||
@@ -882,28 +786,18 @@ void AllocateDescriptorVariables(EmitterState& state) {
|
||||
state.flattened_srt_variable = state.builder.AllocateId();
|
||||
}
|
||||
for (uint32_t i = 0; i < state.sampled_images.size(); i++) {
|
||||
const auto view = static_cast<ImageViewKind>(i % 3u);
|
||||
if (DescriptorBinding(state, SampledBindingKind(i >= 3u, view)) != nullptr) {
|
||||
const auto view = static_cast<ImageViewKind>(i % ImageViewKindCount);
|
||||
if (DescriptorBinding(state, SampledBindingKind(i >= ImageViewKindCount, view)) !=
|
||||
nullptr) {
|
||||
state.sampled_images[i].variable = state.builder.AllocateId();
|
||||
}
|
||||
}
|
||||
if (DescriptorBinding(state, IR::DescriptorBindingKind::Storage2D) != nullptr) {
|
||||
state.storage_image_variable = state.builder.AllocateId();
|
||||
}
|
||||
if (DescriptorBinding(state, IR::DescriptorBindingKind::StorageUint2D) != nullptr) {
|
||||
state.storage_image_uint_variable = state.builder.AllocateId();
|
||||
}
|
||||
if (DescriptorBinding(state, IR::DescriptorBindingKind::Storage2DArray) != nullptr) {
|
||||
state.storage_image_2d_array_variable = state.builder.AllocateId();
|
||||
}
|
||||
if (DescriptorBinding(state, IR::DescriptorBindingKind::StorageUint2DArray) != nullptr) {
|
||||
state.storage_image_uint_2d_array_variable = state.builder.AllocateId();
|
||||
}
|
||||
if (DescriptorBinding(state, IR::DescriptorBindingKind::Storage3D) != nullptr) {
|
||||
state.storage_image_3d_variable = state.builder.AllocateId();
|
||||
}
|
||||
if (DescriptorBinding(state, IR::DescriptorBindingKind::StorageUint3D) != nullptr) {
|
||||
state.storage_image_uint_3d_variable = state.builder.AllocateId();
|
||||
for (uint32_t i = 0; i < state.storage_images.size(); i++) {
|
||||
const auto view = static_cast<ImageViewKind>(i % ImageViewKindCount);
|
||||
if (DescriptorBinding(state, StorageBindingKind(i >= ImageViewKindCount, view)) !=
|
||||
nullptr) {
|
||||
state.storage_images[i].variable = state.builder.AllocateId();
|
||||
}
|
||||
}
|
||||
if (DescriptorBinding(state, IR::DescriptorBindingKind::Samplers) != nullptr) {
|
||||
state.sampler_variable = state.builder.AllocateId();
|
||||
|
||||
@@ -1126,7 +1126,7 @@ void TestResourceSpecializationIsTypedAndTransactional() {
|
||||
null_program.stage = ShaderType::Compute;
|
||||
null_program.blocks.resize(1);
|
||||
null_program.blocks[0].instructions = {
|
||||
ImageUse(0x10, Opcode::ImageLoad, ResourceKind::Image,
|
||||
ImageUse(0x10, Opcode::ImageLoad, ResourceKind::ImageUint,
|
||||
Decoder::ImageDimension::Dim3D)};
|
||||
Prepare(null_program);
|
||||
ResourceSnapshot null_snapshot;
|
||||
@@ -1141,8 +1141,81 @@ void TestResourceSpecializationIsTypedAndTransactional() {
|
||||
ValidateResourceSpecialization(null_program, null_snapshot, &error) &&
|
||||
null_program.info.images[0].kind == ResourceKind::Image &&
|
||||
null_program.info.images[0].dimension ==
|
||||
Decoder::ImageDimension::Dim3D,
|
||||
"zero-base image descriptor did not preserve tracked null-image shape");
|
||||
Decoder::ImageDimension::Dim2D &&
|
||||
null_program.blocks[0].instructions[0].memory.image_dimension ==
|
||||
Decoder::ImageDimension::Dim2D,
|
||||
"zero-base image descriptor did not use the canonical null-image shape");
|
||||
|
||||
Program null_atomic;
|
||||
null_atomic.stage = ShaderType::Compute;
|
||||
null_atomic.blocks.resize(1);
|
||||
null_atomic.blocks[0].instructions = {
|
||||
ImageUse(0x14, Opcode::AtomicAddU32, ResourceKind::StorageImageUint,
|
||||
Decoder::ImageDimension::Dim1D)};
|
||||
Prepare(null_atomic);
|
||||
Check(SpecializeResources(null_atomic, null_snapshot, &error) &&
|
||||
ValidateResourceSpecialization(null_atomic, null_snapshot, &error) &&
|
||||
null_atomic.info.images[0].kind ==
|
||||
ResourceKind::StorageImageUint &&
|
||||
null_atomic.info.images[0].dimension ==
|
||||
Decoder::ImageDimension::Dim2D,
|
||||
"null image atomic did not preserve its required integer storage type");
|
||||
|
||||
Program array_view;
|
||||
array_view.stage = ShaderType::Compute;
|
||||
array_view.blocks.resize(1);
|
||||
array_view.blocks[0].instructions = {
|
||||
ImageUse(0x18, Opcode::ImageLoad, ResourceKind::Image,
|
||||
Decoder::ImageDimension::Dim1D)};
|
||||
Prepare(array_view);
|
||||
ResourceSnapshot array_snapshot;
|
||||
array_snapshot.images.resize(1);
|
||||
array_snapshot.images[0].dword_count = 8;
|
||||
array_snapshot.images[0].dwords[0] = 0x1000;
|
||||
array_snapshot.images[0].dwords[3] =
|
||||
Prospero::GpuEnumValue(Prospero::ImageType::kColor1DArray) << 28u;
|
||||
Check(SpecializeResources(array_view, array_snapshot, &error) &&
|
||||
array_view.info.images[0].dimension ==
|
||||
Decoder::ImageDimension::Dim1D &&
|
||||
array_view.blocks[0].instructions[0].memory.image_dimension ==
|
||||
Decoder::ImageDimension::Dim1D,
|
||||
"non-array MIMG view did not narrow a 1D-array descriptor");
|
||||
auto null_after_1d = array_snapshot;
|
||||
null_after_1d.images[0].dwords.fill(0);
|
||||
Check(!ValidateResourceSpecialization(array_view, null_after_1d, &error) &&
|
||||
error.find("canonical null specialization") != std::string::npos,
|
||||
"non-null 1D specialization accepted a canonical 2D null descriptor");
|
||||
|
||||
Program cross_family_array;
|
||||
cross_family_array.stage = ShaderType::Compute;
|
||||
cross_family_array.blocks.resize(1);
|
||||
cross_family_array.blocks[0].instructions = {
|
||||
ImageUse(0x1c, Opcode::ImageLoad, ResourceKind::Image,
|
||||
Decoder::ImageDimension::Dim2DArray)};
|
||||
Prepare(cross_family_array);
|
||||
Check(SpecializeResources(cross_family_array, array_snapshot, &error) &&
|
||||
cross_family_array.info.images[0].dimension ==
|
||||
Decoder::ImageDimension::Dim1DArray &&
|
||||
cross_family_array.blocks[0]
|
||||
.instructions[0]
|
||||
.memory.image_dimension ==
|
||||
Decoder::ImageDimension::Dim1DArray,
|
||||
"array MIMG intent did not produce a 1D-array view");
|
||||
|
||||
Program cross_family_2d_array;
|
||||
cross_family_2d_array.stage = ShaderType::Compute;
|
||||
cross_family_2d_array.blocks.resize(1);
|
||||
cross_family_2d_array.blocks[0].instructions = {
|
||||
ImageUse(0x20, Opcode::ImageLoad, ResourceKind::Image,
|
||||
Decoder::ImageDimension::Dim1DArray)};
|
||||
Prepare(cross_family_2d_array);
|
||||
auto array_2d_snapshot = array_snapshot;
|
||||
array_2d_snapshot.images[0].dwords[3] =
|
||||
Prospero::GpuEnumValue(Prospero::ImageType::kColor2DArray) << 28u;
|
||||
Check(SpecializeResources(cross_family_2d_array, array_2d_snapshot, &error) &&
|
||||
cross_family_2d_array.info.images[0].dimension ==
|
||||
Decoder::ImageDimension::Dim2DArray,
|
||||
"array MIMG intent did not produce a 2D-array view");
|
||||
|
||||
Program program;
|
||||
program.stage = ShaderType::Compute;
|
||||
@@ -1182,6 +1255,11 @@ void TestResourceSpecializationIsTypedAndTransactional() {
|
||||
inst.memory.image_dimension == Decoder::ImageDimension::Dim2D,
|
||||
"runtime descriptor shape and integer format did not specialize dense "
|
||||
"IR");
|
||||
auto null_after_uint = snapshot;
|
||||
null_after_uint.images[0].dwords.fill(0);
|
||||
Check(!ValidateResourceSpecialization(program, null_after_uint, &error) &&
|
||||
error.find("canonical null specialization") != std::string::npos,
|
||||
"non-null integer specialization accepted a non-integer null descriptor");
|
||||
|
||||
auto stale_swizzle = snapshot;
|
||||
stale_swizzle.images[0].dwords[3] =
|
||||
@@ -1438,6 +1516,45 @@ void TestNativeBindingLayout() {
|
||||
"native binding allocator did not preserve dense typed resource groups");
|
||||
}
|
||||
|
||||
void TestNativeBindingLayoutOneDimensionalImages() {
|
||||
Program program;
|
||||
program.stage = ShaderType::Compute;
|
||||
program.blocks.resize(1);
|
||||
auto &insts = program.blocks[0].instructions;
|
||||
insts.push_back(ImageUse(8, Opcode::ImageSample, ResourceKind::Image,
|
||||
Decoder::ImageDimension::Dim1D, 0, 2));
|
||||
insts.push_back(ImageUse(12, Opcode::ImageSample, ResourceKind::Image,
|
||||
Decoder::ImageDimension::Dim1DArray, 0, 2));
|
||||
insts.push_back(ImageUse(16, Opcode::ImageStore, ResourceKind::StorageImage,
|
||||
Decoder::ImageDimension::Dim1D, 4));
|
||||
insts.push_back(ImageUse(20, Opcode::ImageStore,
|
||||
ResourceKind::StorageImageUint,
|
||||
Decoder::ImageDimension::Dim1DArray, 6));
|
||||
Prepare(program);
|
||||
|
||||
ShaderComputeInputInfo compute;
|
||||
compute.thread_ids_num = 1;
|
||||
std::string error;
|
||||
Check(CollectShaderInfo(program, {.compute = &compute}, &error) &&
|
||||
AllocateBindings(program, {}, &error),
|
||||
error.c_str());
|
||||
const auto *sampled =
|
||||
FindBinding(program.bindings, DescriptorBindingKind::Sampled1D);
|
||||
const auto *sampled_array =
|
||||
FindBinding(program.bindings, DescriptorBindingKind::Sampled1DArray);
|
||||
const auto *storage =
|
||||
FindBinding(program.bindings, DescriptorBindingKind::Storage1D);
|
||||
const auto *storage_uint_array =
|
||||
FindBinding(program.bindings, DescriptorBindingKind::StorageUint1DArray);
|
||||
Check(sampled != nullptr && sampled_array != nullptr && storage != nullptr &&
|
||||
storage_uint_array != nullptr &&
|
||||
sampled->resources == std::vector<uint32_t>{0} &&
|
||||
sampled_array->resources == std::vector<uint32_t>{1} &&
|
||||
storage->resources == std::vector<uint32_t>{2} &&
|
||||
storage_uint_array->resources == std::vector<uint32_t>{3},
|
||||
"binding allocator did not preserve first-class 1D image groups");
|
||||
}
|
||||
|
||||
void TestNativeBindingLayoutSrtAndUserDataOverflow() {
|
||||
Program srt;
|
||||
srt.stage = ShaderType::Compute;
|
||||
@@ -1948,6 +2065,7 @@ int main() {
|
||||
RUN(TestRuntimeSpecializationCoversBakedBufferAndAddressFields);
|
||||
RUN(TestTrackedProgramIsImmutable);
|
||||
RUN(TestNativeBindingLayout);
|
||||
RUN(TestNativeBindingLayoutOneDimensionalImages);
|
||||
RUN(TestNativeBindingLayoutSrtAndUserDataOverflow);
|
||||
RUN(TestNativeBindingLayoutGds);
|
||||
RUN(TestNativeBindingLayoutIsTransactional);
|
||||
|
||||
@@ -218,6 +218,13 @@ struct TextureCacheTestAccess {
|
||||
};
|
||||
|
||||
struct RenderExecutorTestAccess {
|
||||
static DescriptorCache::TextureBinding
|
||||
ResolveTexture(RenderExecutor &executor,
|
||||
const ShaderRecompiler::IR::ImageResource &resource,
|
||||
const ShaderRecompiler::IR::DescriptorValue &value) {
|
||||
return executor.ResolveTexture(resource, value);
|
||||
}
|
||||
|
||||
static auto PrepareGraphicsBindings(RenderExecutor &executor,
|
||||
CommandBuffer &buffer,
|
||||
const ShaderStageRuntime &vertex,
|
||||
@@ -706,6 +713,11 @@ struct TestCase {
|
||||
std::vector<std::vector<u32>> sampled_image_rgba_mips;
|
||||
vk::Format sampled_image_format = vk::Format::eR32G32B32A32Sfloat;
|
||||
u32 sampled_image_dwords_per_pixel = 4;
|
||||
vk::ImageType sampled_image_type = vk::ImageType::e2D;
|
||||
vk::ImageViewType sampled_image_view_type = vk::ImageViewType::e2D;
|
||||
u32 sampled_image_layers = 1;
|
||||
u32 sampled_image_view_base_layer = 0;
|
||||
u32 sampled_image_view_layers = 0;
|
||||
std::vector<u32> storage_image_rgba;
|
||||
std::vector<u32> expected_storage_image_rgba;
|
||||
vk::Format storage_image_format = vk::Format::eR32G32B32A32Sfloat;
|
||||
@@ -1111,6 +1123,7 @@ public:
|
||||
vk::ImageLayout layout = vk::ImageLayout::eUndefined;
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u32 layers = 1;
|
||||
u32 mip_levels = 1;
|
||||
u32 dwords_per_pixel = 0;
|
||||
};
|
||||
@@ -1836,6 +1849,50 @@ public:
|
||||
!ImageViewOps::FormatsCompatible(vk::Format::eD32Sfloat,
|
||||
vk::Format::eR32Sfloat),
|
||||
"role-free image backing or host format compatibility diverged");
|
||||
|
||||
auto line_info = color_info;
|
||||
line_info.type = Prospero::ImageType::kColor1D;
|
||||
line_info.extent = {8, 1, 1};
|
||||
line_info.resources = {2, 2};
|
||||
line_info.mip_layout[0] = {0, 64, 8, 1};
|
||||
line_info.mip_layout[1] = {64, 16, 4, 1};
|
||||
Libs::Graphics::Image line(m_runtime_context, scheduler, line_info);
|
||||
auto line_view_info = sampled;
|
||||
line_view_info.type = vk::ImageViewType::e1D;
|
||||
const auto line_view = line.FindView(line_view_info);
|
||||
auto line_array_info = line_view_info;
|
||||
line_array_info.type = vk::ImageViewType::e1DArray;
|
||||
line_array_info.layer_count = 2;
|
||||
const auto line_array = line.FindView(line_array_info);
|
||||
Require(name, "1D views",
|
||||
line_view != nullptr && line_array != nullptr &&
|
||||
line_array != line_view &&
|
||||
line.backing.image_type == vk::ImageType::e1D &&
|
||||
line.backing.layers == 2,
|
||||
"1D and 1D-array views did not use a first-class 1D backing");
|
||||
|
||||
auto volume_info = color_info;
|
||||
volume_info.type = Prospero::ImageType::kColor3D;
|
||||
volume_info.extent = {8, 8, 4};
|
||||
volume_info.resources = {2, 1};
|
||||
Libs::Graphics::Image volume(m_runtime_context, scheduler, volume_info);
|
||||
auto slice_info = sampled;
|
||||
slice_info.type = vk::ImageViewType::e2D;
|
||||
slice_info.base_level = 1;
|
||||
slice_info.base_layer = 1;
|
||||
const auto slice = volume.FindView(slice_info);
|
||||
auto slice_array_info = slice_info;
|
||||
slice_array_info.type = vk::ImageViewType::e2DArray;
|
||||
slice_array_info.base_layer = 0;
|
||||
slice_array_info.layer_count = 2;
|
||||
const auto slice_array = volume.FindView(slice_array_info);
|
||||
Require(name, "3D slice views",
|
||||
slice != nullptr && slice_array != nullptr &&
|
||||
slice_array != slice &&
|
||||
static_cast<bool>(
|
||||
volume.backing.flags &
|
||||
vk::ImageCreateFlagBits::e2DArrayCompatible),
|
||||
"2D slice views of a compatible 3D backing were rejected");
|
||||
std::printf("[host] %-32s ok\n", name);
|
||||
}
|
||||
|
||||
@@ -2506,10 +2563,13 @@ public:
|
||||
Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
|
||||
desc.info.mip_layout[0] = {0, size, extent.width, extent.height};
|
||||
desc.view_info.format = format;
|
||||
desc.view_info.type = type == Prospero::ImageType::kColor3D
|
||||
? vk::ImageViewType::e3D
|
||||
: layers > 1 ? vk::ImageViewType::e2DArray
|
||||
: vk::ImageViewType::e2D;
|
||||
if (type == Prospero::ImageType::kColor3D) {
|
||||
desc.view_info.type = vk::ImageViewType::e3D;
|
||||
} else if (layers > 1) {
|
||||
desc.view_info.type = vk::ImageViewType::e2DArray;
|
||||
} else {
|
||||
desc.view_info.type = vk::ImageViewType::e2D;
|
||||
}
|
||||
desc.view_info.aspect = vk::ImageAspectFlagBits::eColor;
|
||||
desc.view_info.layer_count = layers;
|
||||
desc.view_info.usage = vk::ImageUsageFlagBits::eSampled;
|
||||
@@ -4939,6 +4999,11 @@ public:
|
||||
ShaderRecompiler::IR::DescriptorValue null_descriptor{};
|
||||
null_descriptor.dword_count = 8;
|
||||
null_snapshot.images.assign(3, null_descriptor);
|
||||
std::string null_error;
|
||||
Require(name, "null specialization",
|
||||
ShaderRecompiler::IR::SpecializeResources(
|
||||
null_program, null_snapshot, &null_error),
|
||||
null_error.c_str());
|
||||
ShaderStageRuntime null_runtime{
|
||||
std::make_shared<const ShaderRecompiler::IR::Program>(
|
||||
std::move(null_program)),
|
||||
@@ -5038,6 +5103,84 @@ public:
|
||||
std::copy(std::begin(storage.fields), std::end(storage.fields),
|
||||
storage_descriptor.dwords.begin());
|
||||
storage_descriptor.dword_count = 8;
|
||||
auto srgb_storage = storage;
|
||||
constexpr auto srgb_format =
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb);
|
||||
constexpr uint64_t srgb_storage_address = base + 0xc0000;
|
||||
const auto encoded_srgb_storage_address = srgb_storage_address >> 8u;
|
||||
srgb_storage.fields[0] =
|
||||
static_cast<uint32_t>(encoded_srgb_storage_address);
|
||||
srgb_storage.fields[1] =
|
||||
static_cast<uint32_t>(encoded_srgb_storage_address >> 32u) |
|
||||
(srgb_format << 20u);
|
||||
ShaderRecompiler::IR::DescriptorValue srgb_storage_descriptor{};
|
||||
std::copy(std::begin(srgb_storage.fields), std::end(srgb_storage.fields),
|
||||
srgb_storage_descriptor.dwords.begin());
|
||||
srgb_storage_descriptor.dword_count = 8;
|
||||
auto srgb_storage_resource = storage_resource;
|
||||
srgb_storage_resource.kind =
|
||||
ShaderRecompiler::IR::ResourceKind::StorageImage;
|
||||
const auto srgb_storage_binding =
|
||||
RenderExecutorTestAccess::ResolveTexture(
|
||||
executor, srgb_storage_resource, srgb_storage_descriptor);
|
||||
const auto srgb_storage_view =
|
||||
texture_cache.FindTexture(srgb_storage_binding.image_id,
|
||||
srgb_storage_binding.desc);
|
||||
Require(name, "sRGB storage view",
|
||||
srgb_storage_view != nullptr &&
|
||||
srgb_storage_binding.desc.info.pixel_format ==
|
||||
vk::Format::eR8G8B8A8Srgb &&
|
||||
srgb_storage_binding.desc.view_info.format ==
|
||||
vk::Format::eR8G8B8A8Unorm &&
|
||||
texture_cache.GetImage(srgb_storage_binding.image_id)
|
||||
.backing.format ==
|
||||
vk::Format::eR8G8B8A8Srgb,
|
||||
"storage descriptor did not preserve its sRGB backing and "
|
||||
"select an UNORM Vulkan view");
|
||||
|
||||
auto narrowed_storage = storage;
|
||||
constexpr uint64_t narrowed_storage_address = base + 0xd0000;
|
||||
const auto encoded_narrowed_address = narrowed_storage_address >> 8u;
|
||||
narrowed_storage.fields[0] =
|
||||
static_cast<uint32_t>(encoded_narrowed_address);
|
||||
narrowed_storage.fields[1] =
|
||||
static_cast<uint32_t>(encoded_narrowed_address >> 32u) |
|
||||
(stencil_format << 20u);
|
||||
narrowed_storage.fields[3] =
|
||||
DstSel(4, 5, 6, 7) | (linear << 20u) |
|
||||
(Prospero::GpuEnumValue(Prospero::ImageType::kColor1DArray) << 28u);
|
||||
narrowed_storage.fields[4] = 1u | (1u << 16u);
|
||||
ShaderRecompiler::IR::DescriptorValue narrowed_storage_descriptor{};
|
||||
std::copy(std::begin(narrowed_storage.fields),
|
||||
std::end(narrowed_storage.fields),
|
||||
narrowed_storage_descriptor.dwords.begin());
|
||||
narrowed_storage_descriptor.dword_count = 8;
|
||||
auto narrowed_storage_resource = storage_resource;
|
||||
narrowed_storage_resource.dimension =
|
||||
ShaderRecompiler::Decoder::ImageDimension::Dim1D;
|
||||
const auto narrowed_storage_binding =
|
||||
RenderExecutorTestAccess::ResolveTexture(
|
||||
executor, narrowed_storage_resource,
|
||||
narrowed_storage_descriptor);
|
||||
const auto narrowed_storage_view =
|
||||
texture_cache.FindTexture(narrowed_storage_binding.image_id,
|
||||
narrowed_storage_binding.desc);
|
||||
const auto &narrowed_storage_image =
|
||||
texture_cache.GetImage(narrowed_storage_binding.image_id);
|
||||
Require(name, "narrowed 1D array storage view",
|
||||
narrowed_storage_view != nullptr &&
|
||||
narrowed_storage_binding.desc.info.type ==
|
||||
Prospero::ImageType::kColor1D &&
|
||||
narrowed_storage_binding.desc.info.resources.layers == 2 &&
|
||||
narrowed_storage_binding.desc.view_info.type ==
|
||||
vk::ImageViewType::e1D &&
|
||||
narrowed_storage_binding.desc.view_info.base_layer == 1 &&
|
||||
narrowed_storage_binding.desc.view_info.layer_count == 1 &&
|
||||
narrowed_storage_image.backing.image_type ==
|
||||
vk::ImageType::e1D,
|
||||
"non-array 1D specialization did not select the descriptor "
|
||||
"base layer from its 1D-array backing");
|
||||
|
||||
ShaderRecompiler::IR::ResourceSnapshot storage_snapshot{};
|
||||
storage_snapshot.images.push_back(storage_descriptor);
|
||||
ShaderStageRuntime storage_runtime{
|
||||
@@ -6048,8 +6191,9 @@ public:
|
||||
if (!initial.empty()) {
|
||||
mips.push_back(initial);
|
||||
}
|
||||
return CreateImage2DMips(shader_name, width, height, format, usage, mips,
|
||||
dwords_per_pixel, final_layout);
|
||||
return CreateImageMips(shader_name, width, height, format, usage, mips,
|
||||
dwords_per_pixel, final_layout,
|
||||
vk::ImageType::e2D, vk::ImageViewType::e2D, 1);
|
||||
}
|
||||
|
||||
static u32 MipExtent(u32 value, u32 level) {
|
||||
@@ -6060,31 +6204,36 @@ public:
|
||||
}
|
||||
|
||||
static size_t ImageMipDwordCount(u32 width, u32 height, u32 dwords_per_pixel,
|
||||
u32 level) {
|
||||
u32 level, u32 layers = 1) {
|
||||
return static_cast<size_t>(MipExtent(width, level)) *
|
||||
static_cast<size_t>(MipExtent(height, level)) * dwords_per_pixel;
|
||||
static_cast<size_t>(MipExtent(height, level)) * layers *
|
||||
dwords_per_pixel;
|
||||
}
|
||||
|
||||
Image CreateImage2DMips(const char *shader_name, u32 width, u32 height,
|
||||
vk::Format format, vk::ImageUsageFlags usage,
|
||||
const std::vector<std::vector<u32>> &initial_mips,
|
||||
u32 dwords_per_pixel, vk::ImageLayout final_layout) {
|
||||
Image CreateImageMips(const char *shader_name, u32 width, u32 height,
|
||||
vk::Format format, vk::ImageUsageFlags usage,
|
||||
const std::vector<std::vector<u32>> &initial_mips,
|
||||
u32 dwords_per_pixel, vk::ImageLayout final_layout,
|
||||
vk::ImageType image_type, vk::ImageViewType view_type,
|
||||
u32 layers, u32 view_base_layer = 0,
|
||||
u32 view_layers = 0) {
|
||||
Image ret;
|
||||
ret.format = format;
|
||||
ret.width = width;
|
||||
ret.height = height;
|
||||
ret.layers = layers;
|
||||
ret.mip_levels = std::max<u32>(static_cast<u32>(initial_mips.size()), 1u);
|
||||
ret.dwords_per_pixel = dwords_per_pixel;
|
||||
|
||||
vk::ImageCreateInfo image_info{};
|
||||
image_info.sType = vk::StructureType::eImageCreateInfo;
|
||||
image_info.imageType = vk::ImageType::e2D;
|
||||
image_info.imageType = image_type;
|
||||
image_info.format = format;
|
||||
image_info.extent.width = width;
|
||||
image_info.extent.height = height;
|
||||
image_info.extent.depth = 1;
|
||||
image_info.mipLevels = ret.mip_levels;
|
||||
image_info.arrayLayers = 1;
|
||||
image_info.arrayLayers = layers;
|
||||
image_info.samples = vk::SampleCountFlagBits::e1;
|
||||
image_info.tiling = vk::ImageTiling::eOptimal;
|
||||
image_info.usage = usage | vk::ImageUsageFlagBits::eTransferDst |
|
||||
@@ -6120,13 +6269,20 @@ public:
|
||||
vk::ImageViewCreateInfo view_info{};
|
||||
view_info.sType = vk::StructureType::eImageViewCreateInfo;
|
||||
view_info.image = ret.image;
|
||||
view_info.viewType = vk::ImageViewType::e2D;
|
||||
view_info.viewType = view_type;
|
||||
view_info.format = format;
|
||||
view_info.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
|
||||
view_info.subresourceRange.baseMipLevel = 0;
|
||||
view_info.subresourceRange.levelCount = ret.mip_levels;
|
||||
view_info.subresourceRange.baseArrayLayer = 0;
|
||||
view_info.subresourceRange.layerCount = 1;
|
||||
Require(shader_name, "dispatch", view_base_layer < layers,
|
||||
"image view base layer is out of bounds");
|
||||
if (view_layers == 0) {
|
||||
view_layers = layers - view_base_layer;
|
||||
}
|
||||
Require(shader_name, "dispatch", view_layers <= layers - view_base_layer,
|
||||
"image view layer count is out of bounds");
|
||||
view_info.subresourceRange.baseArrayLayer = view_base_layer;
|
||||
view_info.subresourceRange.layerCount = view_layers;
|
||||
RequireVk(shader_name, "dispatch",
|
||||
m_device.createImageView(&view_info, nullptr, &ret.view),
|
||||
"vkCreateImageView");
|
||||
@@ -6135,13 +6291,13 @@ public:
|
||||
size_t total_dwords = 0;
|
||||
for (u32 level = 0; level < ret.mip_levels; level++) {
|
||||
total_dwords +=
|
||||
ImageMipDwordCount(width, height, dwords_per_pixel, level);
|
||||
ImageMipDwordCount(width, height, dwords_per_pixel, level, layers);
|
||||
}
|
||||
std::vector<u32> contents(total_dwords, 0);
|
||||
size_t offset = 0;
|
||||
for (u32 level = 0; level < ret.mip_levels; level++) {
|
||||
const auto level_dwords =
|
||||
ImageMipDwordCount(width, height, dwords_per_pixel, level);
|
||||
ImageMipDwordCount(width, height, dwords_per_pixel, level, layers);
|
||||
const auto &src = initial_mips[level];
|
||||
for (size_t i = 0; i < src.size() && i < level_dwords; i++) {
|
||||
contents[offset + i] = src[i];
|
||||
@@ -6183,6 +6339,7 @@ public:
|
||||
std::vector<u32> ReadImage(const char *shader_name, Image *image) {
|
||||
const auto dword_count = static_cast<size_t>(image->width) *
|
||||
static_cast<size_t>(image->height) *
|
||||
image->layers *
|
||||
image->dwords_per_pixel;
|
||||
auto staging = CreateHostBuffer(shader_name, dword_count * sizeof(u32),
|
||||
vk::BufferUsageFlagBits::eTransferDst, {});
|
||||
@@ -6193,13 +6350,13 @@ public:
|
||||
vk::PipelineStageFlagBits::eAllCommands,
|
||||
vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
|
||||
vk::AccessFlagBits::eTransferRead);
|
||||
vk::AccessFlagBits::eTransferRead, image->mip_levels, image->layers);
|
||||
vk::BufferImageCopy copy{};
|
||||
copy.bufferOffset = 0;
|
||||
copy.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
|
||||
copy.imageSubresource.mipLevel = 0;
|
||||
copy.imageSubresource.baseArrayLayer = 0;
|
||||
copy.imageSubresource.layerCount = 1;
|
||||
copy.imageSubresource.layerCount = image->layers;
|
||||
copy.imageExtent.width = image->width;
|
||||
copy.imageExtent.height = image->height;
|
||||
copy.imageExtent.depth = 1;
|
||||
@@ -6267,19 +6424,28 @@ public:
|
||||
};
|
||||
auto Count = [&](Kind kind) {
|
||||
const auto *binding = Binding(kind);
|
||||
return binding == nullptr ? 0u
|
||||
: kind == Kind::Gds ? 1u
|
||||
: static_cast<u32>(binding->resources.size());
|
||||
if (binding == nullptr) {
|
||||
return 0u;
|
||||
}
|
||||
if (kind == Kind::Gds) {
|
||||
return 1u;
|
||||
}
|
||||
return static_cast<u32>(binding->resources.size());
|
||||
};
|
||||
Require(test.name, "dispatch",
|
||||
Count(Kind::Sampled2DArray) == 0 && Count(Kind::Sampled3D) == 0 &&
|
||||
Count(Kind::Sampled2DArray) == 0 &&
|
||||
Count(Kind::Sampled3D) == 0 &&
|
||||
Count(Kind::SampledUint2DArray) == 0 &&
|
||||
Count(Kind::SampledUint3D) == 0 &&
|
||||
Count(Kind::Storage1D) == 0 &&
|
||||
Count(Kind::Storage1DArray) == 0 &&
|
||||
Count(Kind::Storage2DArray) == 0 &&
|
||||
Count(Kind::Storage3D) == 0 &&
|
||||
Count(Kind::StorageUint1D) == 0 &&
|
||||
Count(Kind::StorageUint1DArray) == 0 &&
|
||||
Count(Kind::StorageUint2DArray) == 0 &&
|
||||
Count(Kind::StorageUint3D) == 0,
|
||||
"array/3D image cases must provide matching Vulkan test views "
|
||||
"unsupported array/3D image cases must provide matching Vulkan test views "
|
||||
"before dispatch");
|
||||
|
||||
vk::ShaderModuleCreateInfo module_info{};
|
||||
@@ -6308,17 +6474,25 @@ public:
|
||||
vk::DescriptorType type = vk::DescriptorType::eStorageBuffer;
|
||||
u32 count = static_cast<u32>(binding.resources.size());
|
||||
switch (binding.kind) {
|
||||
case Kind::Sampled1D:
|
||||
case Kind::Sampled1DArray:
|
||||
case Kind::Sampled2D:
|
||||
case Kind::Sampled2DArray:
|
||||
case Kind::Sampled3D:
|
||||
case Kind::SampledUint1D:
|
||||
case Kind::SampledUint1DArray:
|
||||
case Kind::SampledUint2D:
|
||||
case Kind::SampledUint2DArray:
|
||||
case Kind::SampledUint3D:
|
||||
type = vk::DescriptorType::eSampledImage;
|
||||
break;
|
||||
case Kind::Storage1D:
|
||||
case Kind::Storage1DArray:
|
||||
case Kind::Storage2D:
|
||||
case Kind::Storage2DArray:
|
||||
case Kind::Storage3D:
|
||||
case Kind::StorageUint1D:
|
||||
case Kind::StorageUint1DArray:
|
||||
case Kind::StorageUint2D:
|
||||
case Kind::StorageUint2DArray:
|
||||
case Kind::StorageUint3D:
|
||||
@@ -6402,13 +6576,19 @@ public:
|
||||
(Binding(Kind::FlattenedSrt) != nullptr ? 1u : 0u) +
|
||||
(Binding(Kind::UserData) != nullptr ? 1u : 0u));
|
||||
add_pool_size(vk::DescriptorType::eSampledImage,
|
||||
Count(Kind::Sampled2D) + Count(Kind::Sampled2DArray) +
|
||||
Count(Kind::Sampled3D) + Count(Kind::SampledUint2D) +
|
||||
Count(Kind::Sampled1D) + Count(Kind::Sampled1DArray) +
|
||||
Count(Kind::Sampled2D) + Count(Kind::Sampled2DArray) +
|
||||
Count(Kind::Sampled3D) + Count(Kind::SampledUint1D) +
|
||||
Count(Kind::SampledUint1DArray) +
|
||||
Count(Kind::SampledUint2D) +
|
||||
Count(Kind::SampledUint2DArray) +
|
||||
Count(Kind::SampledUint3D));
|
||||
add_pool_size(vk::DescriptorType::eStorageImage,
|
||||
Count(Kind::Storage2D) + Count(Kind::Storage2DArray) +
|
||||
Count(Kind::Storage3D) + Count(Kind::StorageUint2D) +
|
||||
Count(Kind::Storage1D) + Count(Kind::Storage1DArray) +
|
||||
Count(Kind::Storage2D) + Count(Kind::Storage2DArray) +
|
||||
Count(Kind::Storage3D) + Count(Kind::StorageUint1D) +
|
||||
Count(Kind::StorageUint1DArray) +
|
||||
Count(Kind::StorageUint2D) +
|
||||
Count(Kind::StorageUint2DArray) +
|
||||
Count(Kind::StorageUint3D));
|
||||
add_pool_size(vk::DescriptorType::eSampler, Count(Kind::Samplers));
|
||||
@@ -6529,13 +6709,18 @@ public:
|
||||
write.pBufferInfo = &gds_info;
|
||||
writes.push_back(write);
|
||||
}
|
||||
const auto *sampled = Binding(Kind::Sampled2D);
|
||||
const auto *sampled_uint = Binding(Kind::SampledUint2D);
|
||||
Require(test.name, "dispatch",
|
||||
sampled == nullptr || sampled_uint == nullptr,
|
||||
"Vulkan test harness needs separate float and uint sampled images");
|
||||
if (sampled == nullptr) {
|
||||
sampled = sampled_uint;
|
||||
const ShaderRecompiler::IR::DescriptorBinding *sampled = nullptr;
|
||||
constexpr std::array sampled_kinds {
|
||||
Kind::Sampled1D, Kind::Sampled1DArray, Kind::Sampled2D,
|
||||
Kind::SampledUint1D, Kind::SampledUint1DArray, Kind::SampledUint2D,
|
||||
};
|
||||
for (const auto kind : sampled_kinds) {
|
||||
if (const auto *candidate = Binding(kind); candidate != nullptr) {
|
||||
Require(test.name, "dispatch", sampled == nullptr,
|
||||
"Vulkan test harness needs separate sampled images for mixed "
|
||||
"descriptor classes");
|
||||
sampled = candidate;
|
||||
}
|
||||
}
|
||||
if (sampled != nullptr) {
|
||||
Require(test.name, "dispatch", sampled_image != nullptr,
|
||||
@@ -8138,7 +8323,7 @@ private:
|
||||
vk::PipelineStageFlags src_stage,
|
||||
vk::PipelineStageFlags dst_stage,
|
||||
vk::AccessFlags src_access, vk::AccessFlags dst_access,
|
||||
u32 mip_levels = 1) {
|
||||
u32 mip_levels = 1, u32 layers = 1) {
|
||||
vk::ImageMemoryBarrier barrier{};
|
||||
barrier.sType = vk::StructureType::eImageMemoryBarrier;
|
||||
barrier.srcAccessMask = src_access;
|
||||
@@ -8152,7 +8337,7 @@ private:
|
||||
barrier.subresourceRange.baseMipLevel = 0;
|
||||
barrier.subresourceRange.levelCount = mip_levels;
|
||||
barrier.subresourceRange.baseArrayLayer = 0;
|
||||
barrier.subresourceRange.layerCount = 1;
|
||||
barrier.subresourceRange.layerCount = layers;
|
||||
cmd.pipelineBarrier(src_stage, dst_stage, {}, 0, nullptr, 0, nullptr, 1,
|
||||
&barrier);
|
||||
}
|
||||
@@ -8164,7 +8349,8 @@ private:
|
||||
vk::AccessFlags src_access, vk::AccessFlags dst_access) {
|
||||
vk::CommandBuffer cmd = BeginCommands(shader_name, "dispatch");
|
||||
AddImageBarrier(cmd, image->image, image->layout, new_layout, src_stage,
|
||||
dst_stage, src_access, dst_access, image->mip_levels);
|
||||
dst_stage, src_access, dst_access, image->mip_levels,
|
||||
image->layers);
|
||||
EndSubmitAndFree(shader_name, "dispatch", cmd);
|
||||
image->layout = new_layout;
|
||||
}
|
||||
@@ -8181,7 +8367,8 @@ private:
|
||||
vk::ImageLayout::eTransferDstOptimal,
|
||||
vk::PipelineStageFlagBits::eTopOfPipe,
|
||||
vk::PipelineStageFlagBits::eTransfer, {},
|
||||
vk::AccessFlagBits::eTransferWrite, image->mip_levels);
|
||||
vk::AccessFlagBits::eTransferWrite, image->mip_levels,
|
||||
image->layers);
|
||||
std::vector<vk::BufferImageCopy> copies;
|
||||
copies.reserve(image->mip_levels);
|
||||
vk::DeviceSize offset = 0;
|
||||
@@ -8191,14 +8378,14 @@ private:
|
||||
copy.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
|
||||
copy.imageSubresource.mipLevel = level;
|
||||
copy.imageSubresource.baseArrayLayer = 0;
|
||||
copy.imageSubresource.layerCount = 1;
|
||||
copy.imageSubresource.layerCount = image->layers;
|
||||
copy.imageExtent.width = MipExtent(image->width, level);
|
||||
copy.imageExtent.height = MipExtent(image->height, level);
|
||||
copy.imageExtent.depth = 1;
|
||||
copies.push_back(copy);
|
||||
offset += static_cast<vk::DeviceSize>(
|
||||
ImageMipDwordCount(image->width, image->height,
|
||||
image->dwords_per_pixel, level) *
|
||||
image->dwords_per_pixel, level, image->layers) *
|
||||
sizeof(u32));
|
||||
}
|
||||
cmd.copyBufferToImage(staging, image->image,
|
||||
@@ -8208,7 +8395,8 @@ private:
|
||||
final_layout, vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::PipelineStageFlagBits::eComputeShader,
|
||||
vk::AccessFlagBits::eTransferWrite,
|
||||
AccessForLayout(final_layout), image->mip_levels);
|
||||
AccessForLayout(final_layout), image->mip_levels,
|
||||
image->layers);
|
||||
EndSubmitAndFree(shader_name, "dispatch", cmd);
|
||||
image->layout = final_layout;
|
||||
}
|
||||
@@ -8356,12 +8544,16 @@ void RunCase(VulkanHarness *vulkan, const TestCase &test) {
|
||||
VulkanHarness::Buffer gds_buffer;
|
||||
vk::Sampler sampler = nullptr;
|
||||
const bool needs_sampled_image =
|
||||
Has(Kind::Sampled1D) || Has(Kind::Sampled1DArray) ||
|
||||
Has(Kind::Sampled2D) || Has(Kind::Sampled2DArray) ||
|
||||
Has(Kind::Sampled3D) || Has(Kind::SampledUint2D) ||
|
||||
Has(Kind::SampledUint1D) || Has(Kind::SampledUint1DArray) ||
|
||||
Has(Kind::SampledUint2DArray) || Has(Kind::SampledUint3D);
|
||||
const bool needs_storage_image =
|
||||
Has(Kind::Storage1D) || Has(Kind::Storage1DArray) ||
|
||||
Has(Kind::Storage2D) || Has(Kind::Storage2DArray) ||
|
||||
Has(Kind::Storage3D) || Has(Kind::StorageUint2D) ||
|
||||
Has(Kind::StorageUint1D) || Has(Kind::StorageUint1DArray) ||
|
||||
Has(Kind::StorageUint2DArray) || Has(Kind::StorageUint3D);
|
||||
const bool needs_sampler = Has(Kind::Samplers);
|
||||
const bool needs_gds = Has(Kind::Gds);
|
||||
@@ -8372,19 +8564,22 @@ void RunCase(VulkanHarness *vulkan, const TestCase &test) {
|
||||
vulkan->CreateStorageBuffer(test.name, test.gds_initial, gds_dwords);
|
||||
}
|
||||
if (needs_sampled_image) {
|
||||
auto sampled_mips = test.sampled_image_rgba_mips;
|
||||
auto sampled_format = test.sampled_image_format;
|
||||
auto sampled_dwords_per_pixel = test.sampled_image_dwords_per_pixel;
|
||||
if (!test.sampled_image_rgba_mips.empty()) {
|
||||
sampled_image = vulkan->CreateImage2DMips(
|
||||
test.name, test.image_width, test.image_height,
|
||||
vk::Format::eR32G32B32A32Sfloat, vk::ImageUsageFlagBits::eSampled,
|
||||
test.sampled_image_rgba_mips, 4,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
} else {
|
||||
sampled_image = vulkan->CreateImage2D(
|
||||
test.name, test.image_width, test.image_height,
|
||||
test.sampled_image_format, vk::ImageUsageFlagBits::eSampled,
|
||||
test.sampled_image_rgba, test.sampled_image_dwords_per_pixel,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
sampled_format = vk::Format::eR32G32B32A32Sfloat;
|
||||
sampled_dwords_per_pixel = 4;
|
||||
} else if (!test.sampled_image_rgba.empty()) {
|
||||
sampled_mips.push_back(test.sampled_image_rgba);
|
||||
}
|
||||
sampled_image = vulkan->CreateImageMips(
|
||||
test.name, test.image_width, test.image_height, sampled_format,
|
||||
vk::ImageUsageFlagBits::eSampled, sampled_mips,
|
||||
sampled_dwords_per_pixel, vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
test.sampled_image_type, test.sampled_image_view_type,
|
||||
test.sampled_image_layers, test.sampled_image_view_base_layer,
|
||||
test.sampled_image_view_layers);
|
||||
}
|
||||
if (needs_storage_image) {
|
||||
storage_image = vulkan->CreateImage2D(
|
||||
@@ -13465,6 +13660,105 @@ TestCase ImageLoadR32UintUsesIntegerSampledImage() {
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase ImageLoad1DUsesScalarCoordinate() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 20, 2);
|
||||
code.push_back(EncodeMimg0(0x00, 0x1, 0, false, 0));
|
||||
code.push_back(EncodeMimg1(0, 20));
|
||||
AppendStoreVgpr(&code, 0, 0);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "ImageLoad1DUsesScalarCoordinate";
|
||||
test.code = code;
|
||||
test.expected = {0xdeadbeefu};
|
||||
test.opcodes = {O::VMovB32, O::ImageLoad, O::BufferStoreDword, O::SEndpgm};
|
||||
test.image_width = 4;
|
||||
test.image_height = 1;
|
||||
test.sampled_image_rgba = {0, 0, 0xdeadbeefu, 0};
|
||||
test.sampled_image_format = vk::Format::eR32Uint;
|
||||
test.sampled_image_dwords_per_pixel = 1;
|
||||
test.sampled_image_type = vk::ImageType::e1D;
|
||||
test.sampled_image_view_type = vk::ImageViewType::e1D;
|
||||
test.user_data = MakeSampledTextureData(Prospero::BufferFormat::k32UInt);
|
||||
test.user_data[3] =
|
||||
Prospero::GpuEnumValue(Prospero::ImageType::kColor1D) << 28u;
|
||||
test.has_user_data = true;
|
||||
test.required_spirv = {"sampled_uint_1d"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase ImageLoad1DArrayUsesLayerCoordinate() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 20, 2);
|
||||
AppendVMovU32(&code, 21, 1);
|
||||
code.push_back(EncodeMimg0(0x00, 0x1, 0, false, 4));
|
||||
code.push_back(EncodeMimg1(0, 20));
|
||||
AppendStoreVgpr(&code, 0, 0);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "ImageLoad1DArrayUsesLayerCoordinate";
|
||||
test.code = code;
|
||||
test.expected = {0xcafebabeu};
|
||||
test.opcodes = {O::VMovB32, O::ImageLoad, O::BufferStoreDword, O::SEndpgm};
|
||||
test.image_width = 4;
|
||||
test.image_height = 1;
|
||||
test.sampled_image_rgba = {0, 0, 0, 0, 0, 0, 0xcafebabeu, 0};
|
||||
test.sampled_image_format = vk::Format::eR32Uint;
|
||||
test.sampled_image_dwords_per_pixel = 1;
|
||||
test.sampled_image_type = vk::ImageType::e1D;
|
||||
test.sampled_image_view_type = vk::ImageViewType::e1DArray;
|
||||
test.sampled_image_layers = 2;
|
||||
test.user_data = MakeSampledTextureData(Prospero::BufferFormat::k32UInt);
|
||||
test.user_data[3] =
|
||||
Prospero::GpuEnumValue(Prospero::ImageType::kColor1DArray) << 28u;
|
||||
test.has_user_data = true;
|
||||
test.required_spirv = {"sampled_uint_1d_array"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase ImageLoad1DArrayDescriptorUsesSelectedLayer() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 20, 2);
|
||||
code.push_back(EncodeMimg0(0x00, 0x1, 0, false, 0));
|
||||
code.push_back(EncodeMimg1(0, 20));
|
||||
AppendStoreVgpr(&code, 0, 0);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "ImageLoad1DArrayDescriptorUsesSelectedLayer";
|
||||
test.code = code;
|
||||
test.expected = {0xcafebabeu};
|
||||
test.opcodes = {O::VMovB32, O::ImageLoad, O::BufferStoreDword, O::SEndpgm};
|
||||
test.image_width = 4;
|
||||
test.image_height = 1;
|
||||
test.sampled_image_rgba = {
|
||||
0, 0, 0xdeadbeefu, 0,
|
||||
0, 0, 0xcafebabeu, 0,
|
||||
};
|
||||
test.sampled_image_format = vk::Format::eR32Uint;
|
||||
test.sampled_image_dwords_per_pixel = 1;
|
||||
test.sampled_image_type = vk::ImageType::e1D;
|
||||
test.sampled_image_view_type = vk::ImageViewType::e1D;
|
||||
test.sampled_image_layers = 2;
|
||||
test.sampled_image_view_base_layer = 1;
|
||||
test.sampled_image_view_layers = 1;
|
||||
test.user_data = MakeSampledTextureData(Prospero::BufferFormat::k32UInt);
|
||||
test.user_data[3] =
|
||||
Prospero::GpuEnumValue(Prospero::ImageType::kColor1DArray) << 28u;
|
||||
test.user_data[4] = 1u | (1u << 16u);
|
||||
test.has_user_data = true;
|
||||
test.required_spirv = {"sampled_uint_1d"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase ImageLoadMipUsesVaddr2Lod2D() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -13707,7 +14001,7 @@ TestCase ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 20, 0); // Texel offset stays one packed 32-bit VGPR.
|
||||
AppendVMovU32(&code, 20, 1); // Non-constant +1 X offset is not a SPIR-V ConstOffset.
|
||||
AppendVMovLiteral(&code, 21, 0x36003900u); // x=0.625, y=0.375 packed as f16.
|
||||
AppendVMovU32(&code, 22, 0);
|
||||
code.push_back(EncodeMimg0(0x30, 0xf));
|
||||
@@ -13727,7 +14021,8 @@ TestCase ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu() {
|
||||
test.expected = {0x3f800000u, 0x40000000u, 0x40400000u, 0x40800000u};
|
||||
test.opcodes = {O::VMovB32, O::ImageSample, O::BufferStoreDword, O::SEndpgm};
|
||||
test.sampled_image_rgba = image;
|
||||
test.required_spirv = {"UnpackHalf2x16", "OpBitFieldSExtract"};
|
||||
test.required_spirv = {"UnpackHalf2x16"};
|
||||
test.forbidden_spirv = {"OpBitFieldSExtract"};
|
||||
return test;
|
||||
}
|
||||
|
||||
@@ -13989,7 +14284,7 @@ TestCase ImageStoreR32UintUsesUintStorageImage() {
|
||||
test.storage_image_rgba = MakeRgbaImage(4, 4);
|
||||
test.storage_image_r32ui = std::vector<u32>(16, 0);
|
||||
test.expected_storage_image_r32ui = expected_image;
|
||||
test.required_spirv = {"R32ui", "textures2D_L_U"};
|
||||
test.required_spirv = {"R32ui", "storage_uint_2d"};
|
||||
return test;
|
||||
}
|
||||
|
||||
@@ -14524,6 +14819,9 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(BufferAtomicGlc0DoesNotReturnOldValue);
|
||||
AddCase(ImageLoadVariants);
|
||||
AddCase(ImageLoadR32UintUsesIntegerSampledImage);
|
||||
AddCase(ImageLoad1DUsesScalarCoordinate);
|
||||
AddCase(ImageLoad1DArrayUsesLayerCoordinate);
|
||||
AddCase(ImageLoad1DArrayDescriptorUsesSelectedLayer);
|
||||
AddCase(ImageLoadMipUsesVaddr2Lod2D);
|
||||
AddCase(ImageLoadMipNsaUsesSelectedAddressVgprs);
|
||||
AddCase(ImageLoadA16UintCoordsOnGpu);
|
||||
@@ -14850,6 +15148,44 @@ void CheckReverseRenderTargetFormatContract() {
|
||||
} else if (std::strcmp(kind, "sampled-depth-swizzle") == 0) {
|
||||
(void)SelectSampledDepthView(vk::Format::eD32SfloatS8Uint,
|
||||
vk::Format::eR32Sfloat, DstSel(4, 5, 6, 7));
|
||||
} else if (std::strcmp(kind, "storage-incompatible-format") == 0) {
|
||||
ValidateStorageColorView(vk::Format::eR8G8B8A8Srgb,
|
||||
vk::Format::eR16G16B16A16Unorm,
|
||||
DstSel(4, 5, 6, 7));
|
||||
} else if (std::strcmp(kind, "volume-mip-count") == 0 ||
|
||||
std::strcmp(kind, "volume-slice-range") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
auto &graphics = vulkan.RuntimeContext();
|
||||
CommandScheduler scheduler(vulkan.RuntimeRenderer(), graphics);
|
||||
ImageInfo volume_info{};
|
||||
volume_info.pixel_format = vk::Format::eR8G8B8A8Unorm;
|
||||
volume_info.guest_format =
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8UNorm);
|
||||
volume_info.type = Prospero::ImageType::kColor3D;
|
||||
volume_info.extent = {8, 8, 4};
|
||||
volume_info.resources = {2, 1};
|
||||
volume_info.pitch = 8;
|
||||
volume_info.bytes_per_block = 4;
|
||||
volume_info.samples = 1;
|
||||
volume_info.tile_mode =
|
||||
Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
|
||||
Libs::Graphics::Image volume(graphics, scheduler, volume_info);
|
||||
ImageViewInfo view{};
|
||||
view.format = volume_info.pixel_format;
|
||||
view.type = vk::ImageViewType::e2DArray;
|
||||
view.aspect = vk::ImageAspectFlagBits::eColor;
|
||||
view.base_level = 1;
|
||||
view.level_count = 1;
|
||||
view.layer_count = 1;
|
||||
view.usage = vk::ImageUsageFlagBits::eSampled;
|
||||
if (std::strcmp(kind, "volume-mip-count") == 0) {
|
||||
view.base_level = 0;
|
||||
view.level_count = 2;
|
||||
} else {
|
||||
view.base_layer = 1;
|
||||
view.layer_count = 2;
|
||||
}
|
||||
(void)volume.FindView(view);
|
||||
} else {
|
||||
ShaderRecompiler::IR::ImageResource resource{};
|
||||
resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImage;
|
||||
@@ -15083,8 +15419,9 @@ void CheckSampledColorViews() {
|
||||
for (const char *kind :
|
||||
{"sampled-invalid-selector", "sampled-incompatible-format",
|
||||
"sampled-invalid-high", "sampled-depth-format", "sampled-depth-swizzle",
|
||||
"storage-kind", "storage-no-write", "storage-atomic", "storage-compare",
|
||||
"storage-mip", "storage-dimension"}) {
|
||||
"storage-incompatible-format", "storage-kind", "storage-no-write",
|
||||
"storage-atomic", "storage-compare", "storage-mip", "storage-dimension",
|
||||
"volume-mip-count", "volume-slice-range"}) {
|
||||
std::string command =
|
||||
std::string("\"") + path + "\" --image-view-death " + kind;
|
||||
std::vector<char> mutable_command(command.begin(), command.end());
|
||||
@@ -16022,6 +16359,10 @@ void CheckBasicStorageTextureDescriptor() {
|
||||
0x800);
|
||||
ValidateStorageColorView(vk::Format::eR16G16B16A16Sfloat,
|
||||
vk::Format::eR16G16B16A16Sfloat, swizzle);
|
||||
ValidateStorageColorView(vk::Format::eR8G8B8A8Srgb,
|
||||
vk::Format::eR8G8B8A8Unorm, swizzle);
|
||||
ValidateStorageColorView(vk::Format::eB8G8R8A8Srgb,
|
||||
vk::Format::eR8G8B8A8Unorm, swizzle);
|
||||
valid_storage_swizzles++;
|
||||
}
|
||||
Require("BasicStorageTexture", "all write swizzles",
|
||||
|
||||
+153
-6
@@ -640,6 +640,13 @@ void SetImageTestType(std::array<uint32_t, 64>* data, uint32_t srsrc, Prospero::
|
||||
(*data)[type_dword] = Prospero::GpuEnumValue(type) << 28u;
|
||||
}
|
||||
|
||||
void SetImageTestFormat(std::array<uint32_t, 64>* data, uint32_t srsrc,
|
||||
Prospero::BufferFormat format) {
|
||||
const auto format_dword = srsrc * 4u + 1u;
|
||||
Check(data != nullptr && format_dword < data->size(), "invalid image test descriptor source");
|
||||
(*data)[format_dword] = Prospero::GpuEnumValue(format) << 20u;
|
||||
}
|
||||
|
||||
bool ReadZeroTestMemory(void*, uint64_t, uint32_t* value) {
|
||||
if (value == nullptr) {
|
||||
return false;
|
||||
@@ -3102,8 +3109,6 @@ void TestNewShaderRecompilerImageSampleVariants() {
|
||||
"SPIR-V binary does not contain shared OpImageSampleExplicitLod emission");
|
||||
Check(SpirvContainsOpcode(result.spirv, 90),
|
||||
"SPIR-V binary does not contain shared OpImageSampleDrefExplicitLod emission");
|
||||
Check(SpirvContainsOpcode(result.spirv, 202),
|
||||
"SPIR-V binary does not contain packed offset extraction");
|
||||
Check(SpirvContainsOpcode(result.spirv, 80),
|
||||
"SPIR-V binary does not contain coordinate/gradient composite construction");
|
||||
Check(SpirvContainsExtInst(result.spirv, 62),
|
||||
@@ -3400,8 +3405,11 @@ void TestNewShaderRecompilerPixelImageSampleLodSelection() {
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerImageViewDimensions() {
|
||||
constexpr uint32_t MimgDim1D = 0;
|
||||
constexpr uint32_t MimgDim3D = 2;
|
||||
constexpr uint32_t MimgDim1DArray = 4;
|
||||
constexpr uint32_t MimgDim2DArray = 5;
|
||||
constexpr uint32_t SpirvDim1D = 0;
|
||||
constexpr uint32_t SpirvDim2D = 1;
|
||||
constexpr uint32_t SpirvDim3D = 2;
|
||||
|
||||
@@ -3414,6 +3422,18 @@ void TestNewShaderRecompilerImageViewDimensions() {
|
||||
EncodeMimg1(8, 2, 1, 8), // image_sample 3D
|
||||
EncodeMimg0(0x01, 0xf, false, MimgDim2DArray),
|
||||
EncodeMimg1(12, 3, 0, 12), // image_load_mip 2D array
|
||||
EncodeMimg0(0x20, 0xf, false, MimgDim1D),
|
||||
EncodeMimg1(16, 4, 1, 16), // image_sample 1D
|
||||
EncodeMimg0(0x20, 0xf, false, MimgDim1DArray),
|
||||
EncodeMimg1(20, 5, 1, 20), // image_sample 1D array
|
||||
EncodeMimg0(0x22, 0xf, false, MimgDim1D),
|
||||
EncodeMimg1(24, 6, 1, 24), // image_sample_d 1D
|
||||
EncodeMimg0(0x01, 0xf, false, MimgDim1D),
|
||||
EncodeMimg1(28, 7, 0, 28), // image_load_mip 1D
|
||||
EncodeMimg0(0x0e, 0xf, false, MimgDim1D),
|
||||
EncodeMimg1(32, 8, 0, 32), // image_get_resinfo 1D
|
||||
EncodeMimg0(0x00, 0xf, false, MimgDim1D),
|
||||
EncodeMimg1(36, 9, 0, 36), // integer image_load 1D
|
||||
0xbf810000u,
|
||||
};
|
||||
|
||||
@@ -3423,6 +3443,13 @@ void TestNewShaderRecompilerImageViewDimensions() {
|
||||
SetImageTestType(&user_data, 1, Prospero::ImageType::kColor2DArray);
|
||||
SetImageTestType(&user_data, 2, Prospero::ImageType::kColor3D);
|
||||
SetImageTestType(&user_data, 3, Prospero::ImageType::kColor2DArray);
|
||||
SetImageTestType(&user_data, 4, Prospero::ImageType::kColor1D);
|
||||
SetImageTestType(&user_data, 5, Prospero::ImageType::kColor1DArray);
|
||||
SetImageTestType(&user_data, 6, Prospero::ImageType::kColor1D);
|
||||
SetImageTestType(&user_data, 7, Prospero::ImageType::kColor1D);
|
||||
SetImageTestType(&user_data, 8, Prospero::ImageType::kColor1D);
|
||||
SetImageTestType(&user_data, 9, Prospero::ImageType::kColor1D);
|
||||
SetImageTestFormat(&user_data, 9, Prospero::BufferFormat::k32UInt);
|
||||
|
||||
ShaderRecompiler::CompileOptions options;
|
||||
options.stage = ShaderType::Pixel;
|
||||
@@ -3438,6 +3465,15 @@ void TestNewShaderRecompilerImageViewDimensions() {
|
||||
"MIMG DIM did not decode 2D-array image view");
|
||||
Check(Common::ContainsStr(result.decoded_dump, "image_dim=3d"),
|
||||
"MIMG DIM did not decode 3D image view");
|
||||
Check(Common::ContainsStr(result.decoded_dump,
|
||||
"image_dim=1d sample_flags=none addr_components=1"),
|
||||
"1D sample did not preserve its scalar coordinate");
|
||||
Check(Common::ContainsStr(result.decoded_dump,
|
||||
"image_dim=1d_array sample_flags=none addr_components=2"),
|
||||
"1D-array sample did not preserve coordinate plus layer");
|
||||
Check(Common::ContainsStr(result.decoded_dump,
|
||||
"image_dim=1d sample_flags=derivative addr_components=3"),
|
||||
"1D derivative sample did not preserve scalar gradients");
|
||||
Check(Common::ContainsStr(result.decoded_dump, "image_sample_l"),
|
||||
"2D-array LOD sample did not decode");
|
||||
Check(Common::ContainsStr(result.decoded_dump,
|
||||
@@ -3447,13 +3483,121 @@ void TestNewShaderRecompilerImageViewDimensions() {
|
||||
"2D-array image view did not survive into IR metadata");
|
||||
Check(Common::ContainsStr(result.ir_dump, "image_dim=3d"),
|
||||
"3D image view did not survive into IR metadata");
|
||||
Check(Common::ContainsStr(result.ir_dump, "image_dim=1d"),
|
||||
"1D image view did not survive into IR metadata");
|
||||
Check(Common::ContainsStr(result.ir_dump, "image_dim=1d_array"),
|
||||
"1D-array image view did not survive into IR metadata");
|
||||
Check(SpirvContainsTypeImage(result.spirv, SpirvDim1D, 0, 1),
|
||||
"SPIR-V binary does not contain sampled 1D image type");
|
||||
Check(SpirvContainsTypeImage(result.spirv, SpirvDim1D, 1, 1),
|
||||
"SPIR-V binary does not contain sampled 1D-array image type");
|
||||
Check(SpirvContainsTypeImage(result.spirv, SpirvDim2D, 1, 1),
|
||||
"SPIR-V binary does not contain sampled 2D-array image type");
|
||||
Check(SpirvContainsTypeImage(result.spirv, SpirvDim3D, 0, 1),
|
||||
"SPIR-V binary does not contain sampled 3D image type");
|
||||
Check(SpirvContainsCapability(result.spirv, 43),
|
||||
"SPIR-V binary does not request Sampled1D");
|
||||
Check(SpirvContainsCapability(result.spirv, 44),
|
||||
"SPIR-V binary does not request Image1D");
|
||||
Check(SpirvContainsOpcode(result.spirv, 95),
|
||||
"SPIR-V binary does not contain array image fetch");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||
Check(Common::ContainsStr(source, " Grad "),
|
||||
"1D derivative sample did not emit scalar SPIR-V gradients");
|
||||
Check(Common::ContainsStr(source, "OpImageQuerySizeLod"),
|
||||
"1D resource query did not emit a scalar size query");
|
||||
Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "sampled_uint_1d"),
|
||||
"integer 1D load did not access the uint 1D descriptor binding");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerStorageImage1DDescriptorVariants() {
|
||||
constexpr uint32_t MimgDim1D = 0;
|
||||
constexpr uint32_t MimgDim1DArray = 4;
|
||||
constexpr uint32_t SpirvDim1D = 0;
|
||||
|
||||
const uint32_t shader[] = {
|
||||
EncodeMimg0(0x08, 0xf, false, MimgDim1D),
|
||||
EncodeMimg1(20, 4, 0, 4), // image_store 1D
|
||||
EncodeMimg0(0x08, 0xf, false, MimgDim1DArray),
|
||||
EncodeMimg1(24, 5, 0, 8), // image_store 1D array
|
||||
0xbf810000u,
|
||||
};
|
||||
|
||||
auto user_data = ImageTestUserData();
|
||||
SetImageTestType(&user_data, 4, Prospero::ImageType::kColor1D);
|
||||
SetImageTestType(&user_data, 5, Prospero::ImageType::kColor1DArray);
|
||||
|
||||
ShaderRecompiler::CompileOptions options;
|
||||
options.stage = ShaderType::Compute;
|
||||
options.dump_ir = true;
|
||||
options.user_data = user_data.data();
|
||||
|
||||
ShaderRecompiler::CompileResult result;
|
||||
std::string error;
|
||||
Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str());
|
||||
Check(Common::ContainsStr(result.ir_dump, "image_dim=1d"),
|
||||
"1D storage dimension did not survive into IR metadata");
|
||||
Check(Common::ContainsStr(result.ir_dump, "image_dim=1d_array"),
|
||||
"1D-array storage dimension did not survive into IR metadata");
|
||||
Check(SpirvContainsTypeImage(result.spirv, SpirvDim1D, 0, 2),
|
||||
"SPIR-V binary does not contain storage 1D image type");
|
||||
Check(SpirvContainsTypeImage(result.spirv, SpirvDim1D, 1, 2),
|
||||
"SPIR-V binary does not contain storage 1D-array image type");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
|
||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||
Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "storage_1d "),
|
||||
"1D store did not access the 1D storage descriptor binding");
|
||||
Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "storage_1d_array"),
|
||||
"1D-array store did not access the 1D-array storage descriptor binding");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerNullImageUsesCanonical2DView() {
|
||||
const uint32_t shader[] = {
|
||||
EncodeMimg0(0x20, 0xf, false, 0),
|
||||
EncodeMimg1(0, 0, 1, 0), // 1D instruction with a null image descriptor
|
||||
0xbf810000u,
|
||||
};
|
||||
std::array<uint32_t, 64> user_data {};
|
||||
|
||||
ShaderRecompiler::CompileOptions options;
|
||||
options.stage = ShaderType::Compute;
|
||||
options.user_data = user_data.data();
|
||||
|
||||
ShaderRecompiler::CompileResult result;
|
||||
std::string error;
|
||||
Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str());
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||
Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "sampled_2d "),
|
||||
"null image did not use the canonical 2D sampled descriptor binding");
|
||||
Check(!SpirvSourceHasInstructionUsing(source, "OpAccessChain", "sampled_1d "),
|
||||
"null image retained the decoded 1D descriptor binding");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerRejectsOneDimensionalGather() {
|
||||
constexpr std::array cases {
|
||||
std::pair {0u, Prospero::ImageType::kColor1D},
|
||||
std::pair {4u, Prospero::ImageType::kColor1DArray},
|
||||
};
|
||||
for (const auto& [dimension, type]: cases) {
|
||||
const uint32_t shader[] = {
|
||||
EncodeMimg0(0x47, 0x1, false, dimension),
|
||||
EncodeMimg1(0, 0, 1, 0),
|
||||
0xbf810000u,
|
||||
};
|
||||
auto user_data = ImageTestUserData(type);
|
||||
ShaderRecompiler::CompileOptions options;
|
||||
options.stage = ShaderType::Compute;
|
||||
options.user_data = user_data.data();
|
||||
|
||||
ShaderRecompiler::CompileResult result;
|
||||
std::string error;
|
||||
Check(!ShaderRecompiler::TryRecompile(shader, options, result, &error) &&
|
||||
Common::ContainsStr(error, "1D image gather is not supported by SPIR-V"),
|
||||
"1D image gather escaped as invalid SPIR-V");
|
||||
}
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerImageGatherVariants() {
|
||||
@@ -3680,7 +3824,7 @@ void TestNewShaderRecompilerStorageImage3DDescriptorVariant() {
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
|
||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||
Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "textures2D_L_3D"),
|
||||
Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "storage_3d"),
|
||||
"storage image store did not access the 3D storage descriptor binding");
|
||||
}
|
||||
|
||||
@@ -3719,11 +3863,11 @@ void TestNewShaderRecompilerStorageImage2DDescriptorOverridesMimg3D() {
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
|
||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||
Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "textures2D_L"),
|
||||
Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "storage_2d"),
|
||||
"2D descriptor storage image store did not access the base storage binding");
|
||||
Check(!SpirvSourceHasInstructionUsing(source, "OpAccessChain", "textures2D_L_A"),
|
||||
Check(!SpirvSourceHasInstructionUsing(source, "OpAccessChain", "storage_2d_array"),
|
||||
"2D descriptor storage image store unexpectedly used the array storage binding");
|
||||
Check(!SpirvSourceHasInstructionUsing(source, "OpAccessChain", "textures2D_L_3D"),
|
||||
Check(!SpirvSourceHasInstructionUsing(source, "OpAccessChain", "storage_3d"),
|
||||
"2D descriptor storage image store unexpectedly used the 3D storage binding");
|
||||
}
|
||||
|
||||
@@ -6811,6 +6955,9 @@ int main() {
|
||||
TestNewShaderRecompilerImageSampleA16Rdna2AddressOrder();
|
||||
TestNewShaderRecompilerPixelImageSampleLodSelection();
|
||||
TestNewShaderRecompilerImageViewDimensions();
|
||||
TestNewShaderRecompilerStorageImage1DDescriptorVariants();
|
||||
TestNewShaderRecompilerNullImageUsesCanonical2DView();
|
||||
TestNewShaderRecompilerRejectsOneDimensionalGather();
|
||||
TestNewShaderRecompilerImageGatherVariants();
|
||||
TestNewShaderRecompilerImageLoadA16UintCoords();
|
||||
TestNewShaderRecompilerImageLoadVariants();
|
||||
|
||||
Reference in new issue
Block a user