shader: support signed sampled images
This commit is contained in:
parent
fc91fd2b1a
commit
894bc7de3c
|
|
@ -15,6 +15,7 @@ struct FormatInfo {
|
|||
uint32_t render_target_bytes_per_element;
|
||||
bool sampled_texture;
|
||||
bool uint_texture;
|
||||
bool sint_texture = false;
|
||||
};
|
||||
|
||||
constexpr FormatInfo kFormatInfo[] = {
|
||||
|
|
@ -24,19 +25,19 @@ constexpr FormatInfo kFormatInfo[] = {
|
|||
{BufferFormat::k16UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k16SNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k16UInt, 2, 0, 2, true, true},
|
||||
{BufferFormat::k16SInt, 2, 0, 2, false, false},
|
||||
{BufferFormat::k16SInt, 2, 0, 2, true, false, true},
|
||||
{BufferFormat::k16Float, 2, 0, 2, true, false},
|
||||
{BufferFormat::k8_8UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k8_8SNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k8_8UInt, 2, 0, 2, true, true},
|
||||
{BufferFormat::k8_8SInt, 2, 0, 2, false, false},
|
||||
{BufferFormat::k8_8SInt, 2, 0, 2, true, false, true},
|
||||
{BufferFormat::k32UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k32SInt, 4, 0, 4, false, false},
|
||||
{BufferFormat::k32SInt, 4, 0, 4, true, false, true},
|
||||
{BufferFormat::k32Float, 4, 0, 4, true, false},
|
||||
{BufferFormat::k16_16UNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k16_16SNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k16_16UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k16_16SInt, 4, 0, 4, false, false},
|
||||
{BufferFormat::k16_16SInt, 4, 0, 4, true, false, true},
|
||||
{BufferFormat::k16_16Float, 4, 0, 4, true, false},
|
||||
{BufferFormat::k11_11_10UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k11_11_10Float, 4, 0, 4, true, false},
|
||||
|
|
@ -45,20 +46,20 @@ constexpr FormatInfo kFormatInfo[] = {
|
|||
{BufferFormat::k8_8_8_8UNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k8_8_8_8SNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k8_8_8_8UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k8_8_8_8SInt, 4, 0, 4, false, false},
|
||||
{BufferFormat::k8_8_8_8SInt, 4, 0, 4, true, false, true},
|
||||
{BufferFormat::k32_32UInt, 8, 0, 8, true, true},
|
||||
{BufferFormat::k32_32SInt, 8, 0, 8, false, false},
|
||||
{BufferFormat::k32_32SInt, 8, 0, 8, true, false, true},
|
||||
{BufferFormat::k32_32Float, 8, 0, 8, true, false},
|
||||
{BufferFormat::k16_16_16_16UNorm, 8, 0, 8, true, false},
|
||||
{BufferFormat::k16_16_16_16SNorm, 8, 0, 8, true, false},
|
||||
{BufferFormat::k16_16_16_16UInt, 8, 0, 8, true, true},
|
||||
{BufferFormat::k16_16_16_16SInt, 8, 0, 8, false, false},
|
||||
{BufferFormat::k16_16_16_16SInt, 8, 0, 8, true, false, true},
|
||||
{BufferFormat::k16_16_16_16Float, 8, 0, 8, true, false},
|
||||
{BufferFormat::k32_32_32UInt, 12, 0, 12, true, true},
|
||||
{BufferFormat::k32_32_32SInt, 12, 0, 12, false, false},
|
||||
{BufferFormat::k32_32_32SInt, 12, 0, 12, true, false, true},
|
||||
{BufferFormat::k32_32_32Float, 12, 0, 12, true, false},
|
||||
{BufferFormat::k32_32_32_32UInt, 16, 0, 16, true, true},
|
||||
{BufferFormat::k32_32_32_32SInt, 16, 0, 16, false, false},
|
||||
{BufferFormat::k32_32_32_32SInt, 16, 0, 16, true, false, true},
|
||||
{BufferFormat::k32_32_32_32Float, 16, 0, 16, true, false},
|
||||
{BufferFormat::k8Srgb, 1, 0, 0, true, false},
|
||||
{BufferFormat::k8_8Srgb, 2, 0, 0, true, false},
|
||||
|
|
@ -218,14 +219,15 @@ bool IsFmaskTextureFormat(BufferFormat format) {
|
|||
return format >= BufferFormat::kFmask8_S2_F1 && format <= BufferFormat::kFmask64_S16_F8;
|
||||
}
|
||||
|
||||
bool IsSampledTextureFormat(BufferFormat format) {
|
||||
TextureNumericClass SampledTextureNumericClass(BufferFormat format) {
|
||||
const auto* info = FindFormatInfo(format);
|
||||
return info != nullptr && info->sampled_texture;
|
||||
}
|
||||
|
||||
bool IsUintTextureFormat(BufferFormat format) {
|
||||
const auto* info = FindFormatInfo(format);
|
||||
return info != nullptr && info->uint_texture;
|
||||
if (info == nullptr || !info->sampled_texture) {
|
||||
return TextureNumericClass::Unsupported;
|
||||
}
|
||||
if (info->sint_texture) {
|
||||
return TextureNumericClass::Sint;
|
||||
}
|
||||
return info->uint_texture ? TextureNumericClass::Uint : TextureNumericClass::Float;
|
||||
}
|
||||
|
||||
BufferFormat RemapTextureFormat(BufferFormat format) {
|
||||
|
|
|
|||
|
|
@ -37,13 +37,14 @@ struct RenderTargetFormatEncoding {
|
|||
}
|
||||
};
|
||||
|
||||
enum class TextureNumericClass { Unsupported, Float, Uint, Sint };
|
||||
|
||||
RenderTargetFormatEncoding ResolveRenderTargetFormat(ChannelLayout layout, ChannelType type);
|
||||
uint32_t NumBytesPerElement(BufferFormat format);
|
||||
uint32_t BlockCompressedBytesPerBlock(BufferFormat format);
|
||||
uint32_t RenderTargetBytesPerElement(BufferFormat format);
|
||||
bool IsFmaskTextureFormat(BufferFormat format);
|
||||
bool IsSampledTextureFormat(BufferFormat format);
|
||||
bool IsUintTextureFormat(BufferFormat format);
|
||||
TextureNumericClass SampledTextureNumericClass(BufferFormat format);
|
||||
BufferFormat RemapTextureFormat(BufferFormat format);
|
||||
|
||||
} // namespace Libs::Graphics::Prospero
|
||||
|
|
|
|||
|
|
@ -103,11 +103,11 @@ SelectSampledDepthView(vk::Format image_format, vk::Format view_format, uint32_t
|
|||
|
||||
[[nodiscard]] inline bool
|
||||
IsSupportedSampledDepthResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
|
||||
if (resource.kind != ShaderRecompiler::IR::ResourceKind::Image &&
|
||||
resource.kind != ShaderRecompiler::IR::ResourceKind::ImageUint) {
|
||||
if (!ShaderRecompiler::IR::ImageResourceKindMatches(
|
||||
resource.kind, ShaderRecompiler::IR::ImageResourceClass::Sampled)) {
|
||||
return false;
|
||||
}
|
||||
if (resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint && resource.depth_compare) {
|
||||
if (resource.kind != ShaderRecompiler::IR::ResourceKind::Image && resource.depth_compare) {
|
||||
return false;
|
||||
}
|
||||
return (resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D ||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,14 @@ bool IsSampledImage(BindingKind kind) {
|
|||
case BindingKind::SampledUint2DArray:
|
||||
case BindingKind::SampledUint2DMsaa:
|
||||
case BindingKind::SampledUint2DMsaaArray:
|
||||
case BindingKind::SampledUint3D: return true;
|
||||
case BindingKind::SampledUint3D:
|
||||
case BindingKind::SampledSint1D:
|
||||
case BindingKind::SampledSint1DArray:
|
||||
case BindingKind::SampledSint2D:
|
||||
case BindingKind::SampledSint2DArray:
|
||||
case BindingKind::SampledSint2DMsaa:
|
||||
case BindingKind::SampledSint2DMsaaArray:
|
||||
case BindingKind::SampledSint3D: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -229,8 +236,8 @@ static bool IsSupportedSampledColorResource(const ShaderRecompiler::IR::ImageRes
|
|||
break;
|
||||
default: break;
|
||||
}
|
||||
const bool sampled_kind = resource.kind == ShaderRecompiler::IR::ResourceKind::Image ||
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint;
|
||||
const bool sampled_kind = ShaderRecompiler::IR::ImageResourceKindMatches(
|
||||
resource.kind, ShaderRecompiler::IR::ImageResourceClass::Sampled);
|
||||
return sampled_kind && supported_dimension &&
|
||||
resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.read &&
|
||||
!resource.written && !resource.atomic && !resource.depth_compare;
|
||||
|
|
@ -512,10 +519,13 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
|||
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint;
|
||||
const bool raw_sint_storage = format == Prospero::BufferFormat::k32SInt && uint_resource &&
|
||||
resource.written && !resource.read && !resource.atomic;
|
||||
const auto numeric_class = Prospero::SampledTextureNumericClass(format);
|
||||
const bool format_ok =
|
||||
raw_sint_storage || (Prospero::IsSampledTextureFormat(format) &&
|
||||
uint_resource == Prospero::IsUintTextureFormat(format) &&
|
||||
(!resource.atomic || format == Prospero::BufferFormat::k32UInt));
|
||||
raw_sint_storage ||
|
||||
(numeric_class != Prospero::TextureNumericClass::Unsupported &&
|
||||
numeric_class != Prospero::TextureNumericClass::Sint &&
|
||||
uint_resource == (numeric_class == Prospero::TextureNumericClass::Uint) &&
|
||||
(!resource.atomic || format == Prospero::BufferFormat::k32UInt));
|
||||
if (resource_ok && descriptor_ok && encoding_ok && format_ok && size != 0) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -547,6 +557,9 @@ struct NullImageSpec {
|
|||
};
|
||||
|
||||
static NullImageSpec NullTextureSpec(const ShaderRecompiler::IR::ImageResource& resource) {
|
||||
if (resource.kind == ShaderRecompiler::IR::ResourceKind::ImageSint) {
|
||||
return {vk::Format::eR32Sint, Prospero::BufferFormat::k32SInt};
|
||||
}
|
||||
const bool uint_image = resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint ||
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint;
|
||||
return uint_image ? NullImageSpec {vk::Format::eR32Uint, Prospero::BufferFormat::k32UInt}
|
||||
|
|
@ -736,13 +749,25 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
|||
const auto surface_format = TextureGetSurfaceFormatInfo(format);
|
||||
const bool shader_conversion =
|
||||
surface_format.conversion_format != Prospero::BufferFormat::kInvalid;
|
||||
Prospero::TextureNumericClass resource_numeric_class =
|
||||
Prospero::TextureNumericClass::Unsupported;
|
||||
switch (resource.kind) {
|
||||
case ShaderRecompiler::IR::ResourceKind::Image:
|
||||
resource_numeric_class = Prospero::TextureNumericClass::Float;
|
||||
break;
|
||||
case ShaderRecompiler::IR::ResourceKind::ImageUint:
|
||||
resource_numeric_class = Prospero::TextureNumericClass::Uint;
|
||||
break;
|
||||
case ShaderRecompiler::IR::ResourceKind::ImageSint:
|
||||
resource_numeric_class = Prospero::TextureNumericClass::Sint;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
const bool sampled_numeric_class =
|
||||
storage || (Prospero::IsSampledTextureFormat(format) &&
|
||||
(resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint) ==
|
||||
Prospero::IsUintTextureFormat(format));
|
||||
storage || resource_numeric_class == Prospero::SampledTextureNumericClass(format);
|
||||
if (!storage &&
|
||||
(resource.kind == ShaderRecompiler::IR::ResourceKind::Image ||
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint) &&
|
||||
ShaderRecompiler::IR::ImageResourceKindMatches(
|
||||
resource.kind, ShaderRecompiler::IR::ImageResourceClass::Sampled) &&
|
||||
!sampled_numeric_class) {
|
||||
EXIT("sampled image numeric class mismatch: kind=%u format=%u addr=0x%016" PRIx64 "\n",
|
||||
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(format), address);
|
||||
|
|
|
|||
|
|
@ -236,8 +236,8 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
|
|||
}
|
||||
const auto sampled_images = std::count_if(
|
||||
program.info.images.begin(), program.info.images.end(), [](const auto& image) {
|
||||
return image.kind == ShaderRecompiler::IR::ResourceKind::Image ||
|
||||
image.kind == ShaderRecompiler::IR::ResourceKind::ImageUint;
|
||||
return ShaderRecompiler::IR::ImageResourceKindMatches(
|
||||
image.kind, ShaderRecompiler::IR::ImageResourceClass::Sampled);
|
||||
});
|
||||
const bool has_sampler = !program.info.samplers.empty();
|
||||
static std::atomic<uint32_t> dispatch_log_count {0};
|
||||
|
|
@ -265,8 +265,8 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
|
|||
LOGF(" CS texture[%u]: source=%u usage=%s sampled=%s addr=0x%010" PRIx64
|
||||
" type=%u fmt=%u extent=%ux%u depth=%u levels=%u tile=%u\n",
|
||||
i, image.source, image.written ? "read-write" : "read-only",
|
||||
(image.kind == ShaderRecompiler::IR::ResourceKind::Image ||
|
||||
image.kind == ShaderRecompiler::IR::ResourceKind::ImageUint)
|
||||
ShaderRecompiler::IR::ImageResourceKindMatches(
|
||||
image.kind, ShaderRecompiler::IR::ImageResourceClass::Sampled)
|
||||
? "true"
|
||||
: "false",
|
||||
r.Base40(), static_cast<uint32_t>(r.Type()), static_cast<uint32_t>(r.Format()),
|
||||
|
|
|
|||
|
|
@ -199,8 +199,8 @@ static void LogDrawTargetState(const char* draw_name, const RenderColorInfo& col
|
|||
const auto& ps_resources = ps_input_info.stage.program->info;
|
||||
const auto sampled_images = std::count_if(
|
||||
ps_resources.images.begin(), ps_resources.images.end(), [](const auto& image) {
|
||||
return image.kind == ShaderRecompiler::IR::ResourceKind::Image ||
|
||||
image.kind == ShaderRecompiler::IR::ResourceKind::ImageUint;
|
||||
return ShaderRecompiler::IR::ImageResourceKindMatches(
|
||||
image.kind, ShaderRecompiler::IR::ImageResourceClass::Sampled);
|
||||
});
|
||||
|
||||
vk::Extent2D extent = color.image_id ? color.extent : vk::Extent2D {};
|
||||
|
|
|
|||
|
|
@ -163,15 +163,32 @@ uint32_t ImageViewSpatialComponents(ImageViewKind view) {
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t ImageViewImageType(EmitterState& state, ImageViewKind view, bool integer) {
|
||||
const auto component = integer ? TypeU32(state) : TypeF32(state);
|
||||
return state.builder.Type(OpTypeImage,
|
||||
{component, ImageSpirvDimension(view), 0, ImageSpirvArrayed(view),
|
||||
ImageSpirvMultisampled(view), 1, ImageFormatUnknown});
|
||||
uint32_t SampledImageScalarType(EmitterState& state, SampledImageClass image_class) {
|
||||
switch (image_class) {
|
||||
case SampledImageClass::Float: return TypeF32(state);
|
||||
case SampledImageClass::Uint: return TypeU32(state);
|
||||
case SampledImageClass::Sint: return TypeI32(state);
|
||||
case SampledImageClass::Count: break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ImageViewSampledImageType(EmitterState& state, ImageViewKind view, bool integer) {
|
||||
return state.builder.Type(OpTypeSampledImage, {ImageViewImageType(state, view, integer)});
|
||||
uint32_t SampledImageVectorType(EmitterState& state, SampledImageClass image_class,
|
||||
uint32_t components) {
|
||||
return state.builder.Type(OpTypeVector,
|
||||
{SampledImageScalarType(state, image_class), components});
|
||||
}
|
||||
|
||||
uint32_t ImageViewImageType(EmitterState& state, ImageViewKind view,
|
||||
SampledImageClass image_class) {
|
||||
return state.builder.Type(OpTypeImage, {SampledImageScalarType(state, image_class),
|
||||
ImageSpirvDimension(view), 0, ImageSpirvArrayed(view),
|
||||
ImageSpirvMultisampled(view), 1, ImageFormatUnknown});
|
||||
}
|
||||
|
||||
uint32_t ImageViewSampledImageType(EmitterState& state, ImageViewKind view,
|
||||
SampledImageClass image_class) {
|
||||
return state.builder.Type(OpTypeSampledImage, {ImageViewImageType(state, view, image_class)});
|
||||
}
|
||||
|
||||
uint32_t ImageViewSizeType(EmitterState& state, ImageViewKind view) {
|
||||
|
|
@ -218,17 +235,18 @@ uint32_t StorageImageVariable(const EmitterState& state, StorageImageClass image
|
|||
uint32_t LoadSampledImageDescriptor(EmitterState& state, const IR::MemoryInfo& mem, uint32_t use_pc,
|
||||
ImageViewKind view) {
|
||||
(void)use_pc;
|
||||
const bool integer = mem.kind == IR::ResourceKind::ImageUint;
|
||||
const auto kind = SampledBindingKind(integer, view);
|
||||
const auto image_class = SampledImageClassFor(mem.kind);
|
||||
const auto kind = SampledBindingKind(image_class, view);
|
||||
const auto binding = ResourceForDescriptor(state, kind, mem.resource);
|
||||
const auto variable = state.sampled_image_variables[SampledImageIndex(integer, view)];
|
||||
const auto variable = state.sampled_image_variables[SampledImageIndex(image_class, view)];
|
||||
const auto pointer_type = state.builder.Type(
|
||||
OpTypePointer, {StorageClassUniformConstant, ImageViewImageType(state, view, integer)});
|
||||
OpTypePointer, {StorageClassUniformConstant, ImageViewImageType(state, view, image_class)});
|
||||
const auto pointer =
|
||||
DescriptorElementPointer(state, pointer_type, variable, binding.array_index, kind,
|
||||
mem.resource, "sampled image descriptor array was not emitted");
|
||||
const auto image = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpLoad, ImageViewImageType(state, view, integer), image, pointer});
|
||||
state.builder.AddFunction(
|
||||
{OpLoad, ImageViewImageType(state, view, image_class), image, pointer});
|
||||
return image;
|
||||
}
|
||||
|
||||
|
|
@ -252,13 +270,12 @@ uint32_t MakeSampledImage(EmitterState& state, const IR::MemoryInfo& mem, uint32
|
|||
const auto sampler = LoadSamplerDescriptor(state, mem.sampler, use_pc);
|
||||
if (image == 0 || sampler == 0) {
|
||||
ExitDescriptorBindingFailure(
|
||||
state, SampledBindingKind(mem.kind == IR::ResourceKind::ImageUint, view), mem.resource,
|
||||
state, SampledBindingKind(SampledImageClassFor(mem.kind), view), mem.resource,
|
||||
"sampled image or sampler descriptor load failed");
|
||||
}
|
||||
const auto sampled_image = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpSampledImage,
|
||||
ImageViewSampledImageType(state, view, mem.kind == IR::ResourceKind::ImageUint),
|
||||
{OpSampledImage, ImageViewSampledImageType(state, view, SampledImageClassFor(mem.kind)),
|
||||
sampled_image, image, sampler});
|
||||
return sampled_image;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,8 +199,27 @@ uint32_t FloatBits(ValueEmitContext& ctx, uint32_t value) {
|
|||
return Unary(ctx.state, OpBitcast, TypeU32(ctx.state), value);
|
||||
}
|
||||
|
||||
uint32_t ResultVector(ValueEmitContext& ctx, uint32_t value, bool integer, bool dref,
|
||||
const IR::MemoryInfo& mem, bool gather = false) {
|
||||
uint32_t SampledComponentBits(ValueEmitContext& ctx, uint32_t value,
|
||||
SampledImageClass image_class) {
|
||||
return image_class == SampledImageClass::Uint
|
||||
? value
|
||||
: Unary(ctx.state, OpBitcast, TypeU32(ctx.state), value);
|
||||
}
|
||||
|
||||
uint32_t SampledComponentZero(EmitterState& state, SampledImageClass image_class) {
|
||||
switch (image_class) {
|
||||
case SampledImageClass::Float: return ZeroF32(state);
|
||||
case SampledImageClass::Uint: return ConstantU32(state, 0);
|
||||
case SampledImageClass::Sint: return ConstantI32(state, 0);
|
||||
case SampledImageClass::Count: return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ResultVector(ValueEmitContext& ctx, uint32_t value, SampledImageClass image_class,
|
||||
bool dref, const IR::MemoryInfo& mem, bool gather = false) {
|
||||
const auto value_class = dref ? SampledImageClass::Float : image_class;
|
||||
const bool integer = IsIntegerSampledImage(value_class);
|
||||
if (mem.data_bits == 16u) {
|
||||
uint32_t packed[4] = {ConstantU32(ctx.state, 0), ConstantU32(ctx.state, 0),
|
||||
ConstantU32(ctx.state, 0), ConstantU32(ctx.state, 0)};
|
||||
|
|
@ -209,25 +228,27 @@ uint32_t ResultVector(ValueEmitContext& ctx, uint32_t value, bool integer, bool
|
|||
const auto component = gather ? index : DmaskComponent(mem.dmask, index);
|
||||
const auto result = ctx.state.builder.AllocateId();
|
||||
ctx.state.builder.AddFunction({OpCompositeExtract,
|
||||
integer ? TypeU32(ctx.state) : TypeF32(ctx.state),
|
||||
result, value, component});
|
||||
SampledImageScalarType(ctx.state, value_class), result,
|
||||
value, component});
|
||||
return result;
|
||||
};
|
||||
for (uint32_t word = 0; word < mem.data_dwords; word++) {
|
||||
const auto low_index = word * 2u;
|
||||
const auto high_index = low_index + 1u;
|
||||
const auto low = scalar(low_index);
|
||||
const auto high = high_index < mem.component_count
|
||||
? scalar(high_index)
|
||||
: (integer ? ConstantU32(ctx.state, 0) : ZeroF32(ctx.state));
|
||||
const auto high = high_index < mem.component_count
|
||||
? scalar(high_index)
|
||||
: SampledComponentZero(ctx.state, value_class);
|
||||
if (integer) {
|
||||
const auto mask = ConstantU32(ctx.state, 0xffffu);
|
||||
packed[word] =
|
||||
Binary(ctx.state, OpBitwiseOr, TypeU32(ctx.state),
|
||||
Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), low, mask),
|
||||
Binary(ctx.state, OpShiftLeftLogical, TypeU32(ctx.state),
|
||||
Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), high, mask),
|
||||
ConstantU32(ctx.state, 16u)));
|
||||
const auto low_bits = SampledComponentBits(ctx, low, value_class);
|
||||
const auto high_bits = SampledComponentBits(ctx, high, value_class);
|
||||
const auto mask = ConstantU32(ctx.state, 0xffffu);
|
||||
packed[word] = Binary(
|
||||
ctx.state, OpBitwiseOr, TypeU32(ctx.state),
|
||||
Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), low_bits, mask),
|
||||
Binary(ctx.state, OpShiftLeftLogical, TypeU32(ctx.state),
|
||||
Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), high_bits, mask),
|
||||
ConstantU32(ctx.state, 16u)));
|
||||
} else {
|
||||
const auto pair = ctx.state.builder.AllocateId();
|
||||
ctx.state.builder.AddFunction(
|
||||
|
|
@ -250,9 +271,9 @@ uint32_t ResultVector(ValueEmitContext& ctx, uint32_t value, bool integer, bool
|
|||
}
|
||||
const auto scalar = ctx.state.builder.AllocateId();
|
||||
ctx.state.builder.AddFunction({OpCompositeExtract,
|
||||
integer ? TypeU32(ctx.state) : TypeF32(ctx.state), scalar,
|
||||
SampledImageScalarType(ctx.state, value_class), scalar,
|
||||
value, index});
|
||||
component[index] = integer ? scalar : FloatBits(ctx, scalar);
|
||||
component[index] = SampledComponentBits(ctx, scalar, value_class);
|
||||
}
|
||||
const auto result = ctx.state.builder.AllocateId();
|
||||
ctx.state.builder.AddFunction({OpCompositeConstruct, TypeU32Vector(ctx.state, 4), result,
|
||||
|
|
@ -363,8 +384,7 @@ Format::BufferFormatInfo ImageConversionFormat(const EmitterState& state,
|
|||
const auto format = state.program.info.images[mem.resource].conversion_format;
|
||||
if (format == Prospero::BufferFormat::kInvalid) return {};
|
||||
const auto info = Format::GetFormatInfo(format);
|
||||
EXIT_IF(!Prospero::IsSampledTextureFormat(format) ||
|
||||
!Prospero::IsUintTextureFormat(format) ||
|
||||
EXIT_IF(Prospero::SampledTextureNumericClass(format) != Prospero::TextureNumericClass::Uint ||
|
||||
Prospero::RemapTextureFormat(format) == format ||
|
||||
info.type != Format::ComponentType::Uint || !info.packed_bitfield ||
|
||||
info.byte_size != sizeof(uint32_t) || info.component_count == 0u ||
|
||||
|
|
@ -440,7 +460,7 @@ uint32_t UnpackImageGather(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uin
|
|||
}
|
||||
|
||||
uint32_t EmitOneDimensionalGatherLz(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t pc,
|
||||
uint32_t coord, bool integer) {
|
||||
uint32_t coord, SampledImageClass image_class) {
|
||||
auto& state = ctx.state;
|
||||
state.builder.RequireCapability(CapabilityImageQuery);
|
||||
const auto image = LoadSampledImageDescriptor(state, mem, pc, ImageViewKind::Dim1D);
|
||||
|
|
@ -457,8 +477,8 @@ uint32_t EmitOneDimensionalGatherLz(ValueEmitContext& ctx, const IR::MemoryInfo&
|
|||
ConstantF32(state, 0x3f000000u))});
|
||||
|
||||
const auto sampled = MakeSampledImage(state, mem, pc, ImageViewKind::Dim1D);
|
||||
const auto vector_type = integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4);
|
||||
const auto scalar_type = integer ? TypeU32(state) : TypeF32(state);
|
||||
const auto vector_type = SampledImageVectorType(state, image_class, 4);
|
||||
const auto scalar_type = SampledImageScalarType(state, image_class);
|
||||
const auto component = ImageConversionFormat(state, mem).format == Prospero::BufferFormat::kInvalid
|
||||
? ImageGatherComponent(mem.dmask)
|
||||
: 0u;
|
||||
|
|
@ -601,9 +621,9 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||
return true;
|
||||
}
|
||||
if (op == IR::ValueOpcode::ImageRead) {
|
||||
const auto view = SampledImageViewKind(state, mem, pc);
|
||||
const bool integer = mem.kind == IR::ResourceKind::ImageUint;
|
||||
const auto condition = ctx.Arg(inst, 2);
|
||||
const auto view = SampledImageViewKind(state, mem, pc);
|
||||
const auto image_class = SampledImageClassFor(mem.kind);
|
||||
const auto condition = ctx.Arg(inst, 2);
|
||||
ctx.Define(
|
||||
inst,
|
||||
EmitValueOrDefaultIfCondition(
|
||||
|
|
@ -614,17 +634,15 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||
const auto coord = CoordU32(ctx, mem, *address, view);
|
||||
if (ImageSpirvMultisampled(view) != 0u) {
|
||||
state.builder.AddFunction(
|
||||
{OpImageFetch,
|
||||
integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4), color,
|
||||
{OpImageFetch, SampledImageVectorType(state, image_class, 4), color,
|
||||
image, coord, ImageOperandsSampleMask,
|
||||
AddressU32(ctx, mem, *address, ImageViewCoordinateComponents(view))});
|
||||
} else {
|
||||
state.builder.AddFunction(
|
||||
{OpImageFetch,
|
||||
integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4), color,
|
||||
{OpImageFetch, SampledImageVectorType(state, image_class, 4), color,
|
||||
image, coord, ImageOperandsLodMask, LodU32(ctx, mem, *address, view)});
|
||||
}
|
||||
return ResultVector(ctx, UnpackImageTexel(ctx, mem, color), integer, false,
|
||||
return ResultVector(ctx, UnpackImageTexel(ctx, mem, color), image_class, false,
|
||||
mem);
|
||||
}));
|
||||
return true;
|
||||
|
|
@ -644,10 +662,10 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||
return true;
|
||||
}
|
||||
if (op == IR::ValueOpcode::ImageSampleRaw || op == IR::ValueOpcode::ImageGatherRaw) {
|
||||
const auto view = SampledImageViewKind(state, mem, pc);
|
||||
const auto layout = Layout(mem, view);
|
||||
const bool integer = mem.kind == IR::ResourceKind::ImageUint;
|
||||
const bool dref = HasFlag(mem, Decoder::ImageSampleFlagCompare);
|
||||
const auto view = SampledImageViewKind(state, mem, pc);
|
||||
const auto layout = Layout(mem, view);
|
||||
const auto image_class = SampledImageClassFor(mem.kind);
|
||||
const bool dref = HasFlag(mem, Decoder::ImageSampleFlagCompare);
|
||||
if (dref && state.program.info.images[mem.resource].conversion_format !=
|
||||
Prospero::BufferFormat::kInvalid) {
|
||||
ctx.Fail(inst, "uses depth comparison with a packed integer image");
|
||||
|
|
@ -663,8 +681,8 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||
ctx.Fail(inst, "has an unsupported 1D gather variant");
|
||||
return true;
|
||||
}
|
||||
const auto sample = EmitOneDimensionalGatherLz(ctx, mem, pc, coord, integer);
|
||||
ctx.Define(inst, ResultVector(ctx, UnpackImageGather(ctx, mem, sample), integer,
|
||||
const auto sample = EmitOneDimensionalGatherLz(ctx, mem, pc, coord, image_class);
|
||||
ctx.Define(inst, ResultVector(ctx, UnpackImageGather(ctx, mem, sample), image_class,
|
||||
false, mem, true));
|
||||
return true;
|
||||
}
|
||||
|
|
@ -685,7 +703,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||
: ZeroF32(state)}
|
||||
: std::vector<uint32_t> {
|
||||
OpImageGather,
|
||||
integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4),
|
||||
SampledImageVectorType(state, image_class, 4),
|
||||
sample,
|
||||
sampled,
|
||||
coord,
|
||||
|
|
@ -702,7 +720,8 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||
}
|
||||
state.builder.AddFunction(words);
|
||||
ctx.Define(inst, ResultVector(ctx, UnpackImageGather(ctx, mem, sample),
|
||||
integer && !dref, false, mem, true));
|
||||
dref ? SampledImageClass::Float : image_class, false, mem,
|
||||
true));
|
||||
return true;
|
||||
}
|
||||
const bool explicit_lod = HasFlag(mem, Decoder::ImageSampleFlagDerivative) ||
|
||||
|
|
@ -713,7 +732,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||
? (dref ? OpImageSampleDrefExplicitLod : OpImageSampleExplicitLod)
|
||||
: (dref ? OpImageSampleDrefImplicitLod : OpImageSampleImplicitLod);
|
||||
const auto result_type =
|
||||
dref ? TypeF32(state) : (integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4));
|
||||
dref ? TypeF32(state) : SampledImageVectorType(state, image_class, 4);
|
||||
const auto dref_value =
|
||||
dref ? (layout.dref != NoImageComponent ? AddressF32(ctx, mem, *address, layout.dref)
|
||||
: ZeroF32(state))
|
||||
|
|
@ -754,7 +773,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||
if (image.indirect_root != mem.resource) {
|
||||
const auto sample = EmitSample(mem.resource);
|
||||
ctx.Define(inst, ResultVector(ctx, dref ? sample : UnpackImageTexel(ctx, mem, sample),
|
||||
integer, dref, mem));
|
||||
image_class, dref, mem));
|
||||
return true;
|
||||
}
|
||||
const auto* handle = image_arg.ResolveInstruction();
|
||||
|
|
@ -847,7 +866,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||
state.builder.AddFunction(phi_words);
|
||||
ctx.Define(inst,
|
||||
ResultVector(ctx, dref ? phi_words[2] : UnpackImageTexel(ctx, mem, phi_words[2]),
|
||||
integer, dref, mem));
|
||||
image_class, dref, mem));
|
||||
return true;
|
||||
}
|
||||
const auto atomic_opcode = ImageAtomicOpcode(op);
|
||||
|
|
|
|||
|
|
@ -337,6 +337,8 @@ enum class ImageViewKind {
|
|||
constexpr uint32_t SampledImageViewKindCount = static_cast<uint32_t>(ImageViewKind::Count);
|
||||
constexpr uint32_t StorageImageViewKindCount = static_cast<uint32_t>(ImageViewKind::Dim2DMsaa);
|
||||
|
||||
enum class SampledImageClass : uint32_t { Float, Uint, Sint, Count };
|
||||
|
||||
enum class StorageImageClass : uint32_t {
|
||||
FormatlessFloat,
|
||||
FormatlessUint,
|
||||
|
|
@ -369,7 +371,9 @@ struct EmitterState {
|
|||
uint32_t flattened_srt_variable = 0;
|
||||
uint32_t lds_variable = 0;
|
||||
uint32_t scratch_variable = 0;
|
||||
std::array<uint32_t, 2u * SampledImageViewKindCount> sampled_image_variables {};
|
||||
std::array<uint32_t,
|
||||
static_cast<uint32_t>(SampledImageClass::Count) * SampledImageViewKindCount>
|
||||
sampled_image_variables {};
|
||||
std::array<uint32_t,
|
||||
static_cast<uint32_t>(StorageImageClass::Count) * StorageImageViewKindCount>
|
||||
storage_image_variables {};
|
||||
|
|
@ -471,8 +475,22 @@ struct ImageSampleLayout {
|
|||
uint32_t grad_y = NoImageComponent;
|
||||
};
|
||||
|
||||
constexpr uint32_t SampledImageIndex(bool integer, ImageViewKind view) {
|
||||
return static_cast<uint32_t>(view) + (integer ? SampledImageViewKindCount : 0u);
|
||||
constexpr SampledImageClass SampledImageClassFor(IR::ResourceKind kind) {
|
||||
switch (kind) {
|
||||
case IR::ResourceKind::Image: return SampledImageClass::Float;
|
||||
case IR::ResourceKind::ImageUint: return SampledImageClass::Uint;
|
||||
case IR::ResourceKind::ImageSint: return SampledImageClass::Sint;
|
||||
default: return SampledImageClass::Count;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsIntegerSampledImage(SampledImageClass image_class) {
|
||||
return image_class == SampledImageClass::Uint || image_class == SampledImageClass::Sint;
|
||||
}
|
||||
|
||||
constexpr uint32_t SampledImageIndex(SampledImageClass image_class, ImageViewKind view) {
|
||||
return static_cast<uint32_t>(view) +
|
||||
static_cast<uint32_t>(image_class) * SampledImageViewKindCount;
|
||||
}
|
||||
|
||||
constexpr StorageImageClass StorageImageClassFor(bool uint_image, bool atomic) {
|
||||
|
|
@ -490,8 +508,9 @@ constexpr uint32_t StorageImageIndex(StorageImageClass image_class, ImageViewKin
|
|||
static_cast<uint32_t>(image_class) * StorageImageViewKindCount;
|
||||
}
|
||||
|
||||
constexpr IR::DescriptorBindingKind SampledBindingKind(bool integer, ImageViewKind view) {
|
||||
if (integer) {
|
||||
constexpr IR::DescriptorBindingKind SampledBindingKind(SampledImageClass image_class,
|
||||
ImageViewKind view) {
|
||||
if (image_class == SampledImageClass::Uint) {
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim1D: return IR::DescriptorBindingKind::SampledUint1D;
|
||||
case ImageViewKind::Dim1DArray: return IR::DescriptorBindingKind::SampledUint1DArray;
|
||||
|
|
@ -504,6 +523,19 @@ constexpr IR::DescriptorBindingKind SampledBindingKind(bool integer, ImageViewKi
|
|||
default: break;
|
||||
}
|
||||
}
|
||||
if (image_class == SampledImageClass::Sint) {
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim1D: return IR::DescriptorBindingKind::SampledSint1D;
|
||||
case ImageViewKind::Dim1DArray: return IR::DescriptorBindingKind::SampledSint1DArray;
|
||||
case ImageViewKind::Dim2D: return IR::DescriptorBindingKind::SampledSint2D;
|
||||
case ImageViewKind::Dim2DArray: return IR::DescriptorBindingKind::SampledSint2DArray;
|
||||
case ImageViewKind::Dim3D: return IR::DescriptorBindingKind::SampledSint3D;
|
||||
case ImageViewKind::Dim2DMsaa: return IR::DescriptorBindingKind::SampledSint2DMsaa;
|
||||
case ImageViewKind::Dim2DMsaaArray:
|
||||
return IR::DescriptorBindingKind::SampledSint2DMsaaArray;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
switch (view) {
|
||||
case ImageViewKind::Dim1D: return IR::DescriptorBindingKind::Sampled1D;
|
||||
case ImageViewKind::Dim1DArray: return IR::DescriptorBindingKind::Sampled1DArray;
|
||||
|
|
@ -641,9 +673,15 @@ uint32_t ImageViewCoordinateComponents(ImageViewKind view);
|
|||
|
||||
uint32_t ImageViewSpatialComponents(ImageViewKind view);
|
||||
|
||||
uint32_t ImageViewImageType(EmitterState& state, ImageViewKind view, bool integer);
|
||||
uint32_t SampledImageScalarType(EmitterState& state, SampledImageClass image_class);
|
||||
|
||||
uint32_t ImageViewSampledImageType(EmitterState& state, ImageViewKind view, bool integer);
|
||||
uint32_t SampledImageVectorType(EmitterState& state, SampledImageClass image_class,
|
||||
uint32_t components);
|
||||
|
||||
uint32_t ImageViewImageType(EmitterState& state, ImageViewKind view, SampledImageClass image_class);
|
||||
|
||||
uint32_t ImageViewSampledImageType(EmitterState& state, ImageViewKind view,
|
||||
SampledImageClass image_class);
|
||||
|
||||
uint32_t ImageViewSizeType(EmitterState& state, ImageViewKind view);
|
||||
|
||||
|
|
|
|||
|
|
@ -188,14 +188,14 @@ void DefineDescriptorVariables(EmitterState& state) {
|
|||
TypeStorageBufferPointer(state), StorageClassStorageBuffer);
|
||||
}
|
||||
for (uint32_t i = 0; i < state.sampled_image_variables.size(); i++) {
|
||||
const auto view = static_cast<ImageViewKind>(i % SampledImageViewKindCount);
|
||||
const bool integer = i >= SampledImageViewKindCount;
|
||||
const auto kind = SampledBindingKind(integer, view);
|
||||
const auto view = static_cast<ImageViewKind>(i % SampledImageViewKindCount);
|
||||
const auto image_class = static_cast<SampledImageClass>(i / SampledImageViewKindCount);
|
||||
const auto kind = SampledBindingKind(image_class, view);
|
||||
if (DescriptorBinding(state, kind) == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const auto count = ConstantU32(state, DescriptorCount(state, kind));
|
||||
const auto image_type = ImageViewImageType(state, view, integer);
|
||||
const auto image_type = ImageViewImageType(state, view, image_class);
|
||||
const auto array_type = state.builder.Type(OpTypeArray, {image_type, count});
|
||||
const auto pointer_type = TypePointer(state, StorageClassUniformConstant, array_type);
|
||||
state.sampled_image_variables[i] =
|
||||
|
|
@ -607,11 +607,19 @@ void AddDescriptorAnnotationsAndNames(EmitterState& state) {
|
|||
"sampled_uint_2d_array",
|
||||
"sampled_uint_3d",
|
||||
"sampled_uint_2d_msaa",
|
||||
"sampled_uint_2d_msaa_array"};
|
||||
"sampled_uint_2d_msaa_array",
|
||||
"sampled_sint_1d",
|
||||
"sampled_sint_1d_array",
|
||||
"sampled_sint_2d",
|
||||
"sampled_sint_2d_array",
|
||||
"sampled_sint_3d",
|
||||
"sampled_sint_2d_msaa",
|
||||
"sampled_sint_2d_msaa_array"};
|
||||
for (uint32_t i = 0; i < state.sampled_image_variables.size(); i++) {
|
||||
const auto view = static_cast<ImageViewKind>(i % SampledImageViewKindCount);
|
||||
const auto image_class = static_cast<SampledImageClass>(i / SampledImageViewKindCount);
|
||||
Decorate(state.sampled_image_variables[i], SampledNames[i],
|
||||
SampledBindingKind(i >= SampledImageViewKindCount, view));
|
||||
SampledBindingKind(image_class, view));
|
||||
}
|
||||
constexpr const char* StorageNames[] = {"storage_1d",
|
||||
"storage_1d_array",
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ enum class ResourceKind {
|
|||
Gds,
|
||||
Image,
|
||||
ImageUint,
|
||||
ImageSint,
|
||||
StorageImage,
|
||||
StorageImageUint,
|
||||
Sampler
|
||||
|
|
@ -45,7 +46,8 @@ enum class ResourceKind {
|
|||
ImageResourceClass resource_class) {
|
||||
switch (resource_class) {
|
||||
case ImageResourceClass::Sampled:
|
||||
return kind == ResourceKind::Image || kind == ResourceKind::ImageUint;
|
||||
return kind == ResourceKind::Image || kind == ResourceKind::ImageUint ||
|
||||
kind == ResourceKind::ImageSint;
|
||||
case ImageResourceClass::Storage:
|
||||
return kind == ResourceKind::StorageImage || kind == ResourceKind::StorageImageUint;
|
||||
case ImageResourceClass::StorageUint: return kind == ResourceKind::StorageImageUint;
|
||||
|
|
@ -290,6 +292,13 @@ enum class DescriptorBindingKind {
|
|||
SampledUint2DMsaa,
|
||||
SampledUint2DMsaaArray,
|
||||
SampledUint3D,
|
||||
SampledSint1D,
|
||||
SampledSint1DArray,
|
||||
SampledSint2D,
|
||||
SampledSint2DArray,
|
||||
SampledSint2DMsaa,
|
||||
SampledSint2DMsaaArray,
|
||||
SampledSint3D,
|
||||
Storage1D,
|
||||
Storage1DArray,
|
||||
Storage2D,
|
||||
|
|
@ -349,6 +358,17 @@ DescriptorBindingForImage(const ImageResource& image) {
|
|||
case Dimension::Dim3D: return Kind::SampledUint3D;
|
||||
default: return std::nullopt;
|
||||
}
|
||||
case ResourceKind::ImageSint:
|
||||
switch (image.dimension) {
|
||||
case Dimension::Dim1D: return Kind::SampledSint1D;
|
||||
case Dimension::Dim1DArray: return Kind::SampledSint1DArray;
|
||||
case Dimension::Dim2D: return Kind::SampledSint2D;
|
||||
case Dimension::Dim2DArray: return Kind::SampledSint2DArray;
|
||||
case Dimension::Dim2DMsaa: return Kind::SampledSint2DMsaa;
|
||||
case Dimension::Dim2DMsaaArray: return Kind::SampledSint2DMsaaArray;
|
||||
case Dimension::Dim3D: return Kind::SampledSint3D;
|
||||
default: return std::nullopt;
|
||||
}
|
||||
case ResourceKind::StorageImage:
|
||||
if (image.atomic) {
|
||||
return std::nullopt;
|
||||
|
|
|
|||
|
|
@ -89,6 +89,20 @@ Prospero::BufferFormat ImageConversionFormat(Prospero::BufferFormat format) {
|
|||
: Prospero::BufferFormat::kInvalid;
|
||||
}
|
||||
|
||||
Prospero::TextureNumericClass SampledResourceNumericClass(ResourceKind kind) {
|
||||
switch (kind) {
|
||||
case ResourceKind::Image: return Prospero::TextureNumericClass::Float;
|
||||
case ResourceKind::ImageUint: return Prospero::TextureNumericClass::Uint;
|
||||
case ResourceKind::ImageSint: return Prospero::TextureNumericClass::Sint;
|
||||
default: return Prospero::TextureNumericClass::Unsupported;
|
||||
}
|
||||
}
|
||||
|
||||
bool RequiresPointSampler(const ImageResource& image) {
|
||||
return image.kind == ResourceKind::ImageSint ||
|
||||
image.conversion_format != Prospero::BufferFormat::kInvalid;
|
||||
}
|
||||
|
||||
bool DescriptorIsCube(const DescriptorValue& descriptor) {
|
||||
return static_cast<Prospero::ImageType>((descriptor.dwords[3] >> 28u) & 0xfu) ==
|
||||
Prospero::ImageType::kCube;
|
||||
|
|
@ -378,9 +392,8 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
|||
DescriptorIsCube(descriptor) != image.cube) {
|
||||
return false;
|
||||
}
|
||||
if (image.kind == ResourceKind::Image || image.kind == ResourceKind::ImageUint ||
|
||||
image.kind == ResourceKind::StorageImage ||
|
||||
image.kind == ResourceKind::StorageImageUint) {
|
||||
if (ImageResourceKindMatches(image.kind, ImageResourceClass::Sampled) ||
|
||||
ImageResourceKindMatches(image.kind, ImageResourceClass::Storage)) {
|
||||
const bool storage = image.kind == ResourceKind::StorageImage ||
|
||||
image.kind == ResourceKind::StorageImageUint;
|
||||
const auto format =
|
||||
|
|
@ -396,16 +409,27 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
|||
image.shader_swizzle != DescriptorImageSwizzle(descriptor)) {
|
||||
return false;
|
||||
}
|
||||
const bool raw_sint_storage = storage && format == Prospero::BufferFormat::k32SInt &&
|
||||
image.written && !image.read && !image.atomic;
|
||||
const bool uint_descriptor = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
||||
const auto uint_program = image.kind == ResourceKind::ImageUint ||
|
||||
image.kind == ResourceKind::StorageImageUint;
|
||||
if (uint_descriptor != uint_program && !(image.atomic && uint_program)) {
|
||||
return false;
|
||||
}
|
||||
if (image.depth_compare && uint_program) {
|
||||
return false;
|
||||
if (storage) {
|
||||
const bool raw_sint = format == Prospero::BufferFormat::k32SInt && image.written &&
|
||||
!image.read && !image.atomic;
|
||||
const auto numeric_class = Prospero::SampledTextureNumericClass(format);
|
||||
if ((!raw_sint && numeric_class == Prospero::TextureNumericClass::Sint) ||
|
||||
numeric_class == Prospero::TextureNumericClass::Unsupported) {
|
||||
return false;
|
||||
}
|
||||
const bool uint_descriptor =
|
||||
numeric_class == Prospero::TextureNumericClass::Uint || raw_sint;
|
||||
const bool uint_program = image.kind == ResourceKind::StorageImageUint;
|
||||
if (uint_descriptor != uint_program && !(image.atomic && uint_program)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const auto numeric_class = SampledResourceNumericClass(image.kind);
|
||||
if (numeric_class != Prospero::SampledTextureNumericClass(format) ||
|
||||
(image.depth_compare &&
|
||||
numeric_class != Prospero::TextureNumericClass::Float)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -614,6 +638,7 @@ void SpecializeResources(Program& program, ResourceSnapshot& snapshot) {
|
|||
image.cube = false;
|
||||
switch (image.kind) {
|
||||
case ResourceKind::ImageUint: image.kind = ResourceKind::Image; break;
|
||||
case ResourceKind::ImageSint: image.kind = ResourceKind::Image; break;
|
||||
case ResourceKind::StorageImageUint:
|
||||
if (!image.atomic) {
|
||||
image.kind = ResourceKind::StorageImage;
|
||||
|
|
@ -649,12 +674,30 @@ void SpecializeResources(Program& program, ResourceSnapshot& snapshot) {
|
|||
}
|
||||
const bool raw_sint_storage = storage && format == Prospero::BufferFormat::k32SInt &&
|
||||
image.written && !image.read && !image.atomic;
|
||||
const bool uint_image = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
||||
if (uint_image) {
|
||||
switch (image.kind) {
|
||||
case ResourceKind::Image: image.kind = ResourceKind::ImageUint; break;
|
||||
case ResourceKind::StorageImage: image.kind = ResourceKind::StorageImageUint; break;
|
||||
default: break;
|
||||
const auto numeric_class = Prospero::SampledTextureNumericClass(format);
|
||||
if (storage) {
|
||||
if ((!raw_sint_storage && numeric_class == Prospero::TextureNumericClass::Sint) ||
|
||||
numeric_class == Prospero::TextureNumericClass::Unsupported) {
|
||||
return SpecializationFail(
|
||||
fmt::format("storage image descriptor {} uses unsupported format {}", i,
|
||||
static_cast<uint32_t>(format)));
|
||||
}
|
||||
if (numeric_class == Prospero::TextureNumericClass::Uint || raw_sint_storage) {
|
||||
image.kind = ResourceKind::StorageImageUint;
|
||||
}
|
||||
} else {
|
||||
switch (numeric_class) {
|
||||
case Prospero::TextureNumericClass::Uint:
|
||||
image.kind = ResourceKind::ImageUint;
|
||||
break;
|
||||
case Prospero::TextureNumericClass::Sint:
|
||||
image.kind = ResourceKind::ImageSint;
|
||||
break;
|
||||
case Prospero::TextureNumericClass::Float: break;
|
||||
case Prospero::TextureNumericClass::Unsupported:
|
||||
return SpecializationFail(
|
||||
fmt::format("sampled image descriptor {} uses unsupported format {}", i,
|
||||
static_cast<uint32_t>(format)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -723,7 +766,7 @@ void SpecializeResources(Program& program, ResourceSnapshot& snapshot) {
|
|||
fmt::format("sampled resource pair at pc 0x{:08x} is invalid", pair.first_use_pc));
|
||||
}
|
||||
auto& usage = sampler_usage[pair.sampler];
|
||||
if (next.images[pair.image].conversion_format != Prospero::BufferFormat::kInvalid) {
|
||||
if (RequiresPointSampler(next.images[pair.image])) {
|
||||
usage.point_only = true;
|
||||
} else {
|
||||
usage.native = true;
|
||||
|
|
@ -750,7 +793,7 @@ void SpecializeResources(Program& program, ResourceSnapshot& snapshot) {
|
|||
next_snapshot.samplers.push_back(next_snapshot.samplers[i]);
|
||||
}
|
||||
for (auto& pair: next.sampled_pairs) {
|
||||
if (next.images[pair.image].conversion_format != Prospero::BufferFormat::kInvalid) {
|
||||
if (RequiresPointSampler(next.images[pair.image])) {
|
||||
pair.sampler = point_sampler[pair.sampler];
|
||||
}
|
||||
}
|
||||
|
|
@ -771,8 +814,7 @@ void SpecializeResources(Program& program, ResourceSnapshot& snapshot) {
|
|||
"typed image instruction has invalid resource {}", memory.resource));
|
||||
}
|
||||
const auto& image = next.images[memory.resource];
|
||||
if (image_opcode.needs_sampler &&
|
||||
image.conversion_format != Prospero::BufferFormat::kInvalid &&
|
||||
if (image_opcode.needs_sampler && RequiresPointSampler(image) &&
|
||||
memory.sampler < point_sampler.size()) {
|
||||
memory.sampler = point_sampler[memory.sampler];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9531,7 +9531,9 @@ public:
|
|||
test.name, "dispatch",
|
||||
Count(Kind::Sampled2DArray) == 0 && Count(Kind::Sampled3D) == 0 &&
|
||||
Count(Kind::SampledUint2DArray) == 0 &&
|
||||
Count(Kind::SampledUint3D) == 0 && Count(Kind::Storage1D) == 0 &&
|
||||
Count(Kind::SampledUint3D) == 0 &&
|
||||
Count(Kind::SampledSint2DArray) == 0 &&
|
||||
Count(Kind::SampledSint3D) == 0 && Count(Kind::Storage1D) == 0 &&
|
||||
Count(Kind::Storage1DArray) == 0 &&
|
||||
Count(Kind::Storage2DArray) == 0 && Count(Kind::Storage3D) == 0 &&
|
||||
Count(Kind::StorageUint1D) == 0 &&
|
||||
|
|
@ -9646,7 +9648,10 @@ public:
|
|||
Count(Kind::Sampled2D) + Count(Kind::Sampled2DArray) +
|
||||
Count(Kind::Sampled3D) + Count(Kind::SampledUint1D) +
|
||||
Count(Kind::SampledUint1DArray) + Count(Kind::SampledUint2D) +
|
||||
Count(Kind::SampledUint2DArray) + Count(Kind::SampledUint3D));
|
||||
Count(Kind::SampledUint2DArray) + Count(Kind::SampledUint3D) +
|
||||
Count(Kind::SampledSint1D) + Count(Kind::SampledSint1DArray) +
|
||||
Count(Kind::SampledSint2D) + Count(Kind::SampledSint2DArray) +
|
||||
Count(Kind::SampledSint3D));
|
||||
add_pool_size(
|
||||
vk::DescriptorType::eStorageImage,
|
||||
Count(Kind::Storage1D) + Count(Kind::Storage1DArray) +
|
||||
|
|
@ -9791,6 +9796,7 @@ public:
|
|||
constexpr std::array sampled_kinds{
|
||||
Kind::Sampled1D, Kind::Sampled1DArray, Kind::Sampled2D,
|
||||
Kind::SampledUint1D, Kind::SampledUint1DArray, Kind::SampledUint2D,
|
||||
Kind::SampledSint1D, Kind::SampledSint1DArray, Kind::SampledSint2D,
|
||||
};
|
||||
for (const auto kind : sampled_kinds) {
|
||||
if (const auto *candidate = Binding(kind); candidate != nullptr) {
|
||||
|
|
@ -10734,8 +10740,8 @@ public:
|
|||
{16, 0}, {8, 16}, {0, 24}};
|
||||
bool valid = built && Prospero::NumBytesPerElement(format) == 4 &&
|
||||
Prospero::BlockCompressedBytesPerBlock(format) == 0 &&
|
||||
Prospero::IsSampledTextureFormat(format) &&
|
||||
Prospero::IsUintTextureFormat(format) &&
|
||||
Prospero::SampledTextureNumericClass(format) ==
|
||||
Prospero::TextureNumericClass::Uint &&
|
||||
Prospero::RemapTextureFormat(format) ==
|
||||
Prospero::BufferFormat::k32UInt &&
|
||||
surface_format.vk_format == vk::Format::eR32Uint &&
|
||||
|
|
@ -10756,18 +10762,17 @@ public:
|
|||
"PS5 packed integer texture metadata or Standard64KB mip tail "
|
||||
"changed");
|
||||
|
||||
const auto storage_only_format = Prospero::BufferFormat::k32SInt;
|
||||
const auto storage_only_surface =
|
||||
TextureGetSurfaceFormatInfo(storage_only_format);
|
||||
Require(name, "storage-only surface mapping",
|
||||
!Prospero::IsSampledTextureFormat(storage_only_format) &&
|
||||
!Prospero::IsUintTextureFormat(storage_only_format) &&
|
||||
Prospero::RemapTextureFormat(storage_only_format) ==
|
||||
storage_only_format &&
|
||||
storage_only_surface.vk_format == vk::Format::eR32Sint &&
|
||||
storage_only_surface.conversion_format ==
|
||||
const auto signed_format = Prospero::BufferFormat::k32SInt;
|
||||
const auto signed_surface = TextureGetSurfaceFormatInfo(signed_format);
|
||||
Require(name, "signed sampled surface mapping",
|
||||
Prospero::SampledTextureNumericClass(signed_format) ==
|
||||
Prospero::TextureNumericClass::Sint &&
|
||||
Prospero::RemapTextureFormat(signed_format) ==
|
||||
signed_format &&
|
||||
signed_surface.vk_format == vk::Format::eR32Sint &&
|
||||
signed_surface.conversion_format ==
|
||||
Prospero::BufferFormat::kInvalid,
|
||||
"storage-only native backing was conflated with sampled support");
|
||||
"signed sampled texture class or native backing changed");
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -11820,7 +11825,10 @@ void RunCase(VulkanHarness *vulkan, const TestCase &test) {
|
|||
Has(Kind::Sampled2D) || Has(Kind::Sampled2DArray) ||
|
||||
Has(Kind::Sampled3D) || Has(Kind::SampledUint2D) ||
|
||||
Has(Kind::SampledUint1D) || Has(Kind::SampledUint1DArray) ||
|
||||
Has(Kind::SampledUint2DArray) || Has(Kind::SampledUint3D);
|
||||
Has(Kind::SampledUint2DArray) || Has(Kind::SampledUint3D) ||
|
||||
Has(Kind::SampledSint2D) || Has(Kind::SampledSint1D) ||
|
||||
Has(Kind::SampledSint1DArray) || Has(Kind::SampledSint2DArray) ||
|
||||
Has(Kind::SampledSint3D);
|
||||
const bool needs_storage_image =
|
||||
Has(Kind::Storage1D) || Has(Kind::Storage1DArray) ||
|
||||
Has(Kind::Storage2D) || Has(Kind::Storage2DArray) ||
|
||||
|
|
@ -19740,6 +19748,34 @@ TestCase ImageLoadR32UintUsesIntegerSampledImage() {
|
|||
return test;
|
||||
}
|
||||
|
||||
TestCase ImageLoadR32SintUsesSignedSampledImage() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 20, 2);
|
||||
AppendVMovU32(&code, 21, 1);
|
||||
code.push_back(EncodeMimg0(0x00, 0x1));
|
||||
code.push_back(EncodeMimg1(0, 20));
|
||||
AppendStoreVgpr(&code, 0, 0);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "ImageLoadR32SintUsesSignedSampledImage";
|
||||
test.code = code;
|
||||
test.expected = {0xffffff80u};
|
||||
test.opcodes = {O::V_MOV_B32, O::IMAGE_LOAD, O::BUFFER_STORE_DWORD,
|
||||
O::S_ENDPGM};
|
||||
test.sampled_image_rgba.resize(16);
|
||||
test.sampled_image_rgba[6] = 0xffffff80u;
|
||||
test.sampled_image_format = vk::Format::eR32Sint;
|
||||
test.sampled_image_dwords_per_pixel = 1;
|
||||
test.user_data = MakeSampledTextureData(Prospero::BufferFormat::k32SInt);
|
||||
test.has_user_data = true;
|
||||
test.required_spirv = {"sampled_sint_2d", "OpTypeImage %int",
|
||||
"OpImageFetch %v4int", "OpBitcast %uint"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase ImageLoadPackedUintUnpacksAndSwizzles() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
|
|
@ -21511,6 +21547,7 @@ std::vector<TestCase> MakeCases() {
|
|||
AddCase(BufferAtomicFMaxContendedWorkgroup);
|
||||
AddCase(ImageLoadVariants);
|
||||
AddCase(ImageLoadR32UintUsesIntegerSampledImage);
|
||||
AddCase(ImageLoadR32SintUsesSignedSampledImage);
|
||||
AddCase(ImageLoadPackedUintUnpacksAndSwizzles);
|
||||
AddCase(ImageSamplePackedUintConvertsSampleAndGather);
|
||||
AddCase(ImageLoadR128IgnoresAdjacentMaskSgprs);
|
||||
|
|
@ -23611,15 +23648,48 @@ void CheckImageSamplerSpecialization() {
|
|||
sampled_image.kind = ShaderRecompiler::IR::ResourceKind::Image;
|
||||
sampled_image.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
|
||||
sampled_image.read = true;
|
||||
mixed_sampler_program.info.images = {sampled_image, sampled_image};
|
||||
mixed_sampler_program.info.images = {sampled_image, sampled_image,
|
||||
sampled_image};
|
||||
mixed_sampler_program.info.samplers.push_back({0u, 4u});
|
||||
mixed_sampler_program.info.sampled_pairs = {{0u, 0u, 8u}, {1u, 0u, 12u}};
|
||||
mixed_sampler_program.info.sampled_pairs = {
|
||||
{0u, 0u, 8u}, {1u, 0u, 12u}, {2u, 0u, 16u}};
|
||||
ShaderRecompiler::IR::MemoryInfo signed_memory;
|
||||
signed_memory.kind = ShaderRecompiler::IR::ResourceKind::Image;
|
||||
signed_memory.resource = 2u;
|
||||
signed_memory.sampler = 0u;
|
||||
mixed_sampler_program.memory_info.push_back(signed_memory);
|
||||
mixed_sampler_program.block_storage.push_back(
|
||||
std::make_unique<ShaderRecompiler::IR::Block>());
|
||||
auto *mixed_sampler_block = mixed_sampler_program.block_storage.back().get();
|
||||
mixed_sampler_program.blocks.push_back(mixed_sampler_block);
|
||||
using ShaderRecompiler::IR::Value;
|
||||
using ShaderRecompiler::IR::ValueOpcode;
|
||||
auto &image_handle = mixed_sampler_block->AppendNewInst(
|
||||
ValueOpcode::GetImageResource,
|
||||
{Value(0u), Value(0u), Value(0u), Value(0u), Value(0u), Value(0u),
|
||||
Value(0u), Value(0u)});
|
||||
auto &sampler_handle = mixed_sampler_block->AppendNewInst(
|
||||
ValueOpcode::GetSamplerResource,
|
||||
{Value(0u), Value(0u), Value(0u), Value(0u)});
|
||||
auto &image_address = mixed_sampler_block->AppendNewInst(
|
||||
ValueOpcode::MakeImageAddress,
|
||||
{Value(0u), Value(0u), Value(0u), Value(0u), Value(0u), Value(0u),
|
||||
Value(0u), Value(0u), Value(0u), Value(0u), Value(0u), Value(0u),
|
||||
Value(0u)});
|
||||
const ShaderRecompiler::IR::MemoryFlags signed_memory_flags{0u, 16u};
|
||||
uint64_t signed_memory_flag_bits = 0;
|
||||
std::memcpy(&signed_memory_flag_bits, &signed_memory_flags,
|
||||
sizeof(signed_memory_flags));
|
||||
mixed_sampler_block->AppendNewInst(
|
||||
ValueOpcode::ImageSampleRaw,
|
||||
{Value(&image_handle), Value(&sampler_handle), Value(&image_address)},
|
||||
signed_memory_flag_bits);
|
||||
|
||||
ShaderRecompiler::IR::DescriptorValue native_image_descriptor{};
|
||||
native_image_descriptor.dword_count = 8;
|
||||
native_image_descriptor.dwords[0] = 0x1000u;
|
||||
native_image_descriptor.dwords[1] =
|
||||
static_cast<uint32_t>(Prospero::BufferFormat::k32UInt) << 20u;
|
||||
static_cast<uint32_t>(Prospero::BufferFormat::k32Float) << 20u;
|
||||
native_image_descriptor.dwords[2] = 3u | (3u << 14u);
|
||||
native_image_descriptor.dwords[3] =
|
||||
DstSel(4, 5, 6, 7) |
|
||||
|
|
@ -23628,10 +23698,15 @@ void CheckImageSamplerSpecialization() {
|
|||
packed_image_descriptor.dwords[0] = 0x2000u;
|
||||
packed_image_descriptor.dwords[1] =
|
||||
static_cast<uint32_t>(Prospero::BufferFormat::k11_11_10UInt) << 20u;
|
||||
auto signed_image_descriptor = native_image_descriptor;
|
||||
signed_image_descriptor.dwords[0] = 0x3000u;
|
||||
signed_image_descriptor.dwords[1] =
|
||||
static_cast<uint32_t>(Prospero::BufferFormat::k32SInt) << 20u;
|
||||
|
||||
ShaderRecompiler::IR::ResourceSnapshot mixed_sampler_snapshot;
|
||||
mixed_sampler_snapshot.images = {native_image_descriptor,
|
||||
packed_image_descriptor};
|
||||
packed_image_descriptor,
|
||||
signed_image_descriptor};
|
||||
ShaderRecompiler::IR::DescriptorValue sampler_descriptor{};
|
||||
sampler_descriptor.dword_count = 4;
|
||||
mixed_sampler_snapshot.samplers.push_back(sampler_descriptor);
|
||||
|
|
@ -23643,9 +23718,11 @@ void CheckImageSamplerSpecialization() {
|
|||
mixed_sampler_program.info.samplers[1].force_point_filtering &&
|
||||
mixed_sampler_program.info.sampled_pairs[0].sampler == 0u &&
|
||||
mixed_sampler_program.info.sampled_pairs[1].sampler == 1u &&
|
||||
mixed_sampler_program.info.sampled_pairs[2].sampler == 1u &&
|
||||
mixed_sampler_program.memory_info[0].sampler == 1u &&
|
||||
mixed_sampler_snapshot.samplers.size() == 2u,
|
||||
"a shared native/bit-packed sampler was not split into point and "
|
||||
"native variants");
|
||||
"a shared float/integer sampler was not split into point and native "
|
||||
"variants or the signed instruction retained the native sampler");
|
||||
|
||||
std::printf("[host] %-32s ok\n", "ImageSpecializationPipelineId");
|
||||
}
|
||||
|
|
@ -25699,7 +25776,6 @@ int main(int argc, char **argv) {
|
|||
CheckBasicStorageTextureDescriptor();
|
||||
CheckStorageTextureLinearUploadLayout();
|
||||
CheckStorageTextureDepthTileUploadLayout();
|
||||
CheckImageSamplerSpecialization();
|
||||
CheckStandard64RenderTargetTileRoundTrip();
|
||||
CheckStorageTextureVolumeUploadLayout();
|
||||
CheckStorageTextureVolumeMipRegions();
|
||||
|
|
@ -25720,6 +25796,7 @@ int main(int argc, char **argv) {
|
|||
CheckShaderRecompilerFatalContracts();
|
||||
VulkanHarness vulkan;
|
||||
#endif
|
||||
CheckImageSamplerSpecialization();
|
||||
CheckClipControlDepthClipState();
|
||||
CheckReferenceClockScale();
|
||||
CheckVulkan13FeatureRequirements();
|
||||
|
|
|
|||
Loading…
Reference in New Issue