From bc1272269829b6d905f0dde112e68bb2eea5a6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Franch?= Date: Sun, 4 Oct 2026 18:49:49 +0200 Subject: [PATCH] fix(shader): convert unorm and snorm buffer format loads with correct rounding Format loads divided by 2^b-1 with OpFDiv, which Vulkan only bounds to 2.5 ULP; RADV returns a different f32 than the hardware for many 8- and 16-bit values. The quotient is now built on the bits: v/(2^b-1) is v's b bits repeated, so the mantissa is the rotated value replicated and rounded on the next bit (no ties). Adds an execution test of buffer_load_format_x..xyzw, tbuffer_load_format_x/xy/xyz and buffer_store_format_x..xyzw against values measured on RDNA. Co-Authored-By: Claude --- .../tests/execution/BufferFormat.cpp | 188 ++++++++++++++++++ .../src/SpirvMemory/SpirvFormatConvert.cpp | 44 ++-- 2 files changed, 216 insertions(+), 16 deletions(-) create mode 100644 core/libs/prx/libSceAgcDriver/tests/execution/BufferFormat.cpp diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/BufferFormat.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/BufferFormat.cpp new file mode 100644 index 000000000..7ba31cd23 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/execution/BufferFormat.cpp @@ -0,0 +1,188 @@ +#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp" +#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp" +#include "Recompiler.hpp" +#include "VulkanTestDevice.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using AgcDriver::Graphics::Require; +using ShaderRecompiler::ShaderStage; + +constexpr std::uint32_t Threads = 32; +constexpr std::uint32_t Inputs = 8; +constexpr std::uint32_t Results = 32; +constexpr std::uint32_t Loads = 24; +constexpr std::uint32_t Stored = 7; +constexpr std::uint32_t ScratchWords = 16; +alignas(256) std::array Input{}; +alignas(256) std::array Output{}; +alignas(256) std::array Scratch{}; + +alignas(256) constexpr std::array Code{ + 0x34040086, 0x34060087, 0x34500085, 0xe0381000, 0x80023228, 0xe0381010, 0x80023628, 0xbf8c3f70, + 0xe0001000, 0x80020a28, 0xe0041004, 0x80030b28, 0xe0081008, 0x80040d28, 0xe00c100c, 0x80051028, + 0xe8381002, 0x80021428, 0xe9211004, 0x80021528, 0xe99a1000, 0x80021728, 0xe00c1008, 0x80061a28, + 0xe00c100c, 0x80071e28, 0xe0101000, 0x80083602, 0xe0141004, 0x80093702, 0xe018100c, 0x800a3602, + 0xe01c1010, 0x800b3602, 0xe01c1018, 0x80083602, 0xbf8c3f70, 0xe0781000, 0x80010a03, 0xe0781010, + 0x80010e03, 0xe0781020, 0x80011203, 0xe0781030, 0x80011603, 0xe0781040, 0x80011a03, 0xe0781050, + 0x80011e03, 0xbf810000, +}; + +constexpr std::uint32_t Rows[32][8] = { + {0xff807f00u, 0x80000001u, 0x7fff8001u, 0xffff0000u, 0x3f800000u, 0xbf800000u, 0x3f000000u, 0x80000000u}, + {0x00000000u, 0x7c0007c0u, 0xffe00000u, 0x7ff00000u, 0x7f800000u, 0xff800000u, 0x7fc00001u, 0x0da24260u}, + {0x81018101u, 0x00010000u, 0x00008000u, 0x0001ffffu, 0x3b808081u, 0x3b008081u, 0x37800080u, 0xb7800080u}, + {0x200001ffu, 0x3ff003ffu, 0x07ff07ffu, 0xc0000000u, 0x3f7f0000u, 0x3effffffu, 0xbf7fffffu, 0x40000000u}, + {0x52e6b438u, 0xf2a74de4u, 0x269e0d37u, 0x6513270eu, 0xbf8e4b0fu, 0xbeb2cb7fu, 0x7f800000u, 0x80000000u}, + {0x11e20b8fu, 0x3d9c1724u, 0x1738f7d9u, 0x8d116eceu, 0x3f598d83u, 0xbf800000u, 0x3e5cf891u, 0xff800000u}, + {0xf9ebdaccu, 0x3898d190u, 0x0becd7b0u, 0x8e81973eu, 0xbf0c093bu, 0x80000000u, 0x3e206dd1u, 0xbf841a62u}, + {0x301850c5u, 0x5f557203u, 0x18f135d2u, 0x8c38fb29u, 0x3e2b5fc8u, 0xbc18bc9du, 0x3f388602u, 0x3f8cebdau}, + {0x3f98e277u, 0xcb5c7427u, 0x2e05319au, 0xb2f14c94u, 0xbf8b289du, 0xbc500907u, 0xbe08394eu, 0xbf8e09ecu}, + {0x2a3af4d4u, 0xc1d3fcffu, 0x5790f82eu, 0x26e87555u, 0xbe507866u, 0xbf8c9162u, 0x3f406bccu, 0xbed4d441u}, + {0x7f26144bu, 0x9474031bu, 0xcc011cddu, 0x74c9df6au, 0x80000000u, 0xbd89ebd2u, 0xbf92358fu, 0x3ec3dba1u}, + {0xd269a9a5u, 0x72158370u, 0x48db40afu, 0xb774eb52u, 0x3ee082b0u, 0xc0000000u, 0x3e93a7f6u, 0xbf3b8f97u}, + {0xbd0561e6u, 0x3f63af83u, 0x65dc9f50u, 0x6415479cu, 0xbc14cf1du, 0x40000000u, 0x3f7f2e26u, 0x3f7244a0u}, + {0x6a50df4du, 0xfc891b4au, 0x5bd86d40u, 0xaec6f024u, 0x3f98553cu, 0x3f800000u, 0x0da24260u, 0xc0000000u}, + {0x2eae05cfu, 0x43435cc5u, 0x482c9cbcu, 0x010c4759u, 0x7f800000u, 0x3e30a07bu, 0x3efd95cdu, 0x3e9c8a1bu}, + {0x0dd27a65u, 0x74e69a5du, 0xe647cb8fu, 0xdef88334u, 0x3f7946a6u, 0xbe8f43e4u, 0xbf83f15fu, 0x7fcd5c60u}, + {0x298cb3a5u, 0x1c2442f9u, 0x570dc195u, 0x99c94309u, 0x00000000u, 0x3dc2fcb8u, 0x3e976834u, 0xbf800000u}, + {0x2607679du, 0xa268aa87u, 0x4093f6deu, 0xf4998d7cu, 0xbeb4d35cu, 0x3eaaaaabu, 0x3f9f04feu, 0xbefa7653u}, + {0xbfeaa155u, 0x57b6fb7eu, 0xbd87a865u, 0x43c71b9au, 0x3effaa7du, 0xbf44357au, 0xbeb808feu, 0x3f89d3e5u}, + {0x4c4f9b06u, 0xfa7f0eabu, 0xa49636a2u, 0xdd02de92u, 0x3eaaaaabu, 0xbf000000u, 0xbec018e6u, 0x7f800000u}, + {0x5464ecc2u, 0xa2eddbbdu, 0x39194242u, 0x9cfc8652u, 0x3fa16226u, 0x3f4bb9d5u, 0x3f1fa8d4u, 0x7f800000u}, + {0xbb2313f5u, 0x076b3e36u, 0xfd56a926u, 0x0726e25cu, 0xbd93d0edu, 0xff800000u, 0xbe0c8044u, 0x3fa26b48u}, + {0x5d58c705u, 0x149e259bu, 0x38703800u, 0x1a26f889u, 0xbf800000u, 0xbd38bc43u, 0x3e92c7eeu, 0x0da24260u}, + {0xa4a45effu, 0x15b40aebu, 0xd5ab8b4du, 0xa91c2439u, 0x40000000u, 0x3f267e55u, 0xbf55f9d7u, 0xbedef3ffu}, + {0xf8be8831u, 0xb8c9817au, 0x6555abfeu, 0x7691b06fu, 0x3f94b1adu, 0xbf5ba544u, 0x3f800000u, 0xbdb87bd0u}, + {0x9c9011efu, 0xd39630d6u, 0x988af3fbu, 0xfaf55496u, 0x3f9196d5u, 0x7f800000u, 0x00000000u, 0x3f16ac02u}, + {0xbfdefc15u, 0xef02090bu, 0x23a5ef88u, 0x6f0e2289u, 0xbf4b2333u, 0xbf9d156du, 0x7f800000u, 0xff800000u}, + {0x8b5ab3eeu, 0x6b446806u, 0xd58dcdb4u, 0x218e0b7bu, 0x8da24260u, 0xbddec8cdu, 0x3f868ccbu, 0x3f8b0480u}, + {0x8825ae56u, 0x26debfdbu, 0x86048719u, 0x82b33599u, 0xc0000000u, 0x3e908208u, 0xbf69176au, 0xff800000u}, + {0x8e752fdfu, 0x0fcf31cau, 0x537390e5u, 0xaead44b0u, 0x7fdcf073u, 0xbf831638u, 0xbf2766f5u, 0x3e800000u}, + {0xc28ee907u, 0xe4ddf9b9u, 0xe998d0eeu, 0x1038f0b5u, 0x3e95cc10u, 0x3d0183cbu, 0xbdfdbfc7u, 0xbd69e789u}, + {0xb2fff17bu, 0x85f1115bu, 0xe064a114u, 0xe040015cu, 0xbf2003efu, 0x3f9384efu, 0xbf7185f8u, 0xc0000000u} +}; + +constexpr std::uint32_t Expected[32][31] = { + {0x00000000u, 0x37800080u, 0x3f000080u, 0xbf800000u, 0x3f800000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xbc010204u, 0xbc010204u, 0x3f7f8100u, 0x35800000u, 0x00000000u, 0xbf004020u, 0x3d787c3eu, 0xbc804020u, 0x3f800000u, 0x00000000u, 0x3effff00u, 0x3f000180u, 0x00000000u, 0x3f800000u, 0x00000000u, 0x00000000u, 0x000000ffu, 0x80000000u, 0x00000000u, 0x0040817fu, 0x80017fffu, 0x00004000u, 0x008000ffu}, + {0x00000000u, 0x3cf800f8u, 0x3ef800f8u, 0x00000000u, 0xba800100u, 0x00000000u, 0x00000000u, 0x00000000u, 0xbe010204u, 0x3f800000u, 0x00000000u, 0x7f800000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3effe100u, 0x00000000u, 0x3f7fe100u, 0x00000000u, 0x00000000u, 0x3f800000u, 0x00000000u, 0x00000000u, 0x000000ffu, 0x00000000u, 0x00000000u, 0x0000817fu, 0x80017fffu, 0x00000000u, 0x000000ffu}, + {0x3b808081u, 0x00000000u, 0x37800080u, 0xbf800000u, 0x00000000u, 0xb8000100u, 0xbc010204u, 0xbc010204u, 0x3c010204u, 0x00000000u, 0x3f010181u, 0x00000000u, 0x38000000u, 0x3f00c060u, 0x3e406030u, 0x3d004020u, 0x37800080u, 0x3f800000u, 0x00000000u, 0x3f000080u, 0xbf800000u, 0x3f800000u, 0x00000000u, 0xbf800000u, 0x00000001u, 0x00010081u, 0x00000000u, 0x00000000u, 0x00400080u, 0x00000000u, 0x00000101u}, + {0x3f800000u, 0x3c7fc100u, 0x3e7fc100u, 0x3d7fe200u, 0x3d7fe200u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xbf010204u, 0x3e000080u, 0x3ffe0000u, 0x44000000u, 0x3f800000u, 0x00000000u, 0xbf800000u, 0x3f4000c0u, 0x00000000u, 0x3cffe100u, 0x3cffe100u, 0x00000000u, 0x3f800000u, 0x00000000u, 0x00000000u, 0x000000feu, 0x00007fffu, 0x00000000u, 0x00813f7fu, 0x3fff7f7fu, 0x7fff8001u, 0xff007ffeu}, + {0x3e60e0e1u, 0x3e9bc89cu, 0x3f72a7f3u, 0x3dd371a7u, 0x3e9a7935u, 0x3e9c3938u, 0x3de1c387u, 0x3e9d3a75u, 0x3e193265u, 0x3f4b972eu, 0x3ea5cca6u, 0x43c80000u, 0x41d20000u, 0x3de07038u, 0x3f56eb76u, 0x3f174ba6u, 0x3eca26cau, 0x3e1c389cu, 0x3e1a789au, 0x3d5370d3u, 0x421c0000u, 0x3f800000u, 0x00000000u, 0x41600000u, 0x00000000u, 0xffff0000u, 0x00000000u, 0x007fd481u, 0xd34d8001u, 0x00007fffu, 0x00ff0000u}, + {0x3f0f8f90u, 0x3db920b9u, 0x3e7670f6u, 0xbd827105u, 0x3e39c174u, 0x3f5d9dbbu, 0xbec99326u, 0x3f5dbb77u, 0x3e091224u, 0xbf67cf9fu, 0x3d8f108fu, 0x46480000u, 0x3f040000u, 0xbe627139u, 0x3e824121u, 0x3f0f47a4u, 0x3f0d118du, 0x3edd9cdeu, 0x3db9c0bau, 0x3f77d9f8u, 0x42dc0000u, 0x3f800000u, 0x00000000u, 0xc2480000u, 0x000000d9u, 0x373e0000u, 0x00000000u, 0x001b816cu, 0x80016cc6u, 0x80011b9fu, 0x003700d9u}, + {0x3f4ccccdu, 0x3f5190d2u, 0x3e6260e2u, 0xbea14143u, 0x3dbec17eu, 0xbf5185a3u, 0x3ef9f3e8u, 0xbf53a74fu, 0xbf800000u, 0xbf65cb97u, 0x3f79ebfau, 0x3b200000u, 0x3e340000u, 0xbf1a4d27u, 0xbf0542a1u, 0xbe446231u, 0x3f0e818fu, 0x3f173e97u, 0x3d3ec0bfu, 0x3f57b0d8u, 0xc2d20000u, 0x3f800000u, 0x00000000u, 0x42780000u, 0x00000000u, 0x281b0000u, 0x00000000u, 0x001400bbu, 0x0000b9fcu, 0x8001140eu, 0x00280000u}, + {0x3f45c5c6u, 0x3ee406e4u, 0x3ebeaabfu, 0x3ed749afu, 0x3e47898fu, 0xbd1ae136u, 0x3ea54a95u, 0xbd214285u, 0x3ee1c387u, 0xbf69d3a7u, 0x3e4060c0u, 0x3c060000u, 0x3d5c0000u, 0x3ec562b1u, 0xbf767b3eu, 0xbeff7fc0u, 0x3f0c388cu, 0x3f7b29fbu, 0x3dc788c8u, 0x3e5748d7u, 0xc0a00000u, 0x3f800000u, 0x00000000u, 0x42240000u, 0x0000002bu, 0xb8850000u, 0x00000000u, 0x005cff15u, 0xfecf156cu, 0x7fff5c42u, 0xffb8002bu}, + {0x3eeeeeefu, 0x3ee84ee8u, 0x3f4b5ccbu, 0x3ec6698du, 0x3eb81570u, 0x3f192932u, 0xbf59b367u, 0x3f193265u, 0xbdf1e3c8u, 0xbf1d3a75u, 0x3e7e60feu, 0x404e0000u, 0x3f1c0000u, 0xbf44e271u, 0xbf647239u, 0xbc607038u, 0x3f32f1b3u, 0x3e992899u, 0x3e3814b8u, 0x3e4668c6u, 0x42980000u, 0x3f800000u, 0x00000000u, 0xc2d80000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00effe81u, 0xfe608001u, 0x8001eef9u, 0x00000000u}, + {0x3f54d4d5u, 0x3f7cfffdu, 0x3f41d3c2u, 0xbd7a41f5u, 0x3f2f215eu, 0x3f6aabd5u, 0x3f2b56adu, 0x3f6bd7afu, 0xbe418306u, 0x3e993265u, 0x3e28e8a9u, 0x41fe0000u, 0x3cfe0000u, 0x3ed46a35u, 0xbf21d0e8u, 0xbf2ed76cu, 0x3e1ba09cu, 0x3eeaaaebu, 0x3eaf20afu, 0x3f782ef8u, 0x42ea0000u, 0x3f800000u, 0x00000000u, 0x42aa0000u, 0x00000000u, 0xc06b0000u, 0x00000000u, 0x005f81e6u, 0x8001e5f1u, 0xcacb6035u, 0x00c00000u}, + {0x3e969697u, 0x3c46c0c7u, 0x3f147494u, 0x3e66e9ceu, 0xbecffda0u, 0xbe825905u, 0x3f55ab57u, 0xbe850a14u, 0xbeddbb77u, 0x3f69d3a7u, 0x3efe4cfeu, 0x3e360000u, 0x45000000u, 0x3e164b26u, 0x3f42e171u, 0xbce07038u, 0x3ee992eau, 0x3f5f6adfu, 0x3f4c01ccu, 0x3de6e8e7u, 0xc2040000u, 0x3f800000u, 0x00000000u, 0x42d40000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x0081f700u, 0xf7610000u, 0x30f78001u, 0x62000000u}, + {0x3f25a5a6u, 0x3f037083u, 0x3ee42ae4u, 0x3f015f03u, 0x3f11b723u, 0xbe25714bu, 0x3f254a95u, 0xbe2952a5u, 0x3f69d3a7u, 0xbf13264du, 0x3f5269d2u, 0x3ee00000u, 0x3d600000u, 0x3f52e975u, 0xbf4b65b3u, 0x3f1349a5u, 0x3f3774b7u, 0x3f6b52ebu, 0x3e91b692u, 0x3e815e81u, 0xc1a80000u, 0x3f800000u, 0x00000000u, 0x42a40000u, 0x00000070u, 0x49d40000u, 0x00000000u, 0x00258138u, 0x80013820u, 0xa23924eau, 0x004a0070u}, + {0x3f66e6e7u, 0x3f2f83b0u, 0x3e7d8cfeu, 0xbf416183u, 0x3f4bb997u, 0x3f0f391eu, 0xbf499326u, 0x3f0f1e3cu, 0x3e2952a5u, 0x3f499326u, 0x3f3d05bdu, 0x47060000u, 0x40ea0000u, 0x3f7379bdu, 0x3f2c562bu, 0xbdc06030u, 0x3ec82ac8u, 0x3e8f388fu, 0x3ecbb8ccu, 0x3f1f509fu, 0x428e0000u, 0x3f800000u, 0x00000000u, 0xc2c80000u, 0x00000000u, 0xff2dffffu, 0x00000000u, 0x007f7fffu, 0x7ffffed6u, 0x79217f96u, 0xf1feff00u}, + {0x3e9a9a9bu, 0x3dda50dau, 0x3f7c89fdu, 0x3f5a81b5u, 0x3f37b16fu, 0xbdfdc1fcu, 0x3e912245u, 0xbe010204u, 0xbee9d3a7u, 0xbf254a95u, 0x3ed4a0d5u, 0x3e940000u, 0x3a460000u, 0xbeb359adu, 0x3ddc6e37u, 0xbf2dd6ebu, 0x3f2ec6afu, 0x3f7024f0u, 0x3eb7b0b8u, 0x3eda80dbu, 0xc1800000u, 0x3f800000u, 0x00000000u, 0x42100000u, 0x000000ffu, 0x0000ffffu, 0x00000000u, 0x00007f7fu, 0x7fff7fffu, 0x80010000u, 0x0000ffffu}, + {0x3f4fcfd0u, 0x3eb98abau, 0x3e868687u, 0xbf46898du, 0x3f105921u, 0x3f0eb31du, 0x3f3366ceu, 0x3f0f1e3cu, 0x3dc18306u, 0x3c010204u, 0x3e3ab8bbu, 0x418a0000u, 0x38d60000u, 0x3f67f3fau, 0xbe7e7f40u, 0xbf0b45a3u, 0x3b860086u, 0x3e8eb28fu, 0x3e905890u, 0x3f1cbc9du, 0x428e0000u, 0x3f800000u, 0x00000000u, 0x42b20000u, 0x000000ffu, 0x7eca2c28u, 0x00000000u, 0x003f167fu, 0x16147fffu, 0x27223f65u, 0x4e7e2cffu}, + {0x3ecacacbu, 0x3f1a5d9au, 0x3ee9cceau, 0xbed1c5a4u, 0xbe4dc99cu, 0xbf7999f3u, 0x3ed1a347u, 0xbf7bf7f0u, 0xbd810204u, 0xbe891224u, 0x3d5d20ddu, 0x3cba0000u, 0x41a60000u, 0xbf4de6f3u, 0x3e9e4f28u, 0x3edd6eb7u, 0x3f5ef8dfu, 0x3f033483u, 0x3f6647e6u, 0x3f4b8fccu, 0xc2fa0000u, 0x3f800000u, 0x00000000u, 0x42500000u, 0x000000f8u, 0x00000000u, 0x00000000u, 0x0081dc7cu, 0xdc2f7ca2u, 0x00008001u, 0x000000f8u}, + {0x3f25a5a6u, 0x3e85f286u, 0x3de120e1u, 0xbef9adf3u, 0x3f2e1b5cu, 0x3f06130cu, 0x3d912245u, 0x3f070e1cu, 0xbeddbb77u, 0xbf4f9f3eu, 0x3e2630a6u, 0x3df20000u, 0x41100000u, 0xbe365b2eu, 0xbed46a35u, 0xbf345a2du, 0x3f19c99au, 0x3e861286u, 0x3eae1aaeu, 0x3f4195c2u, 0x42860000u, 0x3f800000u, 0x00000000u, 0x41100000u, 0x00000000u, 0x4bb4185fu, 0x00000000u, 0x00260c00u, 0x0c300000u, 0x800125dau, 0x004b1800u}, + {0x3f1d9d9eu, 0x3f2a87abu, 0x3f2268a2u, 0xbd922124u, 0x3f012702u, 0xbf6509cau, 0x3f79f3e8u, 0xbf67cf9fu, 0xbf4f9f3eu, 0xbdc18306u, 0x3e181c98u, 0x3d0e0000u, 0x422a0000u, 0xbe466332u, 0x3f6cf67bu, 0xbf506834u, 0x3f7499f5u, 0x3f0d7c8du, 0x3e812681u, 0x3f76def7u, 0xc2e60000u, 0x3f800000u, 0x00000000u, 0x42f80000u, 0x00000000u, 0xffff5555u, 0x00000000u, 0x007f2ad3u, 0x2aaad2ccu, 0xc1637fffu, 0x00ff5500u}, + {0x3eaaaaabu, 0x3f7b7efbu, 0x3eaf6cafu, 0xbf2f375eu, 0xbf04f30au, 0x3e5cd1bau, 0xbf4d9b36u, 0x3e59b367u, 0xbee5cb97u, 0x3f070e1cu, 0x3f3feac0u, 0x3efc0000u, 0x45be0000u, 0x3f2ad56bu, 0xbf2c562bu, 0xbb804020u, 0x3e878e88u, 0x3ddcd0ddu, 0x3f3d87beu, 0x3f2865a8u, 0x41d80000u, 0x3f800000u, 0x00000000u, 0xc2cc0000u, 0x0000007fu, 0x00000000u, 0x00000000u, 0x00d29f3fu, 0x9de63feau, 0x7fffd1feu, 0xff00007fu}, + {0x3cc0c0c1u, 0x3d6ab0ebu, 0x3f7a7ffau, 0x3eda89b5u, 0xbf36d56eu, 0xbe85b90bu, 0xbf5dbb77u, 0xbe891224u, 0x3c810204u, 0xbe8d1a34u, 0x3e989e99u, 0x45560000u, 0x7fc20000u, 0xbefa7d3fu, 0xbd506834u, 0x3ec46231u, 0x3f5d02ddu, 0x3f5e92dfu, 0x3f2496a5u, 0x3e5a88dbu, 0xc2080000u, 0x3f800000u, 0x00000000u, 0xc2dc0000u, 0x00000055u, 0x00000000u, 0x00000000u, 0x00d0c02au, 0xc0002aaau, 0x7fffcffau, 0xff000055u}, + {0x3f42c2c3u, 0x3f5bbddcu, 0x3f22eda3u, 0x3f048509u, 0x3ee465c9u, 0xbf735de7u, 0x3f254a95u, 0xbf75ebd8u, 0xbd010204u, 0xbf499326u, 0x3ea8c8a9u, 0x3f7a0000u, 0x43760000u, 0x3ec26131u, 0x3f1dcee7u, 0x3f2351a9u, 0x3f1cfc9du, 0x3f065286u, 0x3e6464e4u, 0x3e848485u, 0xc2f40000u, 0x3f800000u, 0x00000000u, 0x42a40000u, 0x000000ffu, 0x9fa8cbb9u, 0x00000000u, 0x004f657fu, 0x65dc7fffu, 0x7fff4fd4u, 0xff9fcbffu}, + {0x3f75f5f6u, 0x3e78d8f9u, 0x3ced60edu, 0xbf2db55bu, 0xbcaa8155u, 0xbe6d21dau, 0x3f3972e6u, 0xbe71e3c8u, 0x3e993265u, 0x3d61c387u, 0x3f3b23bbu, 0x446c0000u, 0x42ce0000u, 0xbcb0582cu, 0x3ec46231u, 0xbe1c4e27u, 0x3ce4c0e5u, 0x3f625ce2u, 0x3f7d56fdu, 0x3f2926a9u, 0xc1f00000u, 0x3f800000u, 0x00000000u, 0x42b80000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00ef81f7u, 0x8001f6c3u, 0x7fffee70u, 0xff000000u}, + {0x3ca0a0a1u, 0x3e166c96u, 0x3da4f0a5u, 0x3ee001c0u, 0x3ee1c1c4u, 0xbd6ee1deu, 0xbf6fdfbfu, 0xbd810204u, 0x3e993265u, 0x3e51a347u, 0x3ebab0bbu, 0x43360000u, 0x3f880000u, 0xbefb7dbfu, 0xbf67f3fau, 0x3f6af57bu, 0x3dd130d1u, 0x3f7889f9u, 0x3e61c0e2u, 0x3e6000e0u, 0xc1000000u, 0x3f800000u, 0x00000000u, 0xc2ee0000u, 0x00000000u, 0x49640000u, 0x00000000u, 0x0024fa81u, 0xfa3a8001u, 0x000024b2u, 0x00490000u}, + {0x3f800000u, 0x3d2eb0afu, 0x3dada0aeu, 0xbf6967d3u, 0xbea95553u, 0x3e90e522u, 0x3ee5cb97u, 0x3e912245u, 0x3e61c387u, 0xbf2f5ebdu, 0x3f24a4a5u, 0x3dd60000u, 0x45020000u, 0xbf00c060u, 0x3f0bc5e3u, 0xbf5b6db7u, 0x3f291ca9u, 0x3e10e491u, 0x3f55abd6u, 0x3f0b4d8bu, 0x42100000u, 0x3f800000u, 0x00000000u, 0x42640000u, 0x000000ffu, 0x0000a67eu, 0x00000000u, 0x0096537fu, 0x533f7fffu, 0xc8439504u, 0x0000a6ffu}, + {0x3e44c4c5u, 0x3f017a81u, 0x3f38c9b9u, 0xbf280550u, 0x3f4aab95u, 0xbf1f233eu, 0x3f5fbf7fu, 0xbf214285u, 0xbf5fbf7fu, 0x3f6ddbb7u, 0x3f78bef9u, 0x3af40000u, 0x3a600000u, 0x3dc46231u, 0xbe3c5e2fu, 0xbe6a753bu, 0x3eed22edu, 0x3f306fb0u, 0x3ecaaacbu, 0x3f2bfeacu, 0xc2a00000u, 0x3f800000u, 0x00000000u, 0x42de0000u, 0x000000ffu, 0xffff0000u, 0x00000000u, 0x007f937fu, 0x922e7fffu, 0xf4787fffu, 0x00ff00ffu}, + {0x3f6feff0u, 0x3e4358c3u, 0x3f5396d4u, 0xbdc05181u, 0xbf4eed9eu, 0x3f292d52u, 0xbf55ab57u, 0x3f2952a5u, 0xbdb162c6u, 0xbd418306u, 0x3f1c909du, 0x39ac0000u, 0x3d8c0000u, 0x3f77fbfeu, 0x3c004020u, 0x3f64f279u, 0x3f7af5fbu, 0x3ea92ca9u, 0x3f188a99u, 0x3f73fbf4u, 0x42a80000u, 0x3f800000u, 0x00000000u, 0xc2d40000u, 0x000000ffu, 0x0000ffffu, 0x00000000u, 0x00007f7fu, 0x7fff7fffu, 0x4b550000u, 0x9600ffffu}, + {0x3da8a8a9u, 0x3d10b091u, 0x3f6f02efu, 0xbe03c108u, 0x3e8e951du, 0x3e8a2514u, 0xbf6fdfbfu, 0x3e891224u, 0x3de1c387u, 0x3f5fbf7fu, 0x3f3fdec0u, 0x3a160000u, 0x38820000u, 0x3d28542au, 0xbe024121u, 0xbbc06030u, 0x3ede1cdeu, 0x3e0a248au, 0x3e0e948fu, 0x3f6f88f0u, 0x42080000u, 0x3f800000u, 0x00000000u, 0xc2ee0000u, 0x00000000u, 0xffff0000u, 0x00000000u, 0x007f819bu, 0x80019a6fu, 0x80017fffu, 0x00ff0000u}, + {0x3f6eeeefu, 0x3ed00cd0u, 0x3ed688d7u, 0xbec93192u, 0xbea9cd54u, 0x3db7b16fu, 0x3f77efe0u, 0x3db162c6u, 0xbf65cb97u, 0x3e850a14u, 0x3f0b5a8bu, 0x36c00000u, 0x391a0000u, 0xbd104824u, 0xbf2a552bu, 0x3eb55aadu, 0x3e063886u, 0x3d37b0b8u, 0x3f558dd6u, 0x3f4db4ceu, 0x41300000u, 0x3f800000u, 0x00000000u, 0x42f60000u, 0x00000000u, 0xffff0000u, 0x00000000u, 0x007ff200u, 0xf2140000u, 0x7fff7fffu, 0xffff0000u}, + {0x3eacacadu, 0x3f3fdbc0u, 0x3e1b789bu, 0xbf71cfe4u, 0xbf73f9e8u, 0x3ed665adu, 0xbf4f9f3eu, 0x3ed5ab57u, 0xbf1b366du, 0xbf7dfbf8u, 0x3f082588u, 0x7fb60000u, 0x3fae0000u, 0xbf556ab5u, 0x3f35daedu, 0x3e824121u, 0x3f02b383u, 0x3e5664d6u, 0x3f060486u, 0x3f071987u, 0x42540000u, 0x3f800000u, 0x00000000u, 0xc2ce0000u, 0x00000000u, 0x00004841u, 0x00000000u, 0x008c2481u, 0x24208001u, 0x80018b75u, 0x00004800u}, + {0x3f5fdfe0u, 0x3e4728c7u, 0x3d7cf0fdu, 0xbf5e37bcu, 0x3f26e74eu, 0x3f096113u, 0xbf214285u, 0x3f091224u, 0xbf274e9du, 0xbf254a95u, 0x3f0e758eu, 0x3b940000u, 0x3bcc0000u, 0xbd844221u, 0x3f25d2e9u, 0x3ee773bau, 0x3f2eadafu, 0x3e896089u, 0x3ea6e6a7u, 0x3f10e591u, 0x42880000u, 0x3f800000u, 0x00000000u, 0xc2a00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00ad8100u, 0x80010000u, 0x2000ac4du, 0x40000000u}, + {0x3ce0e0e1u, 0x3f79b9fau, 0x3f64dde5u, 0xbebc4979u, 0xbe334167u, 0xbdf4b1e9u, 0xbf172e5du, 0xbe010204u, 0x3ee1c387u, 0x3e010204u, 0x3f428ec3u, 0x3b720000u, 0x3f7e0000u, 0x3f03c1e1u, 0xbe0c4623u, 0x3da05028u, 0x3d81c082u, 0x3f70b5f1u, 0x3f6998eau, 0x3f50eed1u, 0xc1800000u, 0x3f800000u, 0x00000000u, 0xc2960000u, 0x0000004bu, 0x00000818u, 0x00000000u, 0x00f00425u, 0x040c2573u, 0xf8b1f024u, 0x0000084bu}, + {0x3ef6f6f7u, 0x3d8ad88bu, 0x3f05f186u, 0xbf3dd97cu, 0xbe7ce1fau, 0x3c2e015cu, 0x3f3972e6u, 0x3c010204u, 0x3f010204u, 0xbe810204u, 0x3f32ffb3u, 0x3ab60000u, 0x44440000u, 0x3f3ddeefu, 0xbc004020u, 0xbed168b4u, 0x3f6040e0u, 0x3bae00aeu, 0x3f6064e0u, 0x3f2114a1u, 0x3f800000u, 0x3f800000u, 0x00000000u, 0x42b80000u, 0x00000000u, 0x0000ffffu, 0x00000000u, 0x00887fb1u, 0x7fffafffu, 0x8001873eu, 0x0000ff00u} +}; + +constexpr const char* Names[Loads + Stored] = { + "buffer_load_format_x 8_8_8_8_UNORM x", "buffer_load_format_xy 16_16_16_16_UNORM x", "buffer_load_format_xy 16_16_16_16_UNORM y", + "buffer_load_format_xyz 16_16_16_16_SNORM x", "buffer_load_format_xyz 16_16_16_16_SNORM y", "buffer_load_format_xyz 16_16_16_16_SNORM z", + "buffer_load_format_xyzw 8_8_8_8_SNORM x", "buffer_load_format_xyzw 8_8_8_8_SNORM y", "buffer_load_format_xyzw 8_8_8_8_SNORM z", + "buffer_load_format_xyzw 8_8_8_8_SNORM w", "tbuffer_load_format_x 16_UNORM x", "tbuffer_load_format_xy 10_11_11_FLOAT x", + "tbuffer_load_format_xy 10_11_11_FLOAT y", "tbuffer_load_format_xyz 2_10_10_10_SNORM x", "tbuffer_load_format_xyz 2_10_10_10_SNORM y", + "tbuffer_load_format_xyz 2_10_10_10_SNORM z", "buffer_load_format_xyzw 16_16_16_16_UNORM x", "buffer_load_format_xyzw 16_16_16_16_UNORM y", + "buffer_load_format_xyzw 16_16_16_16_UNORM z", "buffer_load_format_xyzw 16_16_16_16_UNORM w", "buffer_load_format_xyzw 8_8_8_8_SSCALED x", + "buffer_load_format_xyzw 8_8_8_8_SSCALED y", "buffer_load_format_xyzw 8_8_8_8_SSCALED z", "buffer_load_format_xyzw 8_8_8_8_SSCALED w", + "scratch dword 0 (store_format_x 8_8_8_8_UNORM)", "scratch dword 1 (store_format_xy 16_16_16_16_UNORM low)", "scratch dword 2 (store_format_xy 16_16_16_16_UNORM high)", + "scratch dword 3 (store_format_xyz 8_8_8_8_SNORM)", "scratch dword 4 (store_format_xyzw 16_16_16_16_SNORM low)", "scratch dword 5 (store_format_xyzw 16_16_16_16_SNORM high)", + "scratch dword 6 (store_format_xyzw 8_8_8_8_UNORM)", +}; + +constexpr std::array FormattedWord3{0x31038facu, 0x31041facu, 0x31042facu, 0x31039facu, 0x31041977u, 0x3103b80du, 0x31038facu, 0x31041facu, 0x31039facu, 0x31042facu}; + +std::array BufferDescriptor(const void* data, std::uint32_t bytes, std::uint32_t word3) { + const auto address = reinterpret_cast(data); + return {static_cast(address), static_cast((address >> 32u) & 0xffffu), bytes, word3}; +} + +std::string Hex(std::uint32_t value) { + char text[16]; + std::snprintf(text, sizeof(text), "0x%08x", value); + return text; +} + +void Expect(std::uint32_t tid, std::uint32_t actual, std::uint32_t expected, const char* name) { + Require(actual == expected, std::string("buffer format: lane ") + std::to_string(tid) + " " + name + " is " + Hex(actual) + ", expected " + Hex(expected)); +} + +void Run(AgcDriver::VulkanDevice& device) { + for (std::uint32_t tid = 0; tid < Threads; ++tid) std::copy(std::begin(Rows[tid]), std::end(Rows[tid]), &Input[tid * Inputs]); + Output.fill(0xdeadbeefu); + Scratch.fill(0xaaaaaaaau); + std::vector userData(8u + 4u * FormattedWord3.size(), 0u); + const auto output = BufferDescriptor(Output.data(), static_cast(Output.size() * 4u), 0x31016facu); + std::copy(output.begin(), output.end(), userData.begin() + 4); + for (std::size_t index = 0; index < FormattedWord3.size(); ++index) { + const bool store = index >= 6u; + const auto descriptor = store ? BufferDescriptor(Scratch.data(), static_cast(Scratch.size() * 4u), FormattedWord3[index]) + : BufferDescriptor(Input.data(), static_cast(Input.size() * 4u), FormattedWord3[index]); + std::copy(descriptor.begin(), descriptor.end(), userData.begin() + 8 + 4 * index); + } + const std::span code(Code); + const std::array memory{{{reinterpret_cast(code.data()), std::as_bytes(code)}}}; + const ShaderRecompiler::ShaderComputeStageInfo compute{{Threads, 1, 1}, 0u, {false, false, false}, false, 1}; + ShaderRecompiler::RecompileRequest request{ + {ShaderStage::Compute, reinterpret_cast(code.data()), code, 0, {}}, + {32, 0, userData, compute, std::nullopt, std::nullopt, memory}, + device.Target(), + {0, 0, 0, 128} + }; + request.useCache = false; + const auto result = ShaderRecompiler::Recompile(request); + device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast(code.data())); + device.WaitIdle(); +} + +void Check() { + for (std::uint32_t tid = 0; tid < Threads; ++tid) { + for (std::uint32_t index = 0; index < Loads; ++index) Expect(tid, Output[tid * Results + index], Expected[tid][index], Names[index]); + for (std::uint32_t index = 0; index < Stored; ++index) Expect(tid, Scratch[tid * ScratchWords + index], Expected[tid][Loads + index], Names[Loads + index]); + } +} + +} + +int main() { + try { + const auto device = OpenVulkanTestDevice(); + if (!device) return VulkanTestSkipped; + Run(*device); + Check(); + std::puts("buffer format tests passed"); + return 0; + } catch (const std::exception& error) { + std::cerr << error.what() << '\n'; + return 1; + } +} diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvFormatConvert.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvFormatConvert.cpp index 4f7727739..444bde46e 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvFormatConvert.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvFormatConvert.cpp @@ -19,6 +19,26 @@ namespace { throw std::runtime_error("SPIR-V module emission failed: " + reason); } + std::uint32_t UnormToF32Bits(SpirvEmitterState& state, std::uint32_t value, std::uint32_t bits) { + const std::uint32_t maximum = (1u << bits) - 1u; + const auto zero = EmitCompareU32Constant(state, spv::OpIEqual, value, 0u); + const auto safe = EmitSelectValueU32(state, zero, ConstantU32(state, 1u), value); + const auto msb = state.module.AllocateId(); + state.module.AddFunction(spv::OpExtInst, TypeU32(state), msb, GlslStd450(state), GLSLstd450FindUMsb, safe); + const auto leading = EmitBinaryU32(state, spv::OpISub, ConstantU32(state, bits - 1u), msb); + const auto high = EmitBinaryU32(state, spv::OpShiftLeftLogical, safe, leading); + const auto low = EmitBinaryU32(state, spv::OpShiftRightLogical, safe, EmitBinaryU32(state, spv::OpISub, ConstantU32(state, bits), leading)); + const auto rotated = EmitAndConstant(state, EmitOrU32(state, high, low), maximum); + auto pattern = EmitBinaryU32(state, spv::OpShiftLeftLogical, rotated, ConstantU32(state, 32u - bits)); + for (std::uint32_t shift = bits; shift < 32u; shift *= 2u) { + pattern = EmitOrU32(state, pattern, EmitBinaryU32(state, spv::OpShiftRightLogical, pattern, ConstantU32(state, shift))); + } + const auto round = EmitAndConstant(state, EmitBinaryU32(state, spv::OpShiftRightLogical, pattern, ConstantU32(state, 7u)), 1u); + const auto rounded = EmitAddU32(state, EmitBinaryU32(state, spv::OpShiftRightLogical, pattern, ConstantU32(state, 8u)), round); + const auto exponent = EmitBinaryU32(state, spv::OpShiftLeftLogical, EmitAddU32(state, msb, ConstantU32(state, 126u - bits)), ConstantU32(state, 23u)); + return EmitSelectValueU32(state, zero, ConstantU32(state, 0u), EmitAddU32(state, exponent, rounded)); + } + } std::uint32_t EmitTBufferBitcastU32ToI32(SpirvEmitterState& state, std::uint32_t value) { @@ -71,24 +91,16 @@ std::uint32_t NormalizeFormatComponent(SpirvEmitterState& state, const SpirvBuff state.module.AddFunction(spv::OpConvertSToF, TypeF32(state), value, signedRaw); return EmitBitcastF32ToU32(state, value); } - case SpirvFormatComponentType::Unorm: { - const auto value = state.module.AllocateId(); - const auto normalized = state.module.AllocateId(); - const auto maxValue = static_cast((1u << bits) - 1u); - state.module.AddFunction(spv::OpConvertUToF, TypeF32(state), value, raw); - state.module.AddFunction(spv::OpFDiv, TypeF32(state), normalized, value, ConstantF32Value(state, maxValue)); - return EmitBitcastF32ToU32(state, normalized); - } + case SpirvFormatComponentType::Unorm: + return UnormToF32Bits(state, raw, bits); case SpirvFormatComponentType::Snorm: { - const auto signedRaw = EmitTBufferBitcastU32ToI32(state, raw); - const auto value = state.module.AllocateId(); - const auto normalized = state.module.AllocateId(); + const std::uint32_t maximum = (1u << (bits - 1u)) - 1u; + const auto negative = Binary(state, spv::OpSLessThan, TypeBool(state), EmitTBufferBitcastU32ToI32(state, raw), EmitTBufferBitcastU32ToI32(state, ConstantU32(state, 0u))); + const auto magnitude = EmitSelectValueU32(state, negative, Unary(state, spv::OpSNegate, TypeU32(state), raw), raw); const auto clamped = state.module.AllocateId(); - const auto maxValue = static_cast((1u << (bits - 1u)) - 1u); - state.module.AddFunction(spv::OpConvertSToF, TypeF32(state), value, signedRaw); - state.module.AddFunction(spv::OpFDiv, TypeF32(state), normalized, value, ConstantF32Value(state, maxValue)); - state.module.AddFunction(spv::OpExtInst, TypeF32(state), clamped, GlslStd450(state), GLSLstd450FMax, normalized, ConstantF32Value(state, -1.0f)); - return EmitBitcastF32ToU32(state, clamped); + state.module.AddFunction(spv::OpExtInst, TypeU32(state), clamped, GlslStd450(state), GLSLstd450UMin, magnitude, ConstantU32(state, maximum)); + const auto sign = EmitSelectValueU32(state, negative, ConstantU32(state, 0x80000000u), ConstantU32(state, 0u)); + return EmitOrU32(state, sign, UnormToF32Bits(state, clamped, bits - 1u)); } case SpirvFormatComponentType::Float: if (bits == 32u) {