Shader: implement RDNA2 image atomic float maximum

Shader: implement RDNA2 image atomic float minimum

Co-authored-by: foufouadi <attar.foued@gmail.com>
Co-authored-by: frans <francois.att@gmail.com>
Co-authored-by: fxpw <fxpw@outlook.com>
Co-authored-by: Matthew Hurley <171001439+theantipopau@users.noreply.github.com>
This commit is contained in:
nmzik
2026-09-26 14:04:22 +04:00
parent fd2e15ee73
commit cf268af839
11 changed files with 161 additions and 6 deletions
@@ -335,8 +335,10 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
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 raw_float_atomic = format == Prospero::BufferFormat::k32Float && uint_resource &&
resource.atomic;
const bool format_ok =
raw_sint_storage ||
raw_sint_storage || raw_float_atomic ||
(numeric_class != Prospero::TextureNumericClass::Unsupported &&
numeric_class != Prospero::TextureNumericClass::Sint &&
uint_resource == (numeric_class == Prospero::TextureNumericClass::Uint) &&
@@ -640,7 +642,8 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
pixel_format = depth_format->depth_attachment_format;
}
}
const auto storage_view_format = storage && format == Prospero::BufferFormat::k32SInt
const auto storage_view_format = storage && (resource.atomic ||
format == Prospero::BufferFormat::k32SInt)
? vk::Format::eR32Uint
: SrgbStorageViewFormat(pixel_format);
const auto view_format = storage && storage_view_format != vk::Format::eUndefined
@@ -877,7 +877,7 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
return;
}
const auto atomic_opcode = ImageAtomicOpcode(op);
if (atomic_opcode != spv::OpNop) {
if (image_info.access == IR::ImageAccess::Atomic) {
const auto dimension = image.dimension;
ctx.Define(inst, EmitValueOrZeroIfCondition(state, ctx.Arg(inst, 3), [&]() {
const auto pointer = state.builder.AllocateId();
@@ -887,6 +887,15 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
StorageImageDescriptorPointer(state, mem.resource),
CoordU32(ctx, mem, *address, dimension),
ConstantU32(state, 0));
if (op == IR::ValueOpcode::ImageAtomicFMin32 ||
op == IR::ValueOpcode::ImageAtomicFMax32) {
return AtomicUpdate(state, pointer, IR::ResourceKind::Image,
[&](uint32_t old) {
return EmitFloatAtomicReplacement(
state, old, ctx.Arg(inst, 2),
op == IR::ValueOpcode::ImageAtomicFMax32);
});
}
const auto old = state.builder.AllocateId();
state.builder.AddFunction(atomic_opcode, TypeU32(state), old, pointer,
ConstantU32(state, spv::ScopeDevice),
@@ -271,6 +271,8 @@ inline constexpr auto EmitImageAtomicUMax32 = EmitImage;
inline constexpr auto EmitImageAtomicAnd32 = EmitImage;
inline constexpr auto EmitImageAtomicOr32 = EmitImage;
inline constexpr auto EmitImageAtomicXor32 = EmitImage;
inline constexpr auto EmitImageAtomicFMin32 = EmitImage;
inline constexpr auto EmitImageAtomicFMax32 = EmitImage;
void EmitUnreachable(ValueEmitContext& ctx, const IR::Inst& inst);
inline constexpr auto EmitPhi = EmitUnreachable;
inline constexpr auto EmitTessellationBase = EmitUnreachable;
@@ -198,6 +198,8 @@ constexpr Detail::OpcodeMap MIMG_ATOMIC_OPCODE_LIST[] = {
{0x18u, Opcode::IMAGE_ATOMIC_AND},
{0x19u, Opcode::IMAGE_ATOMIC_OR},
{0x1au, Opcode::IMAGE_ATOMIC_XOR},
{0x1eu, Opcode::IMAGE_ATOMIC_FMIN},
{0x1fu, Opcode::IMAGE_ATOMIC_FMAX},
};
constexpr auto MIMG_SAMPLE_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_SAMPLE_OPCODE_LIST);
@@ -579,6 +579,8 @@ std::string InstructionToString(const Instruction& inst) {
case Opcode::IMAGE_ATOMIC_AND:
case Opcode::IMAGE_ATOMIC_OR:
case Opcode::IMAGE_ATOMIC_XOR:
case Opcode::IMAGE_ATOMIC_FMIN:
case Opcode::IMAGE_ATOMIC_FMAX:
case Opcode::IMAGE_LOAD:
case Opcode::IMAGE_LOAD_MIP:
case Opcode::IMAGE_GET_RESINFO:
@@ -571,6 +571,8 @@ enum class Opcode {
IMAGE_ATOMIC_AND,
IMAGE_ATOMIC_OR,
IMAGE_ATOMIC_XOR,
IMAGE_ATOMIC_FMIN,
IMAGE_ATOMIC_FMAX,
IMAGE_SAMPLE,
IMAGE_GATHER4_L,
IMAGE_GATHER4_LZ,
@@ -223,7 +223,7 @@ Decoder::Operand MemorySourceAt(const Decoder::Instruction& decoded, uint32_t in
const bool store_or_atomic = decoded.opcode == Decoder::Opcode::IMAGE_STORE ||
decoded.opcode == Decoder::Opcode::IMAGE_STORE_MIP ||
(decoded.opcode >= Decoder::Opcode::IMAGE_ATOMIC_SWAP &&
decoded.opcode <= Decoder::Opcode::IMAGE_ATOMIC_XOR);
decoded.opcode <= Decoder::Opcode::IMAGE_ATOMIC_FMAX);
if (store_or_atomic) {
return index == 0u ? decoded.dst : decoded.src0;
}
@@ -1026,6 +1026,10 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) {
return IMAGE_ATOMIC(inst, IR::ValueOpcode::ImageAtomicOr32);
case Decoder::Opcode::IMAGE_ATOMIC_XOR:
return IMAGE_ATOMIC(inst, IR::ValueOpcode::ImageAtomicXor32);
case Decoder::Opcode::IMAGE_ATOMIC_FMIN:
return IMAGE_ATOMIC(inst, IR::ValueOpcode::ImageAtomicFMin32);
case Decoder::Opcode::IMAGE_ATOMIC_FMAX:
return IMAGE_ATOMIC(inst, IR::ValueOpcode::ImageAtomicFMax32);
case Decoder::Opcode::FLAT_LOAD_UBYTE:
case Decoder::Opcode::FLAT_LOAD_SBYTE:
@@ -220,6 +220,8 @@ ImageOpcodeInfo ImageOpcodeInfoOf(ValueOpcode opcode) {
case ValueOpcode::ImageAtomicAnd32:
case ValueOpcode::ImageAtomicOr32:
case ValueOpcode::ImageAtomicXor32:
case ValueOpcode::ImageAtomicFMin32:
case ValueOpcode::ImageAtomicFMax32:
return {ImageAccess::Atomic, ImageResourceClass::Storage, false};
default: return {};
}
@@ -284,6 +284,8 @@ VALUE_OPCODE(ImageAtomicUMax32, U32, ImageResource, ImageAddress, U32, U1)
VALUE_OPCODE(ImageAtomicAnd32, U32, ImageResource, ImageAddress, U32, U1)
VALUE_OPCODE(ImageAtomicOr32, U32, ImageResource, ImageAddress, U32, U1)
VALUE_OPCODE(ImageAtomicXor32, U32, ImageResource, ImageAddress, U32, U1)
VALUE_OPCODE(ImageAtomicFMin32, U32, ImageResource, ImageAddress, U32, U1)
VALUE_OPCODE(ImageAtomicFMax32, U32, ImageResource, ImageAddress, U32, U1)
VALUE_OPCODE(GetAttribute, U32, U32, U32)
VALUE_OPCODE(GetInterpolationParameter, U32, U32, U32, U32)
VALUE_OPCODE(SetAttribute, Void, U32x4, U1)
@@ -485,7 +485,8 @@ static bool BuildResourceSpecialization(const ResourcePlan& program, ResourceSna
image.cube = DescriptorIsCube(descriptor);
const auto format =
static_cast<Prospero::BufferFormat>((descriptor.dwords[1] >> 20u) & 0x1ffu);
if (base.atomic && format != Prospero::BufferFormat::k32UInt) {
if (base.atomic && format != Prospero::BufferFormat::k32UInt &&
format != Prospero::BufferFormat::k32Float) {
return SpecializationFail(
fmt::format("atomic image descriptor {} uses unsupported format {}", i,
static_cast<uint32_t>(format)));
@@ -514,7 +515,7 @@ static bool BuildResourceSpecialization(const ResourcePlan& program, ResourceSna
fmt::format("storage image descriptor {} uses unsupported format {}", i,
static_cast<uint32_t>(format)));
}
if (raw_sint_storage) {
if (raw_sint_storage || base.atomic) {
image.numeric_class = Prospero::TextureNumericClass::Uint;
}
} else if (image.numeric_class == Prospero::TextureNumericClass::Unsupported ||