shader: decode comparison gathers with explicit LOD

Co-authored-by: fxpw <fxpw@outlook.com>
This commit is contained in:
nmzik
2026-10-14 21:40:12 +02:00
parent 8b81f665a2
commit bc2664cefa
6 changed files with 132 additions and 1 deletions
@@ -185,6 +185,7 @@ constexpr MimgGatherInfo MIMG_GATHER_OPCODE_LIST[] = {
{0x44u, Opcode::IMAGE_GATHER4_L, ImageSampleFlagLod},
{0x47u, Opcode::IMAGE_GATHER4_LZ, ImageSampleFlagLevelZero},
{0x48u, Opcode::IMAGE_GATHER4_C, ImageSampleFlagCompare},
{0x4cu, Opcode::IMAGE_GATHER4_C_L, ImageSampleFlagCompare | ImageSampleFlagLod},
{0x4fu, Opcode::IMAGE_GATHER4_C_LZ,
ImageSampleFlagCompare | ImageSampleFlagLevelZero},
{0x57u, Opcode::IMAGE_GATHER4_LZ_O,
@@ -143,6 +143,7 @@ std::string FormatMimg(const Instruction& inst) {
case Opcode::IMAGE_GATHER4_L:
case Opcode::IMAGE_GATHER4_LZ:
case Opcode::IMAGE_GATHER4_C:
case Opcode::IMAGE_GATHER4_C_L:
case Opcode::IMAGE_GATHER4_C_LZ:
case Opcode::IMAGE_GATHER4_LZ_O:
case Opcode::IMAGE_GATHER4_C_O:
@@ -611,6 +612,7 @@ std::string InstructionToString(const Instruction& inst) {
case Opcode::IMAGE_GATHER4_L:
case Opcode::IMAGE_GATHER4_LZ:
case Opcode::IMAGE_GATHER4_C:
case Opcode::IMAGE_GATHER4_C_L:
case Opcode::IMAGE_GATHER4_C_LZ:
case Opcode::IMAGE_GATHER4_LZ_O:
case Opcode::IMAGE_GATHER4_C_O:
@@ -699,6 +699,7 @@ enum class Opcode {
IMAGE_GATHER4_L,
IMAGE_GATHER4_LZ,
IMAGE_GATHER4_C,
IMAGE_GATHER4_C_L,
IMAGE_GATHER4_C_LZ,
IMAGE_GATHER4_LZ_O,
IMAGE_GATHER4_C_O,
@@ -1162,6 +1162,7 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) {
case Decoder::Opcode::IMAGE_GATHER4_L:
case Decoder::Opcode::IMAGE_GATHER4_LZ:
case Decoder::Opcode::IMAGE_GATHER4_C:
case Decoder::Opcode::IMAGE_GATHER4_C_L:
case Decoder::Opcode::IMAGE_GATHER4_C_LZ:
case Decoder::Opcode::IMAGE_GATHER4_LZ_O:
case Decoder::Opcode::IMAGE_GATHER4_C_O:
+100 -1
View File
@@ -12569,6 +12569,103 @@ public:
check_layout(false, DstSel(4, 4, 4, 4));
check_layout(false, DstSel(4, 4, 4, 1));
check_layout(true, DstSel(4, 0, 0, 1));
for (const u32 wave : {32u, 64u}) {
TestCase test;
test.name = wave == 32u ? "ImageGatherCompareLod32" : "ImageGatherCompareLod64";
test.has_user_data = test.has_compute_info = true;
test.compute_info.wave_size = wave;
test.compute_info.threads_num[0] = wave;
test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1u;
test.compute_info.thread_ids_num = 1u;
test.compute_info.lds_size_dwords = 0;
ShaderTextureResource descriptor{{static_cast<u32>(base >> 8u),
(u32(Prospero::BufferFormat::k32Float) << 20u) | (3u << 30u), 3u << 14u,
(u32(Prospero::ImageType::kColor2D) << 28u) |
(u32(Prospero::TileMode::kStandard4KB) << 20u) | (1u << 16u) | DstSel(4, 4, 4, 4),
0, 0x00700000u | (1u << 4u), 0, 0}};
ShaderSamplerResource sampler_descriptor{{0x92u | (1u << 12u), 0xfff000u, 1u << 26u, 0}};
std::copy_n(descriptor.fields, 8u, test.user_data.begin() + 12u);
std::copy_n(sampler_descriptor.fields, 4u, test.user_data.begin() + 20u);
test.code.push_back(EncodeVop1(0x01, 40, Vgpr(0)));
AppendVMovLiteral(&test.code, 25, std::bit_cast<u32>(0.5f));
AppendVMovLiteral(&test.code, 26, std::bit_cast<u32>(0.5f));
constexpr std::array mip_depths{0.1f, 0.9f, 0.7f, 0.3f};
constexpr std::array gather_order{2u, 3u, 1u, 0u};
for (u32 mip = 0; mip < 2u; ++mip) {
AppendVMovLiteral(&test.code, 22, std::bit_cast<u32>(float(mip)));
for (const float reference : {0.2f, 0.5f, 0.8f}) {
AppendVMovLiteral(&test.code, 19, std::bit_cast<u32>(reference));
// Exact captured NSA order: reference v19, coordinates v25/v26, LOD v22.
test.code.insert(test.code.end(), {0xf130010au, 0x00a30013u, 0x00161a19u});
for (u32 component = 0; component < 4u; ++component) {
AppendStoreVgprAtLaneDwordOffset(&test.code, component, 40, test.expected.size());
const float depth = mip == 0u ? 0.6f : mip_depths[gather_order[component]];
test.expected.insert(test.expected.end(), wave, reference < depth ? 0x3f800000u : 0u);
}
}
}
AppendEnd(&test.code);
test.user_data[50] = test.expected.size() * sizeof(u32);
test.user_data[51] = 3u << 28u;
test.initial.assign(test.expected.size(), 0xdeadbeefu);
test.expected_mip_descriptors = 2u;
test.required_spirv = {"OpImageDrefGather", "OpSwitch"};
test.decoded_counts = {{"IMAGE_GATHER4_C_L", 6u}};
const auto compiled = CompileCase(test, SubgroupSize());
Require(test.name, "comparison mip resource", compiled.program.info.images.size() == 1u &&
compiled.program.info.images[0].depth_compare,
"explicit comparison gather lost its depth resource");
std::memset(mapped, 0, allocation_size);
RenderContext context(m_runtime_context);
auto &scheduler = context.GetCommandScheduler();
HW::Context registers{};
HW::UserConfig user_config{};
HW::Shader shaders{};
scheduler.Begin(registers, user_config, shaders);
context.MapMemory(base, allocation_size);
auto &executor = context.GetRenderExecutor();
auto binding = RenderExecutorTestAccess::ResolveTexture(
executor, compiled.program.info.images[0], compiled.resources.images[0]);
auto &cache = context.GetTextureCache();
Image sampled;
sampled.view = cache.FindTexture(binding.image_id, binding.desc);
auto &image = cache.GetImage(binding.image_id);
Require(test.name, "native depth mip backing",
image.info.pixel_format == vk::Format::eD32Sfloat && image.backing.mip_levels == 2u,
"comparison gather did not allocate both depth mips");
std::vector<u32> pixels(16u, std::bit_cast<u32>(0.6f));
for (const auto depth : mip_depths) pixels.push_back(std::bit_cast<u32>(depth));
auto staging = CreateHostBuffer(test.name, pixels.size() * sizeof(u32),
vk::BufferUsageFlagBits::eTransferSrc, pixels);
image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
{}, scheduler.Current().Handle());
std::array<vk::BufferImageCopy, 2> copies{};
for (u32 mip = 0; mip < 2u; ++mip) {
copies[mip].bufferOffset = mip == 0u ? 0u : 16u * sizeof(u32);
copies[mip].imageSubresource = {vk::ImageAspectFlagBits::eDepth, mip, 0, 1};
copies[mip].imageExtent = {4u >> mip, 4u >> mip, 1u};
}
scheduler.Current().Handle().copyBufferToImage(staging.buffer, image.backing.image,
vk::ImageLayout::eTransferDstOptimal, copies.size(), copies.data());
image.Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead,
{}, scheduler.Current().Handle());
scheduler.Finish();
DestroyBuffer(&staging);
sampled.image = image.backing.image;
sampled.format = image.info.pixel_format;
sampled.mip_levels = 2u;
sampled.layout = image.backing.state.layout;
const auto sampler = context.GetSamplerCache().GetSampler(sampler_descriptor, false);
auto output = CreateStorageBuffer(test.name, test.initial, test.initial.size());
Dispatch(test, compiled, output, nullptr, &sampled, nullptr, nullptr, sampler);
Require(test.name, "explicit LOD, comparison reference and gather component order",
ReadBuffer(test.name, output, test.expected.size()) == test.expected,
"comparison gather selected the wrong mip, reference or component order");
DestroyBuffer(&output);
RenderExecutorTestAccess::ResetBindings(executor);
context.UnmapMemory(base, allocation_size);
scheduler.Finish();
}
Require(name, "unmap", Libs::LibKernel::Memory::KernelMunmap(base, allocation_size) == 0,
"comparison texture unmap failed");
Require(name, "release", Libs::LibKernel::Memory::KernelReleaseDirectMemory(
@@ -16612,7 +16709,7 @@ public:
view.viewType = test.sampled_image_view_type;
view.format = source_image->format;
view.subresourceRange = {
vk::ImageAspectFlagBits::eColor, mip, 1,
image.depth_compare ? vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eColor, mip, 1,
test.sampled_image_view_base_layer,
test.sampled_image_view_layers != 0
? test.sampled_image_view_layers
@@ -20589,6 +20686,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode,
case Opcode::IMAGE_SAMPLE:
case Opcode::IMAGE_GATHER4_LZ:
case Opcode::IMAGE_GATHER4_C:
case Opcode::IMAGE_GATHER4_C_L:
case Opcode::IMAGE_GATHER4_C_LZ:
case Opcode::IMAGE_GATHER4_LZ_O:
case Opcode::IMAGE_GATHER4_C_O:
@@ -46858,6 +46956,7 @@ int main(int argc, char **argv) {
}
if (argc == 2 && std::strcmp(argv[1], "--image-gather-lod-only") == 0) {
VulkanHarness vulkan;
vulkan.CheckComparisonDepthTexture();
RunCase(&vulkan, ImageGatherExplicitLod());
RunCase(&vulkan, ImageGatherLodPerLane<0>());
RunCase(&vulkan, ImageGatherLodPerLane<1>());
+27
View File
@@ -5567,6 +5567,26 @@ void TestNewShaderRecompilerCubeSampleCoordinates() {
void TestImageAddressOperands() {
using namespace ShaderRecompiler;
// Captured at PC 0x10cc in compute shader 0041b6a03db7472d.
const uint32_t captured[] = {0xf130010au, 0x00a30013u, 0x00161a19u};
Decoder::Instruction gather;
Decoder::DecodeInstruction(captured, 0, gather);
Check(gather.opcode == Decoder::Opcode::IMAGE_GATHER4_C_L &&
gather.opcode_id == 0x4cu && gather.word_count == 3u &&
gather.image_dimension == Decoder::ImageDimension::Dim2D &&
gather.dmask == 1u && gather.data_components == 4u &&
gather.data_dwords == 4u && gather.data_bits == 32u &&
gather.image_sample_flags == (Decoder::ImageSampleFlagCompare |
Decoder::ImageSampleFlagLod) &&
gather.image_address_components == 4u && gather.image_nsa_dwords == 1u &&
gather.dst.reg == 0u && gather.src0.reg == 19u &&
gather.src1.reg == 12u && gather.src2.reg == 20u &&
gather.image_nsa_addr[0] == 25u && gather.image_nsa_addr[1] == 26u &&
gather.image_nsa_addr[2] == 22u && !gather.glc && !gather.slc &&
!gather.image_r128,
"captured IMAGE_GATHER4_C_L lost compare/LOD, NSA, or result metadata");
Check(Decoder::InstructionToString(gather).find("IMAGE_GATHER4_C_L") != std::string::npos,
"captured IMAGE_GATHER4_C_L is missing from the decoded dump");
struct Case {
uint32_t opcode;
uint32_t dimension;
@@ -5581,6 +5601,7 @@ void TestImageAddressOperands() {
Case{0x20, 1, true, false, 2, 1}, // packed xy
Case{0x3d, 1, true, false, 5, 4}, // offset, half bias, full dref, packed xy
Case{0x20, 2, true, true, 3, 2}, // packed xy/z in nonconsecutive VGPRs
Case{0x4c, 1, false, true, 4, 4}, // full dref, xy, and LOD in NSA VGPRs
Case{0x0e, 1, false, false, 1, 1}, // resource query mip
};
for (const auto &test : cases) {
@@ -5602,6 +5623,12 @@ void TestImageAddressOperands() {
options.stage = ShaderType::Compute;
options.input_info.compute = &compute;
auto program = Frontend::TranslateProgram(decoded, CFG::BuildGraph(decoded), options);
if (test.opcode == 0x4cu) {
Check(program.memory_info.size() == 1u &&
program.memory_info[0].image_sample_flags == gather.image_sample_flags &&
program.memory_info[0].data_dwords == 4u,
"IMAGE_GATHER4_C_L lost its shared compare/LOD metadata during translation");
}
IR::RewriteToSsa(program.blocks);
IR::ConstantPropagationPass(program.blocks);
uint32_t addresses = 0;