nmzik
2026-09-17 08:39:57 +04:00
parent fa913ec1a7
commit e0ecffed70
6 changed files with 47 additions and 3 deletions
@@ -3,7 +3,9 @@
#include "graphics/shader/recompiler/frontend/decode/ImageOps.h" #include "graphics/shader/recompiler/frontend/decode/ImageOps.h"
#include <algorithm> #include <algorithm>
#include <atomic>
#include <bit> #include <bit>
#include <cstdio>
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter { namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
namespace { namespace {
@@ -646,6 +648,13 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
const auto coord = const auto coord =
CoordF32(ctx, mem, *address, layout.coord, dimension_info.coordinate_components); CoordF32(ctx, mem, *address, layout.coord, dimension_info.coordinate_components);
if (op == IR::ValueOpcode::ImageGatherRaw) { if (op == IR::ValueOpcode::ImageGatherRaw) {
if (HasFlag(mem, Decoder::ImageSampleFlagLod)) {
static std::atomic_flag warned = ATOMIC_FLAG_INIT;
if (!warned.test_and_set(std::memory_order_relaxed)) {
std::fputs("Warning: approximating IMAGE_GATHER4_L at mip level 0; explicit LOD is ignored.\n",
stderr);
}
}
if (dimension == ImageDimension::Dim1D) { if (dimension == ImageDimension::Dim1D) {
if (dref || !HasFlag(mem, Decoder::ImageSampleFlagLevelZero) || if (dref || !HasFlag(mem, Decoder::ImageSampleFlagLevelZero) ||
HasFlag(mem, Decoder::ImageSampleFlagOffset) || HasFlag(mem, Decoder::ImageSampleFlagOffset) ||
@@ -175,6 +175,7 @@ constexpr MimgSampleInfo MIMG_SAMPLE_OPCODE_LIST[] = {
}; };
constexpr MimgGatherInfo MIMG_GATHER_OPCODE_LIST[] = { constexpr MimgGatherInfo MIMG_GATHER_OPCODE_LIST[] = {
{0x44u, Opcode::IMAGE_GATHER4_L, ImageSampleFlagLod},
{0x47u, Opcode::IMAGE_GATHER4_LZ, ImageSampleFlagLevelZero}, {0x47u, Opcode::IMAGE_GATHER4_LZ, ImageSampleFlagLevelZero},
{0x48u, Opcode::IMAGE_GATHER4_C, ImageSampleFlagCompare}, {0x48u, Opcode::IMAGE_GATHER4_C, ImageSampleFlagCompare},
{0x4fu, Opcode::IMAGE_GATHER4_C_LZ, {0x4fu, Opcode::IMAGE_GATHER4_C_LZ,
@@ -144,6 +144,7 @@ std::string FormatMimg(const Instruction& inst) {
} }
switch (inst.opcode) { switch (inst.opcode) {
case Opcode::IMAGE_SAMPLE: case Opcode::IMAGE_SAMPLE:
case Opcode::IMAGE_GATHER4_L:
case Opcode::IMAGE_GATHER4_LZ: case Opcode::IMAGE_GATHER4_LZ:
case Opcode::IMAGE_GATHER4_C: case Opcode::IMAGE_GATHER4_C:
case Opcode::IMAGE_GATHER4_C_LZ: case Opcode::IMAGE_GATHER4_C_LZ:
@@ -580,6 +581,7 @@ std::string InstructionToString(const Instruction& inst) {
case Opcode::IMAGE_LOAD_MIP: case Opcode::IMAGE_LOAD_MIP:
case Opcode::IMAGE_GET_RESINFO: case Opcode::IMAGE_GET_RESINFO:
case Opcode::IMAGE_GET_LOD: case Opcode::IMAGE_GET_LOD:
case Opcode::IMAGE_GATHER4_L:
case Opcode::IMAGE_GATHER4_LZ: case Opcode::IMAGE_GATHER4_LZ:
case Opcode::IMAGE_GATHER4_C: case Opcode::IMAGE_GATHER4_C:
case Opcode::IMAGE_GATHER4_C_LZ: case Opcode::IMAGE_GATHER4_C_LZ:
@@ -559,6 +559,7 @@ enum class Opcode {
IMAGE_ATOMIC_OR, IMAGE_ATOMIC_OR,
IMAGE_ATOMIC_XOR, IMAGE_ATOMIC_XOR,
IMAGE_SAMPLE, IMAGE_SAMPLE,
IMAGE_GATHER4_L,
IMAGE_GATHER4_LZ, IMAGE_GATHER4_LZ,
IMAGE_GATHER4_C, IMAGE_GATHER4_C,
IMAGE_GATHER4_C_LZ, IMAGE_GATHER4_C_LZ,
@@ -1034,6 +1034,7 @@ bool Translator::EmitMemory(const Decoder::Instruction& inst) {
case Decoder::Opcode::IMAGE_STORE: case Decoder::Opcode::IMAGE_STORE:
case Decoder::Opcode::IMAGE_STORE_MIP: return IMAGE_STORE(inst); case Decoder::Opcode::IMAGE_STORE_MIP: return IMAGE_STORE(inst);
case Decoder::Opcode::IMAGE_SAMPLE: return IMAGE_SAMPLE(inst); case Decoder::Opcode::IMAGE_SAMPLE: return IMAGE_SAMPLE(inst);
case Decoder::Opcode::IMAGE_GATHER4_L:
case Decoder::Opcode::IMAGE_GATHER4_LZ: case Decoder::Opcode::IMAGE_GATHER4_LZ:
case Decoder::Opcode::IMAGE_GATHER4_C: case Decoder::Opcode::IMAGE_GATHER4_C:
case Decoder::Opcode::IMAGE_GATHER4_C_LZ: case Decoder::Opcode::IMAGE_GATHER4_C_LZ:
+33 -3
View File
@@ -12271,7 +12271,7 @@ public:
return ret; return ret;
} }
vk::Sampler CreateNearestSampler(const char *shader_name) { vk::Sampler CreateNearestSampler(const char *shader_name, u32 mip_levels = 1) {
vk::SamplerCreateInfo sampler_info{}; vk::SamplerCreateInfo sampler_info{};
sampler_info.sType = vk::StructureType::eSamplerCreateInfo; sampler_info.sType = vk::StructureType::eSamplerCreateInfo;
sampler_info.magFilter = vk::Filter::eNearest; sampler_info.magFilter = vk::Filter::eNearest;
@@ -12281,7 +12281,7 @@ public:
sampler_info.addressModeV = vk::SamplerAddressMode::eClampToEdge; sampler_info.addressModeV = vk::SamplerAddressMode::eClampToEdge;
sampler_info.addressModeW = vk::SamplerAddressMode::eClampToEdge; sampler_info.addressModeW = vk::SamplerAddressMode::eClampToEdge;
sampler_info.minLod = 0.0f; sampler_info.minLod = 0.0f;
sampler_info.maxLod = 0.0f; sampler_info.maxLod = static_cast<float>(mip_levels - 1u);
vk::Sampler sampler = nullptr; vk::Sampler sampler = nullptr;
RequireVk(shader_name, "dispatch", RequireVk(shader_name, "dispatch",
m_device.createSampler(&sampler_info, nullptr, &sampler), m_device.createSampler(&sampler_info, nullptr, &sampler),
@@ -15484,7 +15484,7 @@ void RunCase(VulkanHarness *vulkan, const TestCase &test) {
vk::ImageLayout::eGeneral); vk::ImageLayout::eGeneral);
} }
if (needs_sampler) { if (needs_sampler) {
sampler = vulkan->CreateNearestSampler(test.name); sampler = vulkan->CreateNearestSampler(test.name, sampled_image.mip_levels);
} }
vulkan->Dispatch(test, compiled, buffer, needs_gds ? &gds_buffer : nullptr, vulkan->Dispatch(test, compiled, buffer, needs_gds ? &gds_buffer : nullptr,
@@ -25001,6 +25001,34 @@ TestCase ImageSampleAndGather() {
return test; return test;
} }
TestCase ImageGatherLodApproximatesLevelZero() {
using O = ShaderOpcode;
std::vector<u32> code;
AppendVMovLiteral(&code, 20, 0x3f000000u);
AppendVMovLiteral(&code, 21, 0x3f000000u);
AppendVMovLiteral(&code, 22, 0x3f800000u);
code.push_back(EncodeMimg0(0x44, 0x1));
code.push_back(EncodeMimg1(0, 20));
code.push_back(EncodeMimg0(0x24, 0x1));
code.push_back(EncodeMimg1(4, 20));
for (u32 i = 0; i < 5u; i++) {
AppendStoreVgpr(&code, i, i);
}
AppendEnd(&code);
TestCase test;
test.name = "ImageGatherLodApproximatesLevelZero";
test.code = std::move(code);
test.opcodes = {O::V_MOV_B32, O::IMAGE_GATHER4_L, O::IMAGE_SAMPLE,
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
// Explicit sampling proves mip 1 is accessible; the gather must still read mip 0.
test.expected = {0x3f800000u, 0x3f800000u, 0x3f800000u, 0x3f800000u, 0x41000000u};
test.sampled_image_rgba_mips = {std::vector<u32>(4 * 4 * 4, 0x3f800000u),
std::vector<u32>(2 * 2 * 4, 0x41000000u)};
test.required_spirv = {"OpImageGather", "OpImageSampleExplicitLod"};
return test;
}
TestCase ImageD16GatherPacksHalfPairs() { TestCase ImageD16GatherPacksHalfPairs() {
using O = ShaderOpcode; using O = ShaderOpcode;
@@ -26575,6 +26603,7 @@ std::vector<TestCase> MakeCases() {
AddCase(ImageGetResinfoDmaskWidthHeight); AddCase(ImageGetResinfoDmaskWidthHeight);
AddCase(ImageGetResinfoDmaskMipLevels); AddCase(ImageGetResinfoDmaskMipLevels);
AddCase(ImageSampleAndGather); AddCase(ImageSampleAndGather);
AddCase(ImageGatherLodApproximatesLevelZero);
AddCase(ImageD16GatherPacksHalfPairs); AddCase(ImageD16GatherPacksHalfPairs);
AddCase(ImageSampleA16SamplerCoordsOnGpu); AddCase(ImageSampleA16SamplerCoordsOnGpu);
AddCase(ImageSampleOpcodeAliasUsesNormalCoords); AddCase(ImageSampleOpcodeAliasUsesNormalCoords);
@@ -31166,6 +31195,7 @@ int main(int argc, char **argv) {
RunCase(&vulkan, BranchVccnzUsesWaveMask()); RunCase(&vulkan, BranchVccnzUsesWaveMask());
RunCase(&vulkan, BranchVccnzUsesCarryProducedWaveMask()); RunCase(&vulkan, BranchVccnzUsesCarryProducedWaveMask());
RunCase(&vulkan, ImageSampleAndGather()); RunCase(&vulkan, ImageSampleAndGather());
RunCase(&vulkan, ImageGatherLodApproximatesLevelZero());
RunCase(&vulkan, SharedReturnKeepsSelectedValues()); RunCase(&vulkan, SharedReturnKeepsSelectedValues());
return 0; return 0;
} }