Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp"
#include "prx/libSceAgcDriver/Graphics/include/TextureTiling.hpp"
#include "Recompiler.hpp"
#include "VulkanTestDevice.hpp"
#include <algorithm>
#include <array>
#include <bit>
#include <cmath>
#include <cstring>
#include <iostream>
#include <span>
#include <vector>

namespace {
using AgcDriver::Graphics::Require;
constexpr std::uint32_t Threads = 64;
constexpr std::uint32_t Words = 8;
alignas(256) std::array<std::uint32_t, Threads * Words> buffer{};
alignas(256) std::array<std::byte, 65536> texture{};
constexpr std::array<std::uint32_t, 4> Coordinates{0u, 1u, 3u, 4u};
constexpr std::array<std::uint32_t, 4> Samples{0u, 1u, 7u, 0xffffffffu};
constexpr std::array<std::uint32_t, 9> UnormExpected{0x0u, 0x3b808081u, 0x3c40c0c1u, 0x3e808081u, 0x3e828283u, 0x3e868687u, 0x3f40c0c1u, 0x3f41c1c2u, 0x3f43c3c4u};
constexpr std::array<std::uint32_t, 9> SrgbExpected{0x0u, 0x399f0000u, 0x3a6f0000u, 0x3d520000u, 0x3d590000u, 0x3d660000u, 0x3f070000u, 0x3f090000u, 0x3f0c0000u};
alignas(256) constexpr std::array<std::uint32_t, 14> MsaaCode{
0x34020085u, 0xe0301000u, 0x80000201u, 0xe0301004u, 0x80000301u, 0xe0301008u, 0x80000401u,
0xbf8c3f70u, 0xf0000130u, 0x00010802u, 0xbf8c3f70u, 0xe0701010u, 0x80000801u, 0xbf810000u
};
alignas(256) constexpr auto PlaneCode = [] {
auto code = MsaaCode;
code[8] = 0xf0000108u;
return code;
}();

std::array<std::uint32_t, 8> TextureDescriptor(std::uint32_t format) {
const auto address = reinterpret_cast<std::uintptr_t>(texture.data());
return {static_cast<std::uint32_t>(address >> 8u), static_cast<std::uint32_t>((address >> 40u) & 0xffu) | (format << 20u) | 0xc0000000u, 0xc000u, 0x90000facu, 0u, 0u, 0u, 0u};
}

void FillTexture(const std::array<std::uint32_t, 8>& descriptor) {
const auto layout = AgcDriver::Graphics::DescribeSurface(AgcDriver::Graphics::DecodeTextureResource(descriptor));
Require(layout.guestBytes <= texture.size(), "MSAA-address texture exceeds its storage");
texture.fill(std::byte{0xee});
const auto& mip = layout.mips.at(0);
for (std::uint32_t y = 0; y < 4; ++y) {
for (std::uint32_t x = 0; x < 4; ++x) {
const auto value = 0x12340000u + y * 64u + x;
std::memcpy(texture.data() + mip.tiledOffset + y * mip.pitchBytes + x * 4u, &value, sizeof(value));
}
}
}

std::array<std::uint32_t, Threads> Load(AgcDriver::VulkanDevice& device, const std::array<std::uint32_t, 8>& image, std::span<const std::uint32_t> code, std::uint32_t wave) {
buffer.fill(0xdeadbeefu);
for (std::uint32_t lane = 0; lane < Threads; ++lane) {
buffer[lane * Words] = Coordinates[lane % 4u];
buffer[lane * Words + 1u] = Coordinates[(lane / 4u) % 4u];
buffer[lane * Words + 2u] = Samples[lane / 16u];
}
const auto address = reinterpret_cast<std::uintptr_t>(buffer.data());
std::vector<std::uint32_t> data{static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu), static_cast<std::uint32_t>(buffer.size() * 4u), 0x31016facu};
data.insert(data.end(), image.begin(), image.end());
const std::array<ShaderRecompiler::MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(code.data()), std::as_bytes(code)}}};
const ShaderRecompiler::ShaderComputeStageInfo compute{{Threads, 1, 1}, 0u, {false, false, false}, false, 1};
ShaderRecompiler::RecompileRequest request{
{ShaderRecompiler::ShaderStage::Compute, reinterpret_cast<std::uintptr_t>(code.data()), code, 0, {}},
{wave, 0, data, compute, std::nullopt, std::nullopt, memory}, device.Target(), {0, 0, 0, 128}
};
request.useCache = false;
const auto compiled = ShaderRecompiler::Recompile(request);
device.Dispatch(compiled, 1, 1, 1, {}, reinterpret_cast<std::uintptr_t>(code.data()));
device.WaitIdle();
std::array<std::uint32_t, Threads> result{};
for (std::uint32_t lane = 0; lane < Threads; ++lane) result[lane] = buffer[lane * Words + 4u];
return result;
}

void Run(AgcDriver::VulkanDevice& device) {
for (const auto format : {20u, 22u, 56u, 130u}) {
const auto descriptor = TextureDescriptor(format);
FillTexture(descriptor);
for (const auto wave : {32u, 64u}) {
const auto plain = Load(device, descriptor, PlaneCode, wave);
const auto msaa = Load(device, descriptor, MsaaCode, wave);
for (std::uint32_t lane = 0; lane < Threads; ++lane) {
Require(msaa[lane] == plain[lane], "MSAA-address load differs from the measured 2D control at lane " + std::to_string(lane));
const auto x = Coordinates[lane % 4u];
const auto y = Coordinates[(lane / 4u) % 4u];
if (x == 4u || y == 4u) Require(msaa[lane] == 0u, "MSAA-address out-of-range load did not read zero");
else if (format == 20u || format == 22u) Require(msaa[lane] == 0x12340000u + y * 64u + x, "MSAA-address load lost the oracle texel marker");
else {
const auto index = ((lane / 4u) % 4u) * 3u + lane % 4u;
const auto expected = std::bit_cast<float>((format == 56u ? UnormExpected : SrgbExpected)[index]);
const auto actual = std::bit_cast<float>(msaa[lane]);
if (format == 130u) {
const auto encoded = [](float linear) { return std::lround((linear <= 0.0031308f ? linear * 12.92f : 1.055f * std::pow(linear, 1.0f / 2.4f) - 0.055f) * 255.0f); };
Require(std::isfinite(actual) && encoded(actual) == encoded(expected), "MSAA-address sRGB load changed the oracle's encoded texel");
} else Require(std::abs(actual - expected) < 0.0001f, "MSAA-address normalized load differs from the Radeon oracle");
}
}
}
}
}
}

int main() {
try {
const auto device = OpenVulkanTestDevice();
if (!device) return VulkanTestSkipped;
Run(*device);
return 0;
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';
return 1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1542,6 +1542,11 @@ std::vector<ImageResource> ResourceMaterializer::RuntimeImageModes(const ImageRe
volume.dimension = RdnaImageDimension::Dim3D;
modes.push_back(volume);
}
if (image.dimension == RdnaImageDimension::Dim2DMsaa && image.fmaskCompatible && image.cubeFaceCompatible && !table && !storage && !depth && conversion == IrBufferFormat::Invalid && packed == IrBufferFormat::Invalid && image.byElements == 0u) {
auto plane = mode;
plane.dimension = RdnaImageDimension::Dim2D;
modes.push_back(plane);
}
if (image.dimension == RdnaImageDimension::Dim3D && image.flatVolumeCompatible && !storage && !depth && conversion == IrBufferFormat::Invalid && packed == IrBufferFormat::Invalid) {
for (const auto dimension : {RdnaImageDimension::Dim2D, RdnaImageDimension::Dim2DArray}) {
auto plane = mode;
Expand Down
2 changes: 2 additions & 0 deletions docs/dev/debt/shader.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
### Unknown function info

- [VCC-backed MIMG samplers](../../../core/shader/recompiler/RdnaDecoder/src/RdnaImageOpDecoder.cpp): the s104/s105/vcc_lo/vcc_hi tuple follows LLVM's [SGPR register classes](https://github.com/llvm/llvm-project/blob/d8219b47422f7322db252ad29ca8ea87993fb433/llvm/lib/Target/AMDGPU/SIRegisterInfo.td#L452) and [gfx10 sampler operand](https://github.com/llvm/llvm-project/blob/d8219b47422f7322db252ad29ca8ea87993fb433/llvm/lib/Target/AMDGPU/MIMGInstructions.td#L1379), using the existing VCC word reads. Recompiled wave32/wave64 reads are checked by [bilinear and white-border cases](../../../core/libs/prx/libSceAgcDriver/tests/execution/ImageSampleVccSampler.cpp); raw RDNA execution of this tuple on gfx1013 or gfx1036 is unmeasured.
- [MSAA-address image loads on 2D textures](../../../core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp) - 32-bit `image_load` with DIM 2D_MSAA reads a non-multisampled 2D T# as the same DIM 2D load, ignoring the sample index. Measured on RX 6650 XT (gfx1032), wave32 and wave64, for R32_UINT, R32_FLOAT, RGBA8_UNORM and RGBA8_SRGB, sample indices 0/1/2/7/31/0xffffffff and in-range/out-of-range coordinates; IEEE 0, DX10 clamp 1, denorm32 0, denorm16 3, round32/16 0, fp16 overflow 0. Pinned in ImageMsaaAddressOnPlane.cpp. Not measured on gfx1013. Stores, atomics, mip-bearing or A16 addresses, packed accesses, converted formats, depth-bit views, array-MSAA addresses and image-table accesses are not enabled by this compatibility mode.

- [MUBUF/MTBUF descriptors ending in VCC](../../../core/shader/recompiler/RdnaDecoder/src/RdnaMemoryOpDecoder.cpp): the four dword descriptor at s104 is s104, s105, vcc_lo and vcc_hi, as LLVM's [VCCTuple/SReg_128](https://github.com/llvm/llvm-project/blob/d8219b47422f7322db252ad29ca8ea87993fb433/llvm/lib/Target/AMDGPU/SIRegisterInfo.td#L422) and [buffer resource operands](https://github.com/llvm/llvm-project/blob/d8219b47422f7322db252ad29ca8ea87993fb433/llvm/lib/Target/AMDGPU/BUFInstructions.td) describe. Existing translation maps scalar codes106/107 to the raw VCC words. Recompiled loads/stores, both words, bounds, formats and wave32/wave64 execute on NVIDIA and AMD Vulkan; raw gfx10/gfx1013 hardware is not measured. Higher descriptor bases remain refused; the normal SGPR bound is unchanged.
- [Packed pixel ancillary word](../../../core/shader/recompiler/Optimization/src/ConstantFolder.cpp) (shader recompiler) - the sample index at bits 8-11 and the render target index at bits 16-28 follow #295. The sample index is the SampleId builtin, an equality inferred, not measured. The render target index is the Layer builtin when the draw exports it (a layered draw, `PA_CL_VS_OUT_CNTL` bit 18; the pipeline specialization constant `LayerExported`) and 0 otherwise, the first layer Vulkan renders when no pre-rasterization stage writes Layer. Reading the unwritten Layer builtin instead makes AMD's Windows driver drop the fragment's writes (#4800); with the constant at 0 the load is folded away. Mesa reads both fields from those bits, with the layer 14 bits wide only from GFX12 on ([ac_nir_lower_intrinsics_to_args.c](https://gitlab.freedesktop.org/mesa/mesa/-/blob/bffd91947efc1932da99aca69fc540d6d4201b0c/src/amd/common/nir/ac_nir_lower_intrinsics_to_args.c#L342-L368)). A value passed through a select, move or phi whose every read is an extract inside one of those fields is rebuilt from the two fields alone. Any other read the masked-select pass cannot drop gets the whole word: the primitive class at bits 0-1 (0 point, 1 line, 2 triangle, 3 rectangle), which the draw sets as a pipeline specialization constant ([RasterPrimitiveOf](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp)) in the shaders that read it, plus the sample field, with every other bit zero. That layout comes from the RDNA 3 and 3.5 ISA guides (prim type 1:0, VRS rates 3:2 and 5:4, eye id 7, sample 11:8, render target index 28:16). The GCN register guides give the same prim type and sample fields and put the render target index at 26:16, so the two sources agree for layers below 2048. AMD's LLPC reads the primitive class from bits 1:0 with the same encoding ([LowerGlCompatibility.cpp](https://github.com/GPUOpen-Drivers/llpc/blob/40cb8d95ad8d6f7f1652e3fd47d39667594cce08/llpc/lowering/LowerGlCompatibility.cpp#L830-L836)) and without a GFX-level check ([getPrimType](https://github.com/GPUOpen-Drivers/llpc/blob/40cb8d95ad8d6f7f1652e3fd47d39667594cce08/lgc/lowering/LowerInOut.cpp#L5307-L5315)). Nobody has checked it on gfx1013. In the whole word only bits 0-1 and 8-11 have a source. Bits 2-7, 12-15 and 29-31 are assumed zero, not measured: the VRS rates (2-5) and the eye id (7) because the driver supports neither variable-rate shading nor multiview, so a title that enables either would read other bits on the console, and bits 6, 12-15 and 29-31 because no source gives them a meaning. The sample field reads SampleId, which is always 0 here because the driver rejects MSAA and per-sample shading (`PA_SC_AA_CONFIG`, `PS_ITER_SAMPLE`). The whole word has the same 0 at bits 16-28. A change that makes draws export a layer must restore the Layer read in both places
- [Front-face input with `FRONT_FACE_ALL_BITS`](../../../core/shader/recompiler/Translation/src/InstructionTranslator.cpp) (shader recompiler) - with `SPI_BARYC_CNTL.FRONT_FACE_ALL_BITS` set, a back face reads 0 and a front face 0xffffffff. Mesa 22.0 RADV sets the bit ([radv_pipeline.c](https://gitlab.freedesktop.org/mesa/mesa/-/blob/mesa-22.0.0/src/amd/vulkan/radv_pipeline.c#L5953)) and ACO tests the input with `v_cmp_lg_u32 0` ([aco_instruction_selection.cpp](https://gitlab.freedesktop.org/mesa/mesa/-/blob/mesa-22.0.0/src/amd/compiler/aco_instruction_selection.cpp#L8123-L8128)), so only back = 0 and front ≠ 0 are sourced; PPSA05684's two-sided cloth shaders test it with `v_cmp_lt_u32 0`. The exact front value is not measured. Without the bit the input is ±1.0, as current Mesa reads it (`fgt 0`, `fsign`; [ac_nir_lower_intrinsics_to_args.c](https://gitlab.freedesktop.org/mesa/mesa/-/blob/bffd91947efc1932da99aca69fc540d6d4201b0c/src/amd/common/nir/ac_nir_lower_intrinsics_to_args.c))
Expand Down
Loading