Merge pull request #3932 from CouchRiv/fix-shader-f16-denorm-flush-family

fix(shader): flush f16 denormals in unary, frexp and conversion ops per the float mode
This commit is contained in:
Dean 2026-10-12 17:29:43 +13:00 committed by GitHub
commit 32cb3fea95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 386 additions and 10 deletions

View File

@ -0,0 +1,353 @@
#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
#include "Recompiler.hpp"
#include "VulkanTestDevice.hpp"
#include <algorithm>
#include <array>
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <span>
#include <string>
#include <vector>
namespace {
using AgcDriver::Graphics::Require;
using ShaderRecompiler::ShaderStage;
constexpr std::uint32_t Threads = 32;
constexpr std::uint32_t Inputs = 4;
constexpr std::uint32_t Results = 1;
alignas(256) std::array<std::uint32_t, Threads * Inputs> Input{};
alignas(256) std::array<std::uint32_t, Threads * Results> Output{};
struct alignas(256) Shader {
std::array<std::uint32_t, 11> words;
};
struct Vector {
std::uint32_t half;
std::uint32_t expected[4];
};
struct Case {
const char* name;
std::uint32_t word;
std::span<const Vector> rows;
};
constexpr Vector CeilRows[] = {
{0x0001u, {0x5a5a0000u, 0x5a5a3c00u, 0x5a5a0000u, 0x5a5a3c00u}},
{0x0200u, {0x5a5a0000u, 0x5a5a3c00u, 0x5a5a0000u, 0x5a5a3c00u}},
{0x03ffu, {0x5a5a0000u, 0x5a5a3c00u, 0x5a5a0000u, 0x5a5a3c00u}},
{0x8001u, {0x5a5a8000u, 0x5a5a8000u, 0x5a5a8000u, 0x5a5a8000u}},
{0x8200u, {0x5a5a8000u, 0x5a5a8000u, 0x5a5a8000u, 0x5a5a8000u}},
{0x83ffu, {0x5a5a8000u, 0x5a5a8000u, 0x5a5a8000u, 0x5a5a8000u}},
{0x0400u, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0x8400u, {0x5a5a8000u, 0x5a5a8000u, 0x5a5a8000u, 0x5a5a8000u}},
{0x0000u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x3c00u, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0xbc00u, {0x5a5abc00u, 0x5a5abc00u, 0x5a5abc00u, 0x5a5abc00u}},
{0x3800u, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
};
constexpr Vector CvtF32Rows[] = {
{0x0001u, {0x00000000u, 0x33800000u, 0x00000000u, 0x33800000u}},
{0x0200u, {0x00000000u, 0x38000000u, 0x00000000u, 0x38000000u}},
{0x03ffu, {0x00000000u, 0x387fc000u, 0x00000000u, 0x387fc000u}},
{0x8001u, {0x80000000u, 0xb3800000u, 0x80000000u, 0xb3800000u}},
{0x8200u, {0x80000000u, 0xb8000000u, 0x80000000u, 0xb8000000u}},
{0x83ffu, {0x80000000u, 0xb87fc000u, 0x80000000u, 0xb87fc000u}},
{0x0400u, {0x38800000u, 0x38800000u, 0x38800000u, 0x38800000u}},
{0x8400u, {0xb8800000u, 0xb8800000u, 0xb8800000u, 0xb8800000u}},
{0x0000u, {0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u}},
{0x3c00u, {0x3f800000u, 0x3f800000u, 0x3f800000u, 0x3f800000u}},
{0xbc00u, {0xbf800000u, 0xbf800000u, 0xbf800000u, 0xbf800000u}},
{0x3800u, {0x3f000000u, 0x3f000000u, 0x3f000000u, 0x3f000000u}},
};
constexpr Vector CvtNormI16Rows[] = {
{0x0001u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x0200u, {0x5a5a0000u, 0x5a5a0001u, 0x5a5a0000u, 0x5a5a0001u}},
{0x03ffu, {0x5a5a0000u, 0x5a5a0002u, 0x5a5a0000u, 0x5a5a0002u}},
{0x8001u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x8200u, {0x5a5a0000u, 0x5a5affffu, 0x5a5a0000u, 0x5a5affffu}},
{0x83ffu, {0x5a5a0000u, 0x5a5afffeu, 0x5a5a0000u, 0x5a5afffeu}},
{0x0400u, {0x5a5a0002u, 0x5a5a0002u, 0x5a5a0002u, 0x5a5a0002u}},
{0x8400u, {0x5a5afffeu, 0x5a5afffeu, 0x5a5afffeu, 0x5a5afffeu}},
{0x0000u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x3c00u, {0x5a5a7fffu, 0x5a5a7fffu, 0x5a5a7fffu, 0x5a5a7fffu}},
{0xbc00u, {0x5a5a8001u, 0x5a5a8001u, 0x5a5a8001u, 0x5a5a8001u}},
{0x3800u, {0x5a5a4000u, 0x5a5a4000u, 0x5a5a4000u, 0x5a5a4000u}},
{0xb800u, {0x5a5ac000u, 0x5a5ac000u, 0x5a5ac000u, 0x5a5ac000u}},
};
constexpr Vector CvtNormU16Rows[] = {
{0x0001u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x0200u, {0x5a5a0000u, 0x5a5a0002u, 0x5a5a0000u, 0x5a5a0002u}},
{0x03ffu, {0x5a5a0000u, 0x5a5a0004u, 0x5a5a0000u, 0x5a5a0004u}},
{0x8001u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x8200u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x83ffu, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x0400u, {0x5a5a0004u, 0x5a5a0004u, 0x5a5a0004u, 0x5a5a0004u}},
{0x8400u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x0000u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x3c00u, {0x5a5affffu, 0x5a5affffu, 0x5a5affffu, 0x5a5affffu}},
{0xbc00u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x3800u, {0x5a5a8000u, 0x5a5a8000u, 0x5a5a8000u, 0x5a5a8000u}},
{0xb800u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
};
constexpr Vector ExpRows[] = {
{0x0001u, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0x0200u, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0x03ffu, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0x8001u, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0x8200u, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0x83ffu, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0x0400u, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0x8400u, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0x0000u, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0x3c00u, {0x5a5a4000u, 0x5a5a4000u, 0x5a5a4000u, 0x5a5a4000u}},
{0xbc00u, {0x5a5a3800u, 0x5a5a3800u, 0x5a5a3800u, 0x5a5a3800u}},
{0xcb01u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a03fau, 0x5a5a03fau}},
{0xcca0u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a002du, 0x5a5a002du}},
{0xce3cu, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0001u, 0x5a5a0001u}},
{0xc800u, {0x5a5a1c00u, 0x5a5a1c00u, 0x5a5a1c00u, 0x5a5a1c00u}},
{0xc400u, {0x5a5a2c00u, 0x5a5a2c00u, 0x5a5a2c00u, 0x5a5a2c00u}},
};
constexpr Vector FloorRows[] = {
{0x0001u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x0200u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x03ffu, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x8001u, {0x5a5a8000u, 0x5a5abc00u, 0x5a5a8000u, 0x5a5abc00u}},
{0x8200u, {0x5a5a8000u, 0x5a5abc00u, 0x5a5a8000u, 0x5a5abc00u}},
{0x83ffu, {0x5a5a8000u, 0x5a5abc00u, 0x5a5a8000u, 0x5a5abc00u}},
{0x0400u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x8400u, {0x5a5abc00u, 0x5a5abc00u, 0x5a5abc00u, 0x5a5abc00u}},
{0x0000u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x3c00u, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0xbc00u, {0x5a5abc00u, 0x5a5abc00u, 0x5a5abc00u, 0x5a5abc00u}},
{0xb800u, {0x5a5abc00u, 0x5a5abc00u, 0x5a5abc00u, 0x5a5abc00u}},
};
constexpr Vector FractRows[] = {
{0x0001u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0001u}},
{0x0200u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0200u}},
{0x03ffu, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a03ffu}},
{0x8001u, {0x5a5a0000u, 0x5a5a3bffu, 0x5a5a0000u, 0x5a5a3bffu}},
{0x8200u, {0x5a5a0000u, 0x5a5a3bffu, 0x5a5a0000u, 0x5a5a3bffu}},
{0x83ffu, {0x5a5a0000u, 0x5a5a3bffu, 0x5a5a0000u, 0x5a5a3bffu}},
{0x0400u, {0x5a5a0400u, 0x5a5a0400u, 0x5a5a0400u, 0x5a5a0400u}},
{0x8400u, {0x5a5a3bffu, 0x5a5a3bffu, 0x5a5a3bffu, 0x5a5a3bffu}},
{0x0000u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x3c00u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0xbc00u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0xb800u, {0x5a5a3800u, 0x5a5a3800u, 0x5a5a3800u, 0x5a5a3800u}},
{0x4900u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
};
constexpr Vector FrexpExpRows[] = {
{0x0001u, {0x5a5a0000u, 0x5a5affe9u, 0x5a5a0000u, 0x5a5affe9u}},
{0x0200u, {0x5a5a0000u, 0x5a5afff2u, 0x5a5a0000u, 0x5a5afff2u}},
{0x03ffu, {0x5a5a0000u, 0x5a5afff2u, 0x5a5a0000u, 0x5a5afff2u}},
{0x8001u, {0x5a5a0000u, 0x5a5affe9u, 0x5a5a0000u, 0x5a5affe9u}},
{0x8200u, {0x5a5a0000u, 0x5a5afff2u, 0x5a5a0000u, 0x5a5afff2u}},
{0x83ffu, {0x5a5a0000u, 0x5a5afff2u, 0x5a5a0000u, 0x5a5afff2u}},
{0x0400u, {0x5a5afff3u, 0x5a5afff3u, 0x5a5afff3u, 0x5a5afff3u}},
{0x8400u, {0x5a5afff3u, 0x5a5afff3u, 0x5a5afff3u, 0x5a5afff3u}},
{0x0000u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x3c00u, {0x5a5a0001u, 0x5a5a0001u, 0x5a5a0001u, 0x5a5a0001u}},
{0xbc00u, {0x5a5a0001u, 0x5a5a0001u, 0x5a5a0001u, 0x5a5a0001u}},
{0x4900u, {0x5a5a0004u, 0x5a5a0004u, 0x5a5a0004u, 0x5a5a0004u}},
};
constexpr Vector FrexpMantRows[] = {
{0x0001u, {0x5a5a0000u, 0x5a5a3800u, 0x5a5a0000u, 0x5a5a3800u}},
{0x0200u, {0x5a5a0000u, 0x5a5a3800u, 0x5a5a0000u, 0x5a5a3800u}},
{0x03ffu, {0x5a5a0000u, 0x5a5a3bfeu, 0x5a5a0000u, 0x5a5a3bfeu}},
{0x8001u, {0x5a5a8000u, 0x5a5ab800u, 0x5a5a8000u, 0x5a5ab800u}},
{0x8200u, {0x5a5a8000u, 0x5a5ab800u, 0x5a5a8000u, 0x5a5ab800u}},
{0x83ffu, {0x5a5a8000u, 0x5a5abbfeu, 0x5a5a8000u, 0x5a5abbfeu}},
{0x0400u, {0x5a5a3800u, 0x5a5a3800u, 0x5a5a3800u, 0x5a5a3800u}},
{0x8400u, {0x5a5ab800u, 0x5a5ab800u, 0x5a5ab800u, 0x5a5ab800u}},
{0x0000u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x3c00u, {0x5a5a3800u, 0x5a5a3800u, 0x5a5a3800u, 0x5a5a3800u}},
{0xbc00u, {0x5a5ab800u, 0x5a5ab800u, 0x5a5ab800u, 0x5a5ab800u}},
{0x4900u, {0x5a5a3900u, 0x5a5a3900u, 0x5a5a3900u, 0x5a5a3900u}},
};
constexpr Vector LogRows[] = {
{0x0001u, {0x5a5afc00u, 0x5a5ace00u, 0x5a5afc00u, 0x5a5ace00u}},
{0x0200u, {0x5a5afc00u, 0x5a5acb80u, 0x5a5afc00u, 0x5a5acb80u}},
{0x03ffu, {0x5a5afc00u, 0x5a5acb00u, 0x5a5afc00u, 0x5a5acb00u}},
{0x8001u, {0x5a5afc00u, 0x5a5afe00u, 0x5a5afc00u, 0x5a5afe00u}},
{0x8200u, {0x5a5afc00u, 0x5a5afe00u, 0x5a5afc00u, 0x5a5afe00u}},
{0x83ffu, {0x5a5afc00u, 0x5a5afe00u, 0x5a5afc00u, 0x5a5afe00u}},
{0x0400u, {0x5a5acb00u, 0x5a5acb00u, 0x5a5acb00u, 0x5a5acb00u}},
{0x8400u, {0x5a5afe00u, 0x5a5afe00u, 0x5a5afe00u, 0x5a5afe00u}},
{0x0000u, {0x5a5afc00u, 0x5a5afc00u, 0x5a5afc00u, 0x5a5afc00u}},
{0x3c00u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0xbc00u, {0x5a5afe00u, 0x5a5afe00u, 0x5a5afe00u, 0x5a5afe00u}},
{0x4900u, {0x5a5a42a5u, 0x5a5a42a5u, 0x5a5a42a5u, 0x5a5a42a5u}},
{0x0801u, {0x5a5aca80u, 0x5a5aca80u, 0x5a5aca80u, 0x5a5aca80u}},
};
constexpr Vector RcpRows[] = {
{0x0001u, {0x5a5a7c00u, 0x5a5a7c00u, 0x5a5a7c00u, 0x5a5a7c00u}},
{0x0200u, {0x5a5a7c00u, 0x5a5a7800u, 0x5a5a7c00u, 0x5a5a7800u}},
{0x03ffu, {0x5a5a7c00u, 0x5a5a7401u, 0x5a5a7c00u, 0x5a5a7401u}},
{0x8001u, {0x5a5afc00u, 0x5a5afc00u, 0x5a5afc00u, 0x5a5afc00u}},
{0x8200u, {0x5a5afc00u, 0x5a5af800u, 0x5a5afc00u, 0x5a5af800u}},
{0x83ffu, {0x5a5afc00u, 0x5a5af401u, 0x5a5afc00u, 0x5a5af401u}},
{0x0400u, {0x5a5a7400u, 0x5a5a7400u, 0x5a5a7400u, 0x5a5a7400u}},
{0x8400u, {0x5a5af400u, 0x5a5af400u, 0x5a5af400u, 0x5a5af400u}},
{0x0000u, {0x5a5a7c00u, 0x5a5a7c00u, 0x5a5a7c00u, 0x5a5a7c00u}},
{0x3c00u, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0xbc00u, {0x5a5abc00u, 0x5a5abc00u, 0x5a5abc00u, 0x5a5abc00u}},
{0x7bffu, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0100u, 0x5a5a0100u}},
{0x7401u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a03ffu, 0x5a5a03ffu}},
{0xfbffu, {0x5a5a8000u, 0x5a5a8000u, 0x5a5a8100u, 0x5a5a8100u}},
{0xf404u, {0x5a5a8000u, 0x5a5a8000u, 0x5a5a83fcu, 0x5a5a83fcu}},
{0x7800u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0200u, 0x5a5a0200u}},
};
constexpr Vector RsqRows[] = {
{0x0001u, {0x5a5a7c00u, 0x5a5a6c00u, 0x5a5a7c00u, 0x5a5a6c00u}},
{0x0200u, {0x5a5a7c00u, 0x5a5a59a8u, 0x5a5a7c00u, 0x5a5a59a8u}},
{0x03ffu, {0x5a5a7c00u, 0x5a5a5801u, 0x5a5a7c00u, 0x5a5a5801u}},
{0x8001u, {0x5a5afc00u, 0x5a5afe00u, 0x5a5afc00u, 0x5a5afe00u}},
{0x8200u, {0x5a5afc00u, 0x5a5afe00u, 0x5a5afc00u, 0x5a5afe00u}},
{0x83ffu, {0x5a5afc00u, 0x5a5afe00u, 0x5a5afc00u, 0x5a5afe00u}},
{0x0400u, {0x5a5a5800u, 0x5a5a5800u, 0x5a5a5800u, 0x5a5a5800u}},
{0x8400u, {0x5a5afe00u, 0x5a5afe00u, 0x5a5afe00u, 0x5a5afe00u}},
{0x0000u, {0x5a5a7c00u, 0x5a5a7c00u, 0x5a5a7c00u, 0x5a5a7c00u}},
{0x3c00u, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0xbc00u, {0x5a5afe00u, 0x5a5afe00u, 0x5a5afe00u, 0x5a5afe00u}},
{0x4400u, {0x5a5a3800u, 0x5a5a3800u, 0x5a5a3800u, 0x5a5a3800u}},
};
constexpr Vector SinRows[] = {
{0x0001u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0006u}},
{0x0200u, {0x5a5a0000u, 0x5a5a0a48u, 0x5a5a0000u, 0x5a5a0a48u}},
{0x03ffu, {0x5a5a0000u, 0x5a5a0e47u, 0x5a5a0000u, 0x5a5a0e47u}},
{0x8001u, {0x5a5a8000u, 0x5a5a8000u, 0x5a5a8000u, 0x5a5a8006u}},
{0x8200u, {0x5a5a8000u, 0x5a5a8a48u, 0x5a5a8000u, 0x5a5a8a48u}},
{0x83ffu, {0x5a5a8000u, 0x5a5a8e47u, 0x5a5a8000u, 0x5a5a8e47u}},
{0x0400u, {0x5a5a0e48u, 0x5a5a0e48u, 0x5a5a0e48u, 0x5a5a0e48u}},
{0x8400u, {0x5a5a8e48u, 0x5a5a8e48u, 0x5a5a8e48u, 0x5a5a8e48u}},
{0x0000u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x3c00u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0xbc00u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x00a0u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a03edu}},
{0x80a0u, {0x5a5a8000u, 0x5a5a8000u, 0x5a5a8000u, 0x5a5a83edu}},
{0x3800u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
};
constexpr Vector SqrtRows[] = {
{0x0001u, {0x5a5a0000u, 0x5a5a0c00u, 0x5a5a0000u, 0x5a5a0c00u}},
{0x0200u, {0x5a5a0000u, 0x5a5a1da8u, 0x5a5a0000u, 0x5a5a1da8u}},
{0x03ffu, {0x5a5a0000u, 0x5a5a1fffu, 0x5a5a0000u, 0x5a5a1fffu}},
{0x8001u, {0x5a5a8000u, 0x5a5afe00u, 0x5a5a8000u, 0x5a5afe00u}},
{0x8200u, {0x5a5a8000u, 0x5a5afe00u, 0x5a5a8000u, 0x5a5afe00u}},
{0x83ffu, {0x5a5a8000u, 0x5a5afe00u, 0x5a5a8000u, 0x5a5afe00u}},
{0x0400u, {0x5a5a2000u, 0x5a5a2000u, 0x5a5a2000u, 0x5a5a2000u}},
{0x8400u, {0x5a5afe00u, 0x5a5afe00u, 0x5a5afe00u, 0x5a5afe00u}},
{0x0000u, {0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u, 0x5a5a0000u}},
{0x3c00u, {0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u, 0x5a5a3c00u}},
{0xbc00u, {0x5a5afe00u, 0x5a5afe00u, 0x5a5afe00u, 0x5a5afe00u}},
{0x4400u, {0x5a5a4000u, 0x5a5a4000u, 0x5a5a4000u, 0x5a5a4000u}},
};
constexpr Case Cases[] = {
{"v_ceil_f16", 0x7e14b904u, CeilRows},
{"v_cvt_f32_f16", 0x7e141704u, CvtF32Rows},
{"v_cvt_norm_i16_f16", 0x7e14c704u, CvtNormI16Rows},
{"v_cvt_norm_u16_f16", 0x7e14c904u, CvtNormU16Rows},
{"v_exp_f16", 0x7e14b104u, ExpRows},
{"v_floor_f16", 0x7e14b704u, FloorRows},
{"v_fract_f16", 0x7e14bf04u, FractRows},
{"v_frexp_exp_i16_f16", 0x7e14b504u, FrexpExpRows},
{"v_frexp_mant_f16", 0x7e14b304u, FrexpMantRows},
{"v_log_f16", 0x7e14af04u, LogRows},
{"v_rcp_f16", 0x7e14a904u, RcpRows},
{"v_rsq_f16", 0x7e14ad04u, RsqRows},
{"v_sin_f16", 0x7e14c104u, SinRows},
{"v_sqrt_f16", 0x7e14ab04u, SqrtRows},
};
constexpr std::uint32_t CaseCount = std::size(Cases);
std::array<Shader, CaseCount> Shaders{};
std::array<std::uint32_t, 4> BufferDescriptor(const void* data, std::uint32_t bytes) {
const auto address = reinterpret_cast<std::uintptr_t>(data);
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu), bytes, 0x31016facu};
}
std::string Hex(std::uint32_t value) {
char text[16];
std::snprintf(text, sizeof(text), "0x%08x", value);
return text;
}
void Run(AgcDriver::VulkanDevice& device, const Case& item, const Shader& shader, std::uint32_t denorm16, bool ieee) {
Input.fill(0u);
for (std::uint32_t row = 0; row < item.rows.size(); ++row) {
Input[row * Inputs] = item.rows[row].half;
}
Output.fill(0xdeadbeefu);
std::vector<std::uint32_t> userData(8, 0u);
const auto input = BufferDescriptor(Input.data(), static_cast<std::uint32_t>(Input.size() * 4u));
const auto output = BufferDescriptor(Output.data(), static_cast<std::uint32_t>(Output.size() * 4u));
std::copy(input.begin(), input.end(), userData.begin());
std::copy(output.begin(), output.end(), userData.begin() + 4);
const std::span<const std::uint32_t> code(shader.words);
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{
{ShaderStage::Compute, reinterpret_cast<std::uintptr_t>(code.data()), code, 0, {}},
{32, 0, userData, compute, std::nullopt, std::nullopt, memory},
device.Target(),
{0, 0, 0, 128}
};
request.useCache = false;
request.context.floatMode = ShaderRecompiler::ShaderFloatMode{(3u << 4u) | (denorm16 << 6u), true, ieee, false};
const auto result = ShaderRecompiler::Recompile(request);
device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast<std::uintptr_t>(code.data()));
device.WaitIdle();
}
void Check(const Case& item, std::uint32_t denorm16, bool ieee) {
for (std::uint32_t row = 0; row < item.rows.size(); ++row) {
const auto actual = Output[row];
const auto expected = item.rows[row].expected[denorm16];
Require(actual == expected, std::string(item.name) + " denorm16 " + std::to_string(denorm16) + (ieee ? " ieee" : " no ieee") + " input " +
Hex(item.rows[row].half) + " is " + Hex(actual) + ", expected " + Hex(expected));
}
}
}
int main() {
try {
const auto device = OpenVulkanTestDevice();
if (!device) return VulkanTestSkipped;
for (std::uint32_t index = 0; index < CaseCount; ++index) {
Shaders[index].words = {0x34020084u, 0x34040082u, 0xe0381000u, 0x80000401u, 0xbf8c3f70u, 0x7e1402ffu, 0x5a5a5a5au, Cases[index].word,
0xe0701000u, 0x80010a02u, 0xbf810000u};
for (std::uint32_t denorm16 = 0u; denorm16 < 4u; ++denorm16) {
for (const bool ieee : {false, true}) {
Run(*device, Cases[index], Shaders[index], denorm16, ieee);
Check(Cases[index], denorm16, ieee);
}
}
}
std::puts("f16 denormal flush tests passed");
return 0;
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';
return 1;
}
}

View File

@ -61,6 +61,10 @@ private:
void write16Bits(const RdnaOperand& operand, IrU32 value);
void writeF16(const RdnaOperand& operand, IrF32 value, std::initializer_list<IrValue*> sources);
bool directedF16Rounding() const;
IrU32 flushF16Bits(IrU32 bits, std::uint32_t keepBit);
IrU32 flushF16Input(IrU32 bits);
IrU32 flushF16Result(IrU32 bits);
IrF32 flushF16InputValue(IrF32 value);
IrU32 convertF32ToF16Bits(IrF32 value, std::initializer_list<IrValue*> sources);
void writeF16Conversion(const RdnaOperand& operand, IrF32 value, std::initializer_list<IrValue*> sources);
IrU32 readU32(const RdnaOperand& operand);

View File

@ -123,7 +123,7 @@ void TranslationContext::vCvtF16F32(const RdnaInstruction& inst) {
}
void TranslationContext::vCvtF32F16(const RdnaInstruction& inst) {
const IrF32 value = readF16AsF32(sourceAt(inst, 0u));
const IrF32 value = flushF16InputValue(readF16AsF32(sourceAt(inst, 0u)));
writeOperand(inst.destination, &value.Value());
}

View File

@ -104,12 +104,13 @@ bool TranslationContext::packedFloat16(const RdnaInstruction& inst, IrOpcode opc
bool TranslationContext::float16Unary(const RdnaInstruction& inst, IrOpcode opcode) {
const RdnaOperand& operand = sourceAt(inst, 0u);
const IrF32 argument = readF16AsF32(operand);
const IrF32 argument = flushF16InputValue(readF16AsF32(operand));
const auto [result, invalid] = unaryFloatSpecials(opcode, argument, IrF32(ir.Emit(opcode, IrType::F32, {&argument.Value()})));
IrU32 bits = packHalf2x16(applyF16ResultModifiers(inst.destination, clampF16Overflow(result, {&argument.Value()})), IrF32(ir.ConstantF32(0.0f)));
if (opcode == IrOpcode::FPFract32) {
bits = IrU32(ir.Select(ir.IEqual(bits.Value(), ir.Constant(0x3c00u)), ir.Constant(0x3bffu), bits.Value()));
}
bits = flushF16Result(bits);
const IrU32 half = readF16Bits(operand);
const IrU1 nan(ir.UGreaterThan(ir.BitwiseAnd(half.Value(), ir.Constant(0x7fffu)), ir.Constant(0x7c00u)));
const IrU32 special(ir.Select(nan.Value(), quietNan16(half).Value(), ir.Constant(0xfe00u)));
@ -344,11 +345,7 @@ IrU32 TranslationContext::normF32(IrU32 bits, bool signedValue) {
bool TranslationContext::vLdexpF16(const RdnaInstruction& inst) {
const IrU32 source = readF16Bits(sourceAt(inst, 0u));
IrU32 bits = source;
if (floatMode.has_value() && (floatMode->floatMode & 0x40u) == 0u) {
const IrU1 tiny(ir.ULessThan(ir.BitwiseAnd(bits.Value(), ir.Constant(0x7fffu)), ir.Constant(0x0400u)));
bits = IrU32(ir.Select(tiny.Value(), ir.BitwiseAnd(bits.Value(), ir.Constant(0x8000u)), bits.Value()));
}
const IrU32 bits = flushF16Input(source);
const IrF16 half(ir.Emit(IrOpcode::BitCastF16U16, IrType::F16, {&ir.Emit(IrOpcode::ConvertU16U32, IrType::U16, {&bits.Value()})}));
const IrF32 value(ir.Emit(IrOpcode::ConvertF32F16, IrType::F32, {&half.Value()}));
const RdnaOperand& exponentSource = sourceAt(inst, 1u);
@ -368,8 +365,8 @@ bool TranslationContext::vLdexpF16(const RdnaInstruction& inst) {
}
bool TranslationContext::vFrexpF16(const RdnaInstruction& inst, bool exponent) {
const IrU32 bits = readF16Bits(sourceAt(inst, 0u));
const IrF32 value = readF16AsF32(sourceAt(inst, 0u));
const IrU32 bits = flushF16Input(readF16Bits(sourceAt(inst, 0u)));
const IrF32 value = flushF16InputValue(readF16AsF32(sourceAt(inst, 0u)));
const IrU32 word(ir.BitCastU32(value.Value()));
const IrU1 special(ir.LogicalOr(ir.IEqual(ir.BitwiseAnd(bits.Value(), ir.Constant(0x7fffu)), ir.Constant(0u)),
ir.Emit(IrOpcode::UGreaterThanEqual32, IrType::U1, {&ir.BitwiseAnd(bits.Value(), ir.Constant(0x7fffu)), &ir.Constant(0x7c00u)})));
@ -388,7 +385,7 @@ bool TranslationContext::vFrexpF16(const RdnaInstruction& inst, bool exponent) {
}
bool TranslationContext::vCvtNormF16(const RdnaInstruction& inst, bool signedValue) {
write16Bits(inst.destination, normF16(readF16Bits(sourceAt(inst, 0u)), signedValue));
write16Bits(inst.destination, normF16(flushF16Input(readF16Bits(sourceAt(inst, 0u))), signedValue));
return true;
}

View File

@ -160,6 +160,27 @@ bool TranslationContext::directedF16Rounding() const {
return floatMode.has_value() && ((floatMode->floatMode >> 2u) & 3u) != 0u && ((floatMode->floatMode >> 6u) & 3u) == 3u;
}
IrU32 TranslationContext::flushF16Bits(IrU32 bits, std::uint32_t keepBit) {
if (!floatMode.has_value() || (floatMode->floatMode & keepBit) != 0u) return bits;
const IrU1 tiny(ir.ULessThan(ir.BitwiseAnd(bits.Value(), ir.Constant(0x7fffu)), ir.Constant(0x0400u)));
return IrU32(ir.Select(tiny.Value(), ir.BitwiseAnd(bits.Value(), ir.Constant(0x8000u)), bits.Value()));
}
IrU32 TranslationContext::flushF16Input(IrU32 bits) {
return flushF16Bits(bits, 0x40u);
}
IrU32 TranslationContext::flushF16Result(IrU32 bits) {
return flushF16Bits(bits, 0x80u);
}
IrF32 TranslationContext::flushF16InputValue(IrF32 value) {
if (!floatMode.has_value() || (floatMode->floatMode & 0x40u) != 0u) return value;
const IrU32 bits(ir.BitCastU32(value.Value()));
const IrU1 tiny(ir.ULessThan(ir.BitwiseAnd(bits.Value(), ir.Constant(0x7fffffffu)), ir.Constant(0x38800000u)));
return IrF32(ir.BitCastF32(ir.Select(tiny.Value(), ir.BitwiseAnd(bits.Value(), ir.Constant(0x80000000u)), bits.Value())));
}
IrU32 TranslationContext::convertF32ToF16Bits(IrF32 value, std::initializer_list<IrValue*> sources) {
const IrF16 half(ir.Emit(IrOpcode::ConvertF16F32, IrType::F16, {&clampF16Overflow(value, sources).Value()}));
const IrU32 native(ir.Emit(IrOpcode::ConvertU32U16, IrType::U32, {&ir.Emit(IrOpcode::BitCastU16F16, IrType::U16, {&half.Value()})}));

File diff suppressed because one or more lines are too long