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 p10{OpFSub(F32[4], p1, 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_z{OpCompositeExtract(F32[1], bary_coord, 2)};
const Id p10_y{OpVectorTimesScalar(F32[4], p10, bary_coord_y)};
@ -411,9 +413,15 @@ void EmitContext::DefineInputs() {
DefineVariable(U1[1], spv::BuiltIn::FrontFacing, spv::StorageClass::Input);
}
if (profile.needs_manual_interpolation) {
gl_bary_coord_id =
if (info.has_perspective_interp) {
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++) {
const auto& input = runtime_info.fs_info.inputs[i];
const u32 semantic = input.param_index;
@ -435,9 +443,12 @@ void EmitContext::DefineInputs() {
} else {
attr_id = DefineInput(type, semantic);
Name(attr_id, fmt::format("fs_in_attr{}", semantic));
}
if (input.is_flat) {
Decorate(attr_id, spv::Decoration::Flat);
} else if (IsLinear(info.interp_qualifiers[i])) {
Decorate(attr_id, spv::Decoration::NoPerspective);
}
}
input_params[semantic] =
GetAttributeInfo(AmdGpu::NumberFormat::Float, attr_id, num_components, false);

View file

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

View file

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

View file

@ -21,16 +21,60 @@
namespace Shader::Gcn {
static u32 next_vgpr_num;
static std::unordered_map<u32, IR::VectorReg> vgpr_map;
Translator::Translator(IR::Block* block_, Info& info_, const RuntimeInfo& runtime_info_,
const Profile& profile_)
: 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;
Translator::Translator(Info& info_, const RuntimeInfo& runtime_info_, const Profile& profile_)
: info{info_}, runtime_info{runtime_info_}, profile{profile_},
next_vgpr_num{runtime_info.num_allocated_vgprs} {
if (info.l_stage == LogicalStage::Fragment) {
dst_frag_vreg = GatherInterpQualifiers();
}
}
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.SetExec(ir.Imm1(true));
@ -60,39 +104,7 @@ void Translator::EmitPrologue() {
}
break;
case LogicalStage::Fragment:
dst_vreg = IR::VectorReg::V0;
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;
}
dst_vreg = dst_frag_vreg;
if (runtime_info.fs_info.addr_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));
@ -543,6 +555,26 @@ void Translator::LogMissingOpcode(const GcnInst& inst) {
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) {
// Emit instructions for each 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

View file

@ -53,15 +53,17 @@ enum class NegateMode : u32 {
Result,
};
static constexpr size_t MaxInterpVgpr = 16;
class Translator {
public:
explicit Translator(IR::Block* block_, Info& info, const RuntimeInfo& runtime_info,
const Profile& profile);
explicit Translator(Info& info, const RuntimeInfo& runtime_info, const Profile& profile);
void Translate(IR::Block* block, u32 pc, std::span<const GcnInst> inst_list);
void TranslateInstruction(const GcnInst& inst, u32 pc);
// Instruction categories
void EmitPrologue();
void EmitPrologue(IR::Block* first_block);
void EmitFetch(const GcnInst& inst);
void EmitExport(const GcnInst& inst);
void EmitFlowControl(u32 pc, const GcnInst& inst);
@ -326,16 +328,18 @@ private:
void LogMissingOpcode(const GcnInst& inst);
IR::VectorReg GetScratchVgpr(u32 offset);
IR::VectorReg GatherInterpQualifiers();
private:
IR::IREmitter ir;
Info& info;
const RuntimeInfo& runtime_info;
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;
};
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

View file

@ -22,13 +22,14 @@ void Translator::EmitVectorInterpolation(const GcnInst& inst) {
// VINTRP
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};
SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan));
}
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};
SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan));
}

View file

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

View file

@ -83,6 +83,16 @@ enum class Attribute : u64 {
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 NumRenderTargets = 8;
constexpr size_t NumParams = 32;
@ -104,6 +114,15 @@ constexpr bool IsMrt(Attribute attribute) noexcept {
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]] constexpr Attribute operator+(Attribute attr, int num) {

View file

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

View file

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

View file

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

View file

@ -5,34 +5,310 @@
#include <string_view>
#include <fmt/format.h>
#include "common/assert.h"
#include "common/types.h"
#include "video_core/amdgpu/types.h"
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,
Sint,
Uint,
};
[[nodiscard]] constexpr NumberClass GetNumberClass(const NumberFormat nfmt) {
switch (nfmt) {
case NumberFormat::Sint:
return Sint;
case NumberFormat::Uint:
return Uint;
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:
return Float;
UNREACHABLE();
}
}
[[nodiscard]] constexpr bool IsInteger(const NumberFormat nfmt) {
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:
return format;
}
}
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;
}
[[nodiscard]] std::string_view NameOf(DataFormat fmt);
[[nodiscard]] std::string_view NameOf(NumberFormat fmt);
std::string_view NameOf(DataFormat fmt);
std::string_view NameOf(NumberFormat fmt);
int NumComponents(DataFormat format);
int NumBits(DataFormat format);

View file

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

View file

@ -5,7 +5,6 @@
#include <string_view>
#include <fmt/format.h>
#include "common/assert.h"
#include "common/types.h"
namespace AmdGpu {
@ -114,281 +113,6 @@ enum class GsOutputPrimitiveType : u32 {
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
template <>