shader_recompiler: Implement linear interpolation support (#3055)
Some checks are pending
Build and Release / reuse (push) Waiting to run
Build and Release / clang-format (push) Waiting to run
Build and Release / get-info (push) Waiting to run
Build and Release / windows-sdl (push) Blocked by required conditions
Build and Release / windows-qt (push) Blocked by required conditions
Build and Release / macos-sdl (push) Blocked by required conditions
Build and Release / macos-qt (push) Blocked by required conditions
Build and Release / linux-sdl (push) Blocked by required conditions
Build and Release / linux-qt (push) Blocked by required conditions
Build and Release / linux-sdl-gcc (push) Blocked by required conditions
Build and Release / linux-qt-gcc (push) Blocked by required conditions
Build and Release / pre-release (push) Blocked by required conditions

This commit is contained in:
TheTurtle 2025-06-08 22:46:34 +03:00 committed by GitHub
parent 5004e41100
commit 8ffcfc87bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 425 additions and 375 deletions

View file

@ -307,7 +307,9 @@ void EmitContext::DefineInterpolatedAttribs() {
const Id p2{OpCompositeExtract(F32[4], p_array, 2U)}; const Id p2{OpCompositeExtract(F32[4], p_array, 2U)};
const Id p10{OpFSub(F32[4], p1, p0)}; const Id p10{OpFSub(F32[4], p1, p0)};
const Id p20{OpFSub(F32[4], p2, p0)}; const Id p20{OpFSub(F32[4], p2, p0)};
const Id bary_coord{OpLoad(F32[3], gl_bary_coord_id)}; const Id bary_coord{OpLoad(F32[3], IsLinear(info.interp_qualifiers[i])
? bary_coord_linear_id
: bary_coord_persp_id)};
const Id bary_coord_y{OpCompositeExtract(F32[1], bary_coord, 1)}; const Id bary_coord_y{OpCompositeExtract(F32[1], bary_coord, 1)};
const Id bary_coord_z{OpCompositeExtract(F32[1], bary_coord, 2)}; const Id bary_coord_z{OpCompositeExtract(F32[1], bary_coord, 2)};
const Id p10_y{OpVectorTimesScalar(F32[4], p10, bary_coord_y)}; const Id p10_y{OpVectorTimesScalar(F32[4], p10, bary_coord_y)};
@ -411,8 +413,14 @@ void EmitContext::DefineInputs() {
DefineVariable(U1[1], spv::BuiltIn::FrontFacing, spv::StorageClass::Input); DefineVariable(U1[1], spv::BuiltIn::FrontFacing, spv::StorageClass::Input);
} }
if (profile.needs_manual_interpolation) { if (profile.needs_manual_interpolation) {
gl_bary_coord_id = if (info.has_perspective_interp) {
DefineVariable(F32[3], spv::BuiltIn::BaryCoordKHR, spv::StorageClass::Input); bary_coord_persp_id =
DefineVariable(F32[3], spv::BuiltIn::BaryCoordKHR, spv::StorageClass::Input);
}
if (info.has_linear_interp) {
bary_coord_linear_id = DefineVariable(F32[3], spv::BuiltIn::BaryCoordNoPerspKHR,
spv::StorageClass::Input);
}
} }
for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) { for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) {
const auto& input = runtime_info.fs_info.inputs[i]; const auto& input = runtime_info.fs_info.inputs[i];
@ -435,9 +443,12 @@ void EmitContext::DefineInputs() {
} else { } else {
attr_id = DefineInput(type, semantic); attr_id = DefineInput(type, semantic);
Name(attr_id, fmt::format("fs_in_attr{}", semantic)); Name(attr_id, fmt::format("fs_in_attr{}", semantic));
}
if (input.is_flat) { if (input.is_flat) {
Decorate(attr_id, spv::Decoration::Flat); Decorate(attr_id, spv::Decoration::Flat);
} else if (IsLinear(info.interp_qualifiers[i])) {
Decorate(attr_id, spv::Decoration::NoPerspective);
}
} }
input_params[semantic] = input_params[semantic] =
GetAttributeInfo(AmdGpu::NumberFormat::Float, attr_id, num_components, false); GetAttributeInfo(AmdGpu::NumberFormat::Float, attr_id, num_components, false);

View file

@ -293,8 +293,8 @@ public:
Id shared_memory_u32_type{}; Id shared_memory_u32_type{};
Id interpolate_func{}; Id bary_coord_persp_id{};
Id gl_bary_coord_id{}; Id bary_coord_linear_id{};
struct TextureDefinition { struct TextureDefinition {
const VectorIds* data_types; const VectorIds* data_types;

View file

@ -605,11 +605,12 @@ public:
Info& info_, const RuntimeInfo& runtime_info_, const Profile& profile_) Info& info_, const RuntimeInfo& runtime_info_, const Profile& profile_)
: stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, : stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_},
syntax_list{syntax_list_}, inst_list{inst_list_}, info{info_}, syntax_list{syntax_list_}, inst_list{inst_list_}, info{info_},
runtime_info{runtime_info_}, profile{profile_} { runtime_info{runtime_info_}, profile{profile_},
translator{info_, runtime_info_, profile_} {
Visit(root_stmt, nullptr, nullptr); Visit(root_stmt, nullptr, nullptr);
IR::Block& first_block{*syntax_list.front().data.block}; IR::Block* first_block = syntax_list.front().data.block;
Translator{&first_block, info, runtime_info, profile}.EmitPrologue(); translator.EmitPrologue(first_block);
} }
private: private:
@ -637,8 +638,8 @@ private:
current_block->has_multiple_predecessors = stmt.block->num_predecessors > 1; current_block->has_multiple_predecessors = stmt.block->num_predecessors > 1;
const u32 start = stmt.block->begin_index; const u32 start = stmt.block->begin_index;
const u32 size = stmt.block->end_index - start + 1; const u32 size = stmt.block->end_index - start + 1;
Translate(current_block, stmt.block->begin, inst_list.subspan(start, size), translator.Translate(current_block, stmt.block->begin,
info, runtime_info, profile); inst_list.subspan(start, size));
} }
break; break;
} }
@ -820,6 +821,7 @@ private:
Info& info; Info& info;
const RuntimeInfo& runtime_info; const RuntimeInfo& runtime_info;
const Profile& profile; const Profile& profile;
Translator translator;
}; };
} // Anonymous namespace } // Anonymous namespace

View file

@ -21,16 +21,60 @@
namespace Shader::Gcn { namespace Shader::Gcn {
static u32 next_vgpr_num; Translator::Translator(Info& info_, const RuntimeInfo& runtime_info_, const Profile& profile_)
static std::unordered_map<u32, IR::VectorReg> vgpr_map; : info{info_}, runtime_info{runtime_info_}, profile{profile_},
next_vgpr_num{runtime_info.num_allocated_vgprs} {
Translator::Translator(IR::Block* block_, Info& info_, const RuntimeInfo& runtime_info_, if (info.l_stage == LogicalStage::Fragment) {
const Profile& profile_) dst_frag_vreg = GatherInterpQualifiers();
: ir{*block_, block_->begin()}, info{info_}, runtime_info{runtime_info_}, profile{profile_} { }
next_vgpr_num = vgpr_map.empty() ? runtime_info.num_allocated_vgprs : next_vgpr_num;
} }
void Translator::EmitPrologue() { IR::VectorReg Translator::GatherInterpQualifiers() {
u32 dst_vreg{};
if (runtime_info.fs_info.addr_flags.persp_sample_ena) {
vgpr_to_interp[dst_vreg++] = IR::Interpolation::PerspectiveSample; // I
vgpr_to_interp[dst_vreg++] = IR::Interpolation::PerspectiveSample; // J
info.has_perspective_interp = true;
}
if (runtime_info.fs_info.addr_flags.persp_center_ena) {
vgpr_to_interp[dst_vreg++] = IR::Interpolation::PerspectiveCenter; // I
vgpr_to_interp[dst_vreg++] = IR::Interpolation::PerspectiveCenter; // J
info.has_perspective_interp = true;
}
if (runtime_info.fs_info.addr_flags.persp_centroid_ena) {
vgpr_to_interp[dst_vreg++] = IR::Interpolation::PerspectiveCentroid; // I
vgpr_to_interp[dst_vreg++] = IR::Interpolation::PerspectiveCentroid; // J
info.has_perspective_interp = true;
}
if (runtime_info.fs_info.addr_flags.persp_pull_model_ena) {
++dst_vreg; // I/W
++dst_vreg; // J/W
++dst_vreg; // 1/W
}
if (runtime_info.fs_info.addr_flags.linear_sample_ena) {
vgpr_to_interp[dst_vreg++] = IR::Interpolation::LinearSample; // I
vgpr_to_interp[dst_vreg++] = IR::Interpolation::LinearSample; // J
info.has_linear_interp = true;
}
if (runtime_info.fs_info.addr_flags.linear_center_ena) {
vgpr_to_interp[dst_vreg++] = IR::Interpolation::LinearCenter; // I
vgpr_to_interp[dst_vreg++] = IR::Interpolation::LinearCenter; // J
info.has_linear_interp = true;
}
if (runtime_info.fs_info.addr_flags.linear_centroid_ena) {
vgpr_to_interp[dst_vreg++] = IR::Interpolation::LinearCentroid; // I
vgpr_to_interp[dst_vreg++] = IR::Interpolation::LinearCentroid; // J
info.has_linear_interp = true;
}
if (runtime_info.fs_info.addr_flags.line_stipple_tex_ena) {
++dst_vreg;
}
return IR::VectorReg(dst_vreg);
}
void Translator::EmitPrologue(IR::Block* first_block) {
ir = IR::IREmitter(*first_block, first_block->begin());
ir.Prologue(); ir.Prologue();
ir.SetExec(ir.Imm1(true)); ir.SetExec(ir.Imm1(true));
@ -60,39 +104,7 @@ void Translator::EmitPrologue() {
} }
break; break;
case LogicalStage::Fragment: case LogicalStage::Fragment:
dst_vreg = IR::VectorReg::V0; dst_vreg = dst_frag_vreg;
if (runtime_info.fs_info.addr_flags.persp_sample_ena) {
++dst_vreg; // I
++dst_vreg; // J
}
if (runtime_info.fs_info.addr_flags.persp_center_ena) {
++dst_vreg; // I
++dst_vreg; // J
}
if (runtime_info.fs_info.addr_flags.persp_centroid_ena) {
++dst_vreg; // I
++dst_vreg; // J
}
if (runtime_info.fs_info.addr_flags.persp_pull_model_ena) {
++dst_vreg; // I/W
++dst_vreg; // J/W
++dst_vreg; // 1/W
}
if (runtime_info.fs_info.addr_flags.linear_sample_ena) {
++dst_vreg; // I
++dst_vreg; // J
}
if (runtime_info.fs_info.addr_flags.linear_center_ena) {
++dst_vreg; // I
++dst_vreg; // J
}
if (runtime_info.fs_info.addr_flags.linear_centroid_ena) {
++dst_vreg; // I
++dst_vreg; // J
}
if (runtime_info.fs_info.addr_flags.line_stipple_tex_ena) {
++dst_vreg;
}
if (runtime_info.fs_info.addr_flags.pos_x_float_ena) { if (runtime_info.fs_info.addr_flags.pos_x_float_ena) {
if (runtime_info.fs_info.en_flags.pos_x_float_ena) { if (runtime_info.fs_info.en_flags.pos_x_float_ena) {
ir.SetVectorReg(dst_vreg++, ir.GetAttribute(IR::Attribute::FragCoord, 0)); ir.SetVectorReg(dst_vreg++, ir.GetAttribute(IR::Attribute::FragCoord, 0));
@ -543,6 +555,26 @@ void Translator::LogMissingOpcode(const GcnInst& inst) {
info.translation_failed = true; info.translation_failed = true;
} }
void Translator::Translate(IR::Block* block, u32 pc, std::span<const GcnInst> inst_list) {
if (inst_list.empty()) {
return;
}
ir = IR::IREmitter{*block, block->begin()};
for (const auto& inst : inst_list) {
pc += inst.length;
// Special case for emitting fetch shader.
if (inst.opcode == Opcode::S_SWAPPC_B64) {
ASSERT(info.stage == Stage::Vertex || info.stage == Stage::Export ||
info.stage == Stage::Local);
EmitFetch(inst);
continue;
}
TranslateInstruction(inst, pc);
}
}
void Translator::TranslateInstruction(const GcnInst& inst, const u32 pc) { void Translator::TranslateInstruction(const GcnInst& inst, const u32 pc) {
// Emit instructions for each category. // Emit instructions for each category.
switch (inst.category) { switch (inst.category) {
@ -577,25 +609,4 @@ void Translator::TranslateInstruction(const GcnInst& inst, const u32 pc) {
} }
} }
void Translate(IR::Block* block, u32 pc, std::span<const GcnInst> inst_list, Info& info,
const RuntimeInfo& runtime_info, const Profile& profile) {
if (inst_list.empty()) {
return;
}
Translator translator{block, info, runtime_info, profile};
for (const auto& inst : inst_list) {
pc += inst.length;
// Special case for emitting fetch shader.
if (inst.opcode == Opcode::S_SWAPPC_B64) {
ASSERT(info.stage == Stage::Vertex || info.stage == Stage::Export ||
info.stage == Stage::Local);
translator.EmitFetch(inst);
continue;
}
translator.TranslateInstruction(inst, pc);
}
}
} // namespace Shader::Gcn } // namespace Shader::Gcn

View file

@ -53,15 +53,17 @@ enum class NegateMode : u32 {
Result, Result,
}; };
static constexpr size_t MaxInterpVgpr = 16;
class Translator { class Translator {
public: public:
explicit Translator(IR::Block* block_, Info& info, const RuntimeInfo& runtime_info, explicit Translator(Info& info, const RuntimeInfo& runtime_info, const Profile& profile);
const Profile& profile);
void Translate(IR::Block* block, u32 pc, std::span<const GcnInst> inst_list);
void TranslateInstruction(const GcnInst& inst, u32 pc); void TranslateInstruction(const GcnInst& inst, u32 pc);
// Instruction categories // Instruction categories
void EmitPrologue(); void EmitPrologue(IR::Block* first_block);
void EmitFetch(const GcnInst& inst); void EmitFetch(const GcnInst& inst);
void EmitExport(const GcnInst& inst); void EmitExport(const GcnInst& inst);
void EmitFlowControl(u32 pc, const GcnInst& inst); void EmitFlowControl(u32 pc, const GcnInst& inst);
@ -326,16 +328,18 @@ private:
void LogMissingOpcode(const GcnInst& inst); void LogMissingOpcode(const GcnInst& inst);
IR::VectorReg GetScratchVgpr(u32 offset); IR::VectorReg GetScratchVgpr(u32 offset);
IR::VectorReg GatherInterpQualifiers();
private: private:
IR::IREmitter ir; IR::IREmitter ir;
Info& info; Info& info;
const RuntimeInfo& runtime_info; const RuntimeInfo& runtime_info;
const Profile& profile; const Profile& profile;
u32 next_vgpr_num;
std::unordered_map<u32, IR::VectorReg> vgpr_map;
std::array<IR::Interpolation, MaxInterpVgpr> vgpr_to_interp{};
IR::VectorReg dst_frag_vreg{};
bool opcode_missing = false; bool opcode_missing = false;
}; };
void Translate(IR::Block* block, u32 block_base, std::span<const GcnInst> inst_list, Info& info,
const RuntimeInfo& runtime_info, const Profile& profile);
} // namespace Shader::Gcn } // namespace Shader::Gcn

View file

@ -22,13 +22,14 @@ void Translator::EmitVectorInterpolation(const GcnInst& inst) {
// VINTRP // VINTRP
void Translator::V_INTERP_P2_F32(const GcnInst& inst) { void Translator::V_INTERP_P2_F32(const GcnInst& inst) {
auto& attr = runtime_info.fs_info.inputs.at(inst.control.vintrp.attr); const auto& attr = runtime_info.fs_info.inputs.at(inst.control.vintrp.attr);
info.interp_qualifiers[attr.param_index] = vgpr_to_interp[inst.src[0].code];
const IR::Attribute attrib{IR::Attribute::Param0 + attr.param_index}; const IR::Attribute attrib{IR::Attribute::Param0 + attr.param_index};
SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan)); SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan));
} }
void Translator::V_INTERP_MOV_F32(const GcnInst& inst) { void Translator::V_INTERP_MOV_F32(const GcnInst& inst) {
auto& attr = runtime_info.fs_info.inputs.at(inst.control.vintrp.attr); const auto& attr = runtime_info.fs_info.inputs.at(inst.control.vintrp.attr);
const IR::Attribute attrib{IR::Attribute::Param0 + attr.param_index}; const IR::Attribute attrib{IR::Attribute::Param0 + attr.param_index};
SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan)); SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan));
} }

View file

@ -193,6 +193,8 @@ struct Info {
PersistentSrtInfo srt_info; PersistentSrtInfo srt_info;
std::vector<u32> flattened_ud_buf; std::vector<u32> flattened_ud_buf;
std::array<IR::Interpolation, 32> interp_qualifiers{};
IR::ScalarReg tess_consts_ptr_base = IR::ScalarReg::Max; IR::ScalarReg tess_consts_ptr_base = IR::ScalarReg::Max;
s32 tess_consts_dword_offset = -1; s32 tess_consts_dword_offset = -1;
@ -206,6 +208,8 @@ struct Info {
bool has_discard{}; bool has_discard{};
bool has_image_gather{}; bool has_image_gather{};
bool has_image_query{}; bool has_image_query{};
bool has_perspective_interp{};
bool has_linear_interp{};
bool uses_atomic_float_min_max{}; bool uses_atomic_float_min_max{};
bool uses_lane_id{}; bool uses_lane_id{};
bool uses_group_quad{}; bool uses_group_quad{};

View file

@ -83,6 +83,16 @@ enum class Attribute : u64 {
Max, Max,
}; };
enum class Interpolation {
Invalid = 0,
PerspectiveSample = 1,
PerspectiveCenter = 2,
PerspectiveCentroid = 3,
LinearSample = 4,
LinearCenter = 5,
LinearCentroid = 6,
};
constexpr size_t NumAttributes = static_cast<size_t>(Attribute::Max); constexpr size_t NumAttributes = static_cast<size_t>(Attribute::Max);
constexpr size_t NumRenderTargets = 8; constexpr size_t NumRenderTargets = 8;
constexpr size_t NumParams = 32; constexpr size_t NumParams = 32;
@ -104,6 +114,15 @@ constexpr bool IsMrt(Attribute attribute) noexcept {
return attribute >= Attribute::RenderTarget0 && attribute <= Attribute::RenderTarget7; return attribute >= Attribute::RenderTarget0 && attribute <= Attribute::RenderTarget7;
} }
constexpr bool IsLinear(Interpolation interp) noexcept {
return interp >= Interpolation::LinearSample && interp <= Interpolation::LinearCentroid;
}
constexpr bool IsPerspective(Interpolation interp) noexcept {
return interp >= Interpolation::PerspectiveSample &&
interp <= Interpolation::PerspectiveCentroid;
}
[[nodiscard]] std::string NameOf(Attribute attribute); [[nodiscard]] std::string NameOf(Attribute attribute);
[[nodiscard]] constexpr Attribute operator+(Attribute attr, int num) { [[nodiscard]] constexpr Attribute operator+(Attribute attr, int num) {

View file

@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <array> #include <array>
#include <bit>
#include <source_location> #include <source_location>
#include <boost/container/small_vector.hpp> #include <boost/container/small_vector.hpp>
#include "common/assert.h" #include "common/assert.h"

View file

@ -6,7 +6,6 @@
#include <cstring> #include <cstring>
#include <type_traits> #include <type_traits>
#include "shader_recompiler/info.h"
#include "shader_recompiler/ir/attribute.h" #include "shader_recompiler/ir/attribute.h"
#include "shader_recompiler/ir/basic_block.h" #include "shader_recompiler/ir/basic_block.h"
#include "shader_recompiler/ir/condition.h" #include "shader_recompiler/ir/condition.h"
@ -17,6 +16,7 @@ namespace Shader::IR {
class IREmitter { class IREmitter {
public: public:
explicit IREmitter() = default;
explicit IREmitter(Block& block_) : block{&block_}, insertion_point{block->end()} {} explicit IREmitter(Block& block_) : block{&block_}, insertion_point{block->end()} {}
explicit IREmitter(Block& block_, Block::iterator insertion_point_) explicit IREmitter(Block& block_, Block::iterator insertion_point_)
: block{&block_}, insertion_point{insertion_point_} {} : block{&block_}, insertion_point{insertion_point_} {}

View file

@ -7,7 +7,7 @@
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/enum.h" #include "common/enum.h"
#include "common/types.h" #include "common/types.h"
#include "video_core/amdgpu/types.h" #include "video_core/amdgpu/pixel_format.h"
namespace Shader::IR { namespace Shader::IR {

View file

@ -5,34 +5,310 @@
#include <string_view> #include <string_view>
#include <fmt/format.h> #include <fmt/format.h>
#include "common/assert.h"
#include "common/types.h" #include "common/types.h"
#include "video_core/amdgpu/types.h"
namespace AmdGpu { namespace AmdGpu {
enum NumberClass { // Table 8.13 Data and Image Formats [Sea Islands Series Instruction Set Architecture]
enum class DataFormat : u32 {
FormatInvalid = 0,
Format8 = 1,
Format16 = 2,
Format8_8 = 3,
Format32 = 4,
Format16_16 = 5,
Format10_11_11 = 6,
Format11_11_10 = 7,
Format10_10_10_2 = 8,
Format2_10_10_10 = 9,
Format8_8_8_8 = 10,
Format32_32 = 11,
Format16_16_16_16 = 12,
Format32_32_32 = 13,
Format32_32_32_32 = 14,
Format5_6_5 = 16,
Format1_5_5_5 = 17,
Format5_5_5_1 = 18,
Format4_4_4_4 = 19,
Format8_24 = 20,
Format24_8 = 21,
FormatX24_8_32 = 22,
FormatGB_GR = 32,
FormatBG_RG = 33,
Format5_9_9_9 = 34,
FormatBc1 = 35,
FormatBc2 = 36,
FormatBc3 = 37,
FormatBc4 = 38,
FormatBc5 = 39,
FormatBc6 = 40,
FormatBc7 = 41,
FormatFmask8_1 = 47,
FormatFmask8_2 = 48,
FormatFmask8_4 = 49,
FormatFmask16_1 = 50,
FormatFmask16_2 = 51,
FormatFmask32_2 = 52,
FormatFmask32_4 = 53,
FormatFmask32_8 = 54,
FormatFmask64_4 = 55,
FormatFmask64_8 = 56,
Format4_4 = 57,
Format6_5_5 = 58,
Format1 = 59,
Format1_Reversed = 60,
Format32_As_8 = 61,
Format32_As_8_8 = 62,
Format32_As_32_32_32_32 = 63,
};
enum class NumberFormat : u32 {
Unorm = 0,
Snorm = 1,
Uscaled = 2,
Sscaled = 3,
Uint = 4,
Sint = 5,
SnormNz = 6,
Float = 7,
Srgb = 9,
Ubnorm = 10,
UbnormNz = 11,
Ubint = 12,
Ubscaled = 13,
};
enum class NumberClass {
Float, Float,
Sint, Sint,
Uint, Uint,
}; };
[[nodiscard]] constexpr NumberClass GetNumberClass(const NumberFormat nfmt) { enum class CompSwizzle : u8 {
switch (nfmt) { Zero = 0,
case NumberFormat::Sint: One = 1,
return Sint; Red = 4,
case NumberFormat::Uint: Green = 5,
return Uint; Blue = 6,
Alpha = 7,
};
enum class NumberConversion : u32 {
None = 0,
UintToUscaled = 1,
SintToSscaled = 2,
UnormToUbnorm = 3,
Sint8ToSnormNz = 4,
Sint16ToSnormNz = 5,
Uint32ToUnorm = 6,
};
struct CompMapping {
CompSwizzle r;
CompSwizzle g;
CompSwizzle b;
CompSwizzle a;
auto operator<=>(const CompMapping& other) const = default;
template <typename T>
[[nodiscard]] std::array<T, 4> Apply(const std::array<T, 4>& data) const {
return {
ApplySingle(data, r),
ApplySingle(data, g),
ApplySingle(data, b),
ApplySingle(data, a),
};
}
[[nodiscard]] CompMapping Inverse() const {
CompMapping result{};
InverseSingle(result.r, CompSwizzle::Red);
InverseSingle(result.g, CompSwizzle::Green);
InverseSingle(result.b, CompSwizzle::Blue);
InverseSingle(result.a, CompSwizzle::Alpha);
return result;
}
private:
template <typename T>
T ApplySingle(const std::array<T, 4>& data, const CompSwizzle swizzle) const {
switch (swizzle) {
case CompSwizzle::Zero:
return T(0);
case CompSwizzle::One:
return T(1);
case CompSwizzle::Red:
return data[0];
case CompSwizzle::Green:
return data[1];
case CompSwizzle::Blue:
return data[2];
case CompSwizzle::Alpha:
return data[3];
default:
UNREACHABLE();
}
}
void InverseSingle(CompSwizzle& dst, const CompSwizzle target) const {
if (r == target) {
dst = CompSwizzle::Red;
} else if (g == target) {
dst = CompSwizzle::Green;
} else if (b == target) {
dst = CompSwizzle::Blue;
} else if (a == target) {
dst = CompSwizzle::Alpha;
} else {
dst = CompSwizzle::Zero;
}
}
};
static constexpr CompMapping IdentityMapping = {
.r = CompSwizzle::Red,
.g = CompSwizzle::Green,
.b = CompSwizzle::Blue,
.a = CompSwizzle::Alpha,
};
constexpr DataFormat RemapDataFormat(const DataFormat format) {
switch (format) {
case DataFormat::Format11_11_10:
return DataFormat::Format10_11_11;
case DataFormat::Format10_10_10_2:
return DataFormat::Format2_10_10_10;
case DataFormat::Format5_5_5_1:
return DataFormat::Format1_5_5_5;
default: default:
return Float; return format;
} }
} }
[[nodiscard]] constexpr bool IsInteger(const NumberFormat nfmt) { constexpr NumberFormat RemapNumberFormat(const NumberFormat format, const DataFormat data_format) {
switch (format) {
case NumberFormat::Unorm: {
switch (data_format) {
case DataFormat::Format32:
case DataFormat::Format32_32:
case DataFormat::Format32_32_32:
case DataFormat::Format32_32_32_32:
return NumberFormat::Uint;
default:
return format;
}
}
case NumberFormat::Uscaled:
return NumberFormat::Uint;
case NumberFormat::Sscaled:
case NumberFormat::SnormNz:
return NumberFormat::Sint;
case NumberFormat::Ubnorm:
return NumberFormat::Unorm;
case NumberFormat::Float:
if (data_format == DataFormat::Format8) {
// Games may ask for 8-bit float when they want to access the stencil component
// of a depth-stencil image. Change to unsigned int to match the stencil format.
// This is also the closest approximation to pass the bits through unconverted.
return NumberFormat::Uint;
}
[[fallthrough]];
default:
return format;
}
}
constexpr CompMapping RemapSwizzle(const DataFormat format, const CompMapping swizzle) {
switch (format) {
case DataFormat::Format1_5_5_5:
case DataFormat::Format11_11_10: {
CompMapping result;
result.r = swizzle.b;
result.g = swizzle.g;
result.b = swizzle.r;
result.a = swizzle.a;
return result;
}
case DataFormat::Format10_10_10_2: {
CompMapping result;
result.r = swizzle.a;
result.g = swizzle.b;
result.b = swizzle.g;
result.a = swizzle.r;
return result;
}
case DataFormat::Format4_4_4_4: {
// Remap to a more supported component order.
CompMapping result;
result.r = swizzle.g;
result.g = swizzle.b;
result.b = swizzle.a;
result.a = swizzle.r;
return result;
}
default:
return swizzle;
}
}
constexpr NumberConversion MapNumberConversion(const NumberFormat num_fmt,
const DataFormat data_fmt) {
switch (num_fmt) {
case NumberFormat::Unorm: {
switch (data_fmt) {
case DataFormat::Format32:
case DataFormat::Format32_32:
case DataFormat::Format32_32_32:
case DataFormat::Format32_32_32_32:
return NumberConversion::Uint32ToUnorm;
default:
return NumberConversion::None;
}
}
case NumberFormat::Uscaled:
return NumberConversion::UintToUscaled;
case NumberFormat::Sscaled:
return NumberConversion::SintToSscaled;
case NumberFormat::Ubnorm:
return NumberConversion::UnormToUbnorm;
case NumberFormat::SnormNz: {
switch (data_fmt) {
case DataFormat::Format8:
case DataFormat::Format8_8:
case DataFormat::Format8_8_8_8:
return NumberConversion::Sint8ToSnormNz;
case DataFormat::Format16:
case DataFormat::Format16_16:
case DataFormat::Format16_16_16_16:
return NumberConversion::Sint16ToSnormNz;
default:
UNREACHABLE_MSG("data_fmt = {}", u32(data_fmt));
}
}
default:
return NumberConversion::None;
}
}
constexpr NumberClass GetNumberClass(const NumberFormat nfmt) {
switch (nfmt) {
case NumberFormat::Sint:
return NumberClass::Sint;
case NumberFormat::Uint:
return NumberClass::Uint;
default:
return NumberClass::Float;
}
}
constexpr bool IsInteger(const NumberFormat nfmt) {
return nfmt == AmdGpu::NumberFormat::Sint || nfmt == AmdGpu::NumberFormat::Uint; return nfmt == AmdGpu::NumberFormat::Sint || nfmt == AmdGpu::NumberFormat::Uint;
} }
[[nodiscard]] std::string_view NameOf(DataFormat fmt); std::string_view NameOf(DataFormat fmt);
[[nodiscard]] std::string_view NameOf(NumberFormat fmt); std::string_view NameOf(NumberFormat fmt);
int NumComponents(DataFormat format); int NumComponents(DataFormat format);
int NumBits(DataFormat format); int NumBits(DataFormat format);

View file

@ -6,7 +6,6 @@
#include "common/alignment.h" #include "common/alignment.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/types.h"
#include "video_core/amdgpu/pixel_format.h" #include "video_core/amdgpu/pixel_format.h"
namespace AmdGpu { namespace AmdGpu {

View file

@ -5,7 +5,6 @@
#include <string_view> #include <string_view>
#include <fmt/format.h> #include <fmt/format.h>
#include "common/assert.h"
#include "common/types.h" #include "common/types.h"
namespace AmdGpu { namespace AmdGpu {
@ -114,281 +113,6 @@ enum class GsOutputPrimitiveType : u32 {
TriangleStrip = 2, TriangleStrip = 2,
}; };
// Table 8.13 Data and Image Formats [Sea Islands Series Instruction Set Architecture]
enum class DataFormat : u32 {
FormatInvalid = 0,
Format8 = 1,
Format16 = 2,
Format8_8 = 3,
Format32 = 4,
Format16_16 = 5,
Format10_11_11 = 6,
Format11_11_10 = 7,
Format10_10_10_2 = 8,
Format2_10_10_10 = 9,
Format8_8_8_8 = 10,
Format32_32 = 11,
Format16_16_16_16 = 12,
Format32_32_32 = 13,
Format32_32_32_32 = 14,
Format5_6_5 = 16,
Format1_5_5_5 = 17,
Format5_5_5_1 = 18,
Format4_4_4_4 = 19,
Format8_24 = 20,
Format24_8 = 21,
FormatX24_8_32 = 22,
FormatGB_GR = 32,
FormatBG_RG = 33,
Format5_9_9_9 = 34,
FormatBc1 = 35,
FormatBc2 = 36,
FormatBc3 = 37,
FormatBc4 = 38,
FormatBc5 = 39,
FormatBc6 = 40,
FormatBc7 = 41,
FormatFmask8_1 = 47,
FormatFmask8_2 = 48,
FormatFmask8_4 = 49,
FormatFmask16_1 = 50,
FormatFmask16_2 = 51,
FormatFmask32_2 = 52,
FormatFmask32_4 = 53,
FormatFmask32_8 = 54,
FormatFmask64_4 = 55,
FormatFmask64_8 = 56,
Format4_4 = 57,
Format6_5_5 = 58,
Format1 = 59,
Format1_Reversed = 60,
Format32_As_8 = 61,
Format32_As_8_8 = 62,
Format32_As_32_32_32_32 = 63,
};
enum class NumberFormat : u32 {
Unorm = 0,
Snorm = 1,
Uscaled = 2,
Sscaled = 3,
Uint = 4,
Sint = 5,
SnormNz = 6,
Float = 7,
Srgb = 9,
Ubnorm = 10,
UbnormNz = 11,
Ubint = 12,
Ubscaled = 13,
};
enum class CompSwizzle : u8 {
Zero = 0,
One = 1,
Red = 4,
Green = 5,
Blue = 6,
Alpha = 7,
};
enum class NumberConversion : u32 {
None = 0,
UintToUscaled = 1,
SintToSscaled = 2,
UnormToUbnorm = 3,
Sint8ToSnormNz = 4,
Sint16ToSnormNz = 5,
Uint32ToUnorm = 6,
};
struct CompMapping {
CompSwizzle r;
CompSwizzle g;
CompSwizzle b;
CompSwizzle a;
auto operator<=>(const CompMapping& other) const = default;
template <typename T>
[[nodiscard]] std::array<T, 4> Apply(const std::array<T, 4>& data) const {
return {
ApplySingle(data, r),
ApplySingle(data, g),
ApplySingle(data, b),
ApplySingle(data, a),
};
}
[[nodiscard]] CompMapping Inverse() const {
CompMapping result{};
InverseSingle(result.r, CompSwizzle::Red);
InverseSingle(result.g, CompSwizzle::Green);
InverseSingle(result.b, CompSwizzle::Blue);
InverseSingle(result.a, CompSwizzle::Alpha);
return result;
}
private:
template <typename T>
T ApplySingle(const std::array<T, 4>& data, const CompSwizzle swizzle) const {
switch (swizzle) {
case CompSwizzle::Zero:
return T(0);
case CompSwizzle::One:
return T(1);
case CompSwizzle::Red:
return data[0];
case CompSwizzle::Green:
return data[1];
case CompSwizzle::Blue:
return data[2];
case CompSwizzle::Alpha:
return data[3];
default:
UNREACHABLE();
}
}
void InverseSingle(CompSwizzle& dst, const CompSwizzle target) const {
if (r == target) {
dst = CompSwizzle::Red;
} else if (g == target) {
dst = CompSwizzle::Green;
} else if (b == target) {
dst = CompSwizzle::Blue;
} else if (a == target) {
dst = CompSwizzle::Alpha;
} else {
dst = CompSwizzle::Zero;
}
}
};
static constexpr CompMapping IdentityMapping = {
.r = CompSwizzle::Red,
.g = CompSwizzle::Green,
.b = CompSwizzle::Blue,
.a = CompSwizzle::Alpha,
};
inline DataFormat RemapDataFormat(const DataFormat format) {
switch (format) {
case DataFormat::Format11_11_10:
return DataFormat::Format10_11_11;
case DataFormat::Format10_10_10_2:
return DataFormat::Format2_10_10_10;
case DataFormat::Format5_5_5_1:
return DataFormat::Format1_5_5_5;
default:
return format;
}
}
inline NumberFormat RemapNumberFormat(const NumberFormat format, const DataFormat data_format) {
switch (format) {
case NumberFormat::Unorm: {
switch (data_format) {
case DataFormat::Format32:
case DataFormat::Format32_32:
case DataFormat::Format32_32_32:
case DataFormat::Format32_32_32_32:
return NumberFormat::Uint;
default:
return format;
}
}
case NumberFormat::Uscaled:
return NumberFormat::Uint;
case NumberFormat::Sscaled:
case NumberFormat::SnormNz:
return NumberFormat::Sint;
case NumberFormat::Ubnorm:
return NumberFormat::Unorm;
case NumberFormat::Float:
if (data_format == DataFormat::Format8) {
// Games may ask for 8-bit float when they want to access the stencil component
// of a depth-stencil image. Change to unsigned int to match the stencil format.
// This is also the closest approximation to pass the bits through unconverted.
return NumberFormat::Uint;
}
[[fallthrough]];
default:
return format;
}
}
inline CompMapping RemapSwizzle(const DataFormat format, const CompMapping swizzle) {
switch (format) {
case DataFormat::Format1_5_5_5:
case DataFormat::Format11_11_10: {
CompMapping result;
result.r = swizzle.b;
result.g = swizzle.g;
result.b = swizzle.r;
result.a = swizzle.a;
return result;
}
case DataFormat::Format10_10_10_2: {
CompMapping result;
result.r = swizzle.a;
result.g = swizzle.b;
result.b = swizzle.g;
result.a = swizzle.r;
return result;
}
case DataFormat::Format4_4_4_4: {
// Remap to a more supported component order.
CompMapping result;
result.r = swizzle.g;
result.g = swizzle.b;
result.b = swizzle.a;
result.a = swizzle.r;
return result;
}
default:
return swizzle;
}
}
inline NumberConversion MapNumberConversion(const NumberFormat num_fmt, const DataFormat data_fmt) {
switch (num_fmt) {
case NumberFormat::Unorm: {
switch (data_fmt) {
case DataFormat::Format32:
case DataFormat::Format32_32:
case DataFormat::Format32_32_32:
case DataFormat::Format32_32_32_32:
return NumberConversion::Uint32ToUnorm;
default:
return NumberConversion::None;
}
}
case NumberFormat::Uscaled:
return NumberConversion::UintToUscaled;
case NumberFormat::Sscaled:
return NumberConversion::SintToSscaled;
case NumberFormat::Ubnorm:
return NumberConversion::UnormToUbnorm;
case NumberFormat::SnormNz: {
switch (data_fmt) {
case DataFormat::Format8:
case DataFormat::Format8_8:
case DataFormat::Format8_8_8_8:
return NumberConversion::Sint8ToSnormNz;
case DataFormat::Format16:
case DataFormat::Format16_16:
case DataFormat::Format16_16_16_16:
return NumberConversion::Sint16ToSnormNz;
default:
UNREACHABLE_MSG("data_fmt = {}", u32(data_fmt));
}
}
default:
return NumberConversion::None;
}
}
} // namespace AmdGpu } // namespace AmdGpu
template <> template <>