mirror of https://github.com/yuzu-mirror/yuzu
shader: Initial support for textures and TEX
parent
7d6ba5b984
commit
ab46371247
@ -0,0 +1,146 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <boost/container/static_vector.hpp>
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
class ImageOperands {
|
||||
public:
|
||||
explicit ImageOperands(EmitContext& ctx, bool has_bias, bool has_lod, bool has_lod_clamp,
|
||||
Id lod, Id offset) {
|
||||
if (has_bias) {
|
||||
const Id bias{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
|
||||
Add(spv::ImageOperandsMask::Bias, bias);
|
||||
}
|
||||
if (has_lod) {
|
||||
const Id lod_value{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
|
||||
Add(spv::ImageOperandsMask::Lod, lod_value);
|
||||
}
|
||||
if (Sirit::ValidId(offset)) {
|
||||
Add(spv::ImageOperandsMask::Offset, offset);
|
||||
}
|
||||
if (has_lod_clamp) {
|
||||
const Id lod_clamp{has_bias ? ctx.OpCompositeExtract(ctx.F32[1], lod, 1) : lod};
|
||||
Add(spv::ImageOperandsMask::MinLod, lod_clamp);
|
||||
}
|
||||
}
|
||||
|
||||
void Add(spv::ImageOperandsMask new_mask, Id value) {
|
||||
mask = static_cast<spv::ImageOperandsMask>(static_cast<unsigned>(mask) |
|
||||
static_cast<unsigned>(new_mask));
|
||||
operands.push_back(value);
|
||||
}
|
||||
|
||||
std::span<const Id> Span() const noexcept {
|
||||
return std::span{operands.data(), operands.size()};
|
||||
}
|
||||
|
||||
spv::ImageOperandsMask Mask() const noexcept {
|
||||
return mask;
|
||||
}
|
||||
|
||||
private:
|
||||
boost::container::static_vector<Id, 3> operands;
|
||||
spv::ImageOperandsMask mask{};
|
||||
};
|
||||
|
||||
Id Texture(EmitContext& ctx, const IR::Value& index) {
|
||||
if (index.IsImmediate()) {
|
||||
const TextureDefinition def{ctx.textures.at(index.U32())};
|
||||
return ctx.OpLoad(def.type, def.id);
|
||||
}
|
||||
throw NotImplementedException("Indirect texture sample");
|
||||
}
|
||||
|
||||
template <typename MethodPtrType, typename... Args>
|
||||
Id Emit(MethodPtrType sparse_ptr, MethodPtrType non_sparse_ptr, EmitContext& ctx, IR::Inst* inst,
|
||||
Id result_type, Args&&... args) {
|
||||
IR::Inst* const sparse{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
|
||||
if (!sparse) {
|
||||
return (ctx.*non_sparse_ptr)(result_type, std::forward<Args>(args)...);
|
||||
}
|
||||
const Id struct_type{ctx.TypeStruct(ctx.U32[1], result_type)};
|
||||
const Id sample{(ctx.*sparse_ptr)(struct_type, std::forward<Args>(args)...)};
|
||||
const Id resident_code{ctx.OpCompositeExtract(ctx.U32[1], sample, 0U)};
|
||||
sparse->SetDefinition(ctx.OpImageSparseTexelsResident(ctx.U1, resident_code));
|
||||
sparse->Invalidate();
|
||||
return ctx.OpCompositeExtract(result_type, sample, 1U);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageSampleExplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageSampleDrefImplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBindlessImageSampleDrefExplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageSampleImplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageSampleExplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageSampleDrefImplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitBoundImageSampleDrefExplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id bias_lc, Id offset) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0, bias_lc,
|
||||
offset);
|
||||
return Emit(&EmitContext::OpImageSparseSampleImplicitLod,
|
||||
&EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4], Texture(ctx, index),
|
||||
coords, operands.Mask(), operands.Span());
|
||||
}
|
||||
|
||||
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id lod_lc, Id offset) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod_lc, offset);
|
||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], Texture(ctx, index),
|
||||
coords, operands.Mask(), operands.Span());
|
||||
}
|
||||
|
||||
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
|
||||
Id coords, Id dref, Id bias_lc, Id offset) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0, bias_lc,
|
||||
offset);
|
||||
return Emit(&EmitContext::OpImageSparseSampleDrefImplicitLod,
|
||||
&EmitContext::OpImageSampleDrefImplicitLod, ctx, inst, ctx.F32[1],
|
||||
Texture(ctx, index), coords, dref, operands.Mask(), operands.Span());
|
||||
}
|
||||
|
||||
Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
|
||||
Id coords, Id dref, Id lod_lc, Id offset) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod_lc, offset);
|
||||
return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod,
|
||||
&EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1],
|
||||
Texture(ctx, index), coords, dref, operands.Mask(), operands.Span());
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
@ -0,0 +1,232 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
|
||||
|
||||
namespace Shader::Maxwell {
|
||||
namespace {
|
||||
enum class Blod : u64 {
|
||||
None,
|
||||
LZ,
|
||||
LB,
|
||||
LL,
|
||||
INVALIDBLOD4,
|
||||
INVALIDBLOD5,
|
||||
LBA,
|
||||
LLA,
|
||||
};
|
||||
|
||||
enum class TextureType : u64 {
|
||||
_1D,
|
||||
ARRAY_1D,
|
||||
_2D,
|
||||
ARRAY_2D,
|
||||
_3D,
|
||||
ARRAY_3D,
|
||||
CUBE,
|
||||
ARRAY_CUBE,
|
||||
};
|
||||
|
||||
Shader::TextureType GetType(TextureType type, bool dc) {
|
||||
switch (type) {
|
||||
case TextureType::_1D:
|
||||
return dc ? Shader::TextureType::Shadow1D : Shader::TextureType::Color1D;
|
||||
case TextureType::ARRAY_1D:
|
||||
return dc ? Shader::TextureType::ShadowArray1D : Shader::TextureType::ColorArray1D;
|
||||
case TextureType::_2D:
|
||||
return dc ? Shader::TextureType::Shadow2D : Shader::TextureType::Color2D;
|
||||
case TextureType::ARRAY_2D:
|
||||
return dc ? Shader::TextureType::ShadowArray2D : Shader::TextureType::ColorArray2D;
|
||||
case TextureType::_3D:
|
||||
return dc ? Shader::TextureType::Shadow3D : Shader::TextureType::Color3D;
|
||||
case TextureType::ARRAY_3D:
|
||||
throw NotImplementedException("3D array texture type");
|
||||
case TextureType::CUBE:
|
||||
return dc ? Shader::TextureType::ShadowCube : Shader::TextureType::ColorCube;
|
||||
case TextureType::ARRAY_CUBE:
|
||||
return dc ? Shader::TextureType::ShadowArrayCube : Shader::TextureType::ColorArrayCube;
|
||||
}
|
||||
throw NotImplementedException("Invalid texture type {}", type);
|
||||
}
|
||||
|
||||
IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, TextureType type) {
|
||||
const auto read_array{[&]() -> IR::F32 { return v.ir.ConvertUToF(32, v.X(reg)); }};
|
||||
switch (type) {
|
||||
case TextureType::_1D:
|
||||
return v.F(reg);
|
||||
case TextureType::ARRAY_1D:
|
||||
return v.ir.CompositeConstruct(read_array(), v.F(reg + 1));
|
||||
case TextureType::_2D:
|
||||
return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1));
|
||||
case TextureType::ARRAY_2D:
|
||||
return v.ir.CompositeConstruct(read_array(), v.F(reg + 1), v.F(reg + 2));
|
||||
case TextureType::_3D:
|
||||
return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2));
|
||||
case TextureType::ARRAY_3D:
|
||||
throw NotImplementedException("3D array texture type");
|
||||
case TextureType::CUBE:
|
||||
return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2));
|
||||
case TextureType::ARRAY_CUBE:
|
||||
return v.ir.CompositeConstruct(read_array(), v.F(reg + 1), v.F(reg + 2), v.F(reg + 3));
|
||||
}
|
||||
throw NotImplementedException("Invalid texture type {}", type);
|
||||
}
|
||||
|
||||
IR::F32 MakeLod(TranslatorVisitor& v, IR::Reg& reg, Blod blod) {
|
||||
switch (blod) {
|
||||
case Blod::None:
|
||||
return v.ir.Imm32(0.0f);
|
||||
case Blod::LZ:
|
||||
return v.ir.Imm32(0.0f);
|
||||
case Blod::LB:
|
||||
case Blod::LL:
|
||||
case Blod::LBA:
|
||||
case Blod::LLA:
|
||||
return v.F(reg++);
|
||||
case Blod::INVALIDBLOD4:
|
||||
case Blod::INVALIDBLOD5:
|
||||
break;
|
||||
}
|
||||
throw NotImplementedException("Invalid blod {}", blod);
|
||||
}
|
||||
|
||||
IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg& reg, TextureType type) {
|
||||
const IR::U32 value{v.X(reg++)};
|
||||
switch (type) {
|
||||
case TextureType::_1D:
|
||||
case TextureType::ARRAY_1D:
|
||||
return v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4));
|
||||
case TextureType::_2D:
|
||||
case TextureType::ARRAY_2D:
|
||||
return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4)),
|
||||
v.ir.BitFieldExtract(value, v.ir.Imm32(4), v.ir.Imm32(4)));
|
||||
case TextureType::_3D:
|
||||
case TextureType::ARRAY_3D:
|
||||
return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4)),
|
||||
v.ir.BitFieldExtract(value, v.ir.Imm32(4), v.ir.Imm32(4)),
|
||||
v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(4)));
|
||||
case TextureType::CUBE:
|
||||
case TextureType::ARRAY_CUBE:
|
||||
throw NotImplementedException("Illegal offset on CUBE sample");
|
||||
}
|
||||
throw NotImplementedException("Invalid texture type {}", type);
|
||||
}
|
||||
|
||||
bool HasExplicitLod(Blod blod) {
|
||||
switch (blod) {
|
||||
case Blod::LL:
|
||||
case Blod::LLA:
|
||||
case Blod::LZ:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Impl(TranslatorVisitor& v, u64 insn, bool aoffi, Blod blod, bool lc,
|
||||
std::optional<u32> cbuf_offset) {
|
||||
union {
|
||||
u64 raw;
|
||||
BitField<35, 1, u64> ndv;
|
||||
BitField<49, 1, u64> nodep;
|
||||
BitField<50, 1, u64> dc;
|
||||
BitField<51, 3, IR::Pred> sparse_pred;
|
||||
BitField<0, 8, IR::Reg> dest_reg;
|
||||
BitField<8, 8, IR::Reg> coord_reg;
|
||||
BitField<20, 8, IR::Reg> meta_reg;
|
||||
BitField<28, 3, TextureType> type;
|
||||
BitField<31, 4, u64> mask;
|
||||
} const tex{insn};
|
||||
|
||||
if (lc) {
|
||||
throw NotImplementedException("LC");
|
||||
}
|
||||
const IR::Value coords{MakeCoords(v, tex.coord_reg, tex.type)};
|
||||
|
||||
IR::Reg meta_reg{tex.meta_reg};
|
||||
IR::Value handle;
|
||||
IR::Value offset;
|
||||
IR::F32 dref;
|
||||
IR::F32 lod_clamp;
|
||||
if (cbuf_offset) {
|
||||
handle = v.ir.Imm32(*cbuf_offset);
|
||||
} else {
|
||||
handle = v.X(meta_reg++);
|
||||
}
|
||||
const IR::F32 lod{MakeLod(v, meta_reg, blod)};
|
||||
if (aoffi) {
|
||||
offset = MakeOffset(v, meta_reg, tex.type);
|
||||
}
|
||||
if (tex.dc != 0) {
|
||||
dref = v.F(meta_reg++);
|
||||
}
|
||||
IR::TextureInstInfo info{};
|
||||
info.type.Assign(GetType(tex.type, tex.dc != 0));
|
||||
info.has_bias.Assign(blod == Blod::LB || blod == Blod::LBA ? 1 : 0);
|
||||
info.has_lod_clamp.Assign(lc ? 1 : 0);
|
||||
|
||||
const IR::Value sample{[&]() -> IR::Value {
|
||||
if (tex.dc == 0) {
|
||||
if (HasExplicitLod(blod)) {
|
||||
return v.ir.ImageSampleExplicitLod(handle, coords, lod, offset, lod_clamp, info);
|
||||
} else {
|
||||
return v.ir.ImageSampleImplicitLod(handle, coords, lod, offset, lod_clamp, info);
|
||||
}
|
||||
}
|
||||
if (HasExplicitLod(blod)) {
|
||||
return v.ir.ImageSampleDrefExplicitLod(handle, coords, dref, lod, offset, lod_clamp,
|
||||
info);
|
||||
} else {
|
||||
return v.ir.ImageSampleDrefImplicitLod(handle, coords, dref, lod, offset, lod_clamp,
|
||||
info);
|
||||
}
|
||||
}()};
|
||||
|
||||
for (int element = 0; element < 4; ++element) {
|
||||
if (((tex.mask >> element) & 1) == 0) {
|
||||
continue;
|
||||
}
|
||||
IR::F32 value;
|
||||
if (tex.dc != 0) {
|
||||
value = element < 3 ? IR::F32{sample} : v.ir.Imm32(1.0f);
|
||||
} else {
|
||||
value = IR::F32{v.ir.CompositeExtract(sample, element)};
|
||||
}
|
||||
v.F(tex.dest_reg + element, value);
|
||||
}
|
||||
if (tex.sparse_pred != IR::Pred::PT) {
|
||||
v.ir.SetPred(tex.sparse_pred, v.ir.LogicalNot(v.ir.GetSparseFromOp(sample)));
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void TranslatorVisitor::TEX(u64 insn) {
|
||||
union {
|
||||
u64 raw;
|
||||
BitField<54, 1, u64> aoffi;
|
||||
BitField<55, 3, Blod> blod;
|
||||
BitField<58, 1, u64> lc;
|
||||
BitField<36, 13, u64> cbuf_offset;
|
||||
} const tex{insn};
|
||||
|
||||
Impl(*this, insn, tex.aoffi != 0, tex.blod, tex.lc != 0, static_cast<u32>(tex.cbuf_offset));
|
||||
}
|
||||
|
||||
void TranslatorVisitor::TEX_b(u64 insn) {
|
||||
union {
|
||||
u64 raw;
|
||||
BitField<36, 1, u64> aoffi;
|
||||
BitField<37, 3, Blod> blod;
|
||||
BitField<40, 1, u64> lc;
|
||||
} const tex{insn};
|
||||
|
||||
Impl(*this, insn, tex.aoffi != 0, tex.blod, tex.lc != 0, std::nullopt);
|
||||
}
|
||||
|
||||
} // namespace Shader::Maxwell
|
@ -0,0 +1,199 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "shader_recompiler/environment.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
#include "shader_recompiler/ir_opt/passes.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
|
||||
namespace Shader::Optimization {
|
||||
namespace {
|
||||
struct ConstBufferAddr {
|
||||
u32 index;
|
||||
u32 offset;
|
||||
};
|
||||
|
||||
struct TextureInst {
|
||||
ConstBufferAddr cbuf;
|
||||
IR::Inst* inst;
|
||||
IR::Block* block;
|
||||
};
|
||||
|
||||
using TextureInstVector = boost::container::small_vector<TextureInst, 24>;
|
||||
|
||||
using VisitedBlocks = boost::container::flat_set<IR::Block*, std::less<IR::Block*>,
|
||||
boost::container::small_vector<IR::Block*, 2>>;
|
||||
|
||||
IR::Opcode IndexedInstruction(const IR::Inst& inst) {
|
||||
switch (inst.Opcode()) {
|
||||
case IR::Opcode::BindlessImageSampleImplicitLod:
|
||||
case IR::Opcode::BoundImageSampleImplicitLod:
|
||||
return IR::Opcode::ImageSampleImplicitLod;
|
||||
case IR::Opcode::BoundImageSampleExplicitLod:
|
||||
case IR::Opcode::BindlessImageSampleExplicitLod:
|
||||
return IR::Opcode::ImageSampleExplicitLod;
|
||||
case IR::Opcode::BoundImageSampleDrefImplicitLod:
|
||||
case IR::Opcode::BindlessImageSampleDrefImplicitLod:
|
||||
return IR::Opcode::ImageSampleDrefImplicitLod;
|
||||
case IR::Opcode::BoundImageSampleDrefExplicitLod:
|
||||
case IR::Opcode::BindlessImageSampleDrefExplicitLod:
|
||||
return IR::Opcode::ImageSampleDrefExplicitLod;
|
||||
default:
|
||||
return IR::Opcode::Void;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsBindless(const IR::Inst& inst) {
|
||||
switch (inst.Opcode()) {
|
||||
case IR::Opcode::BindlessImageSampleImplicitLod:
|
||||
case IR::Opcode::BindlessImageSampleExplicitLod:
|
||||
case IR::Opcode::BindlessImageSampleDrefImplicitLod:
|
||||
case IR::Opcode::BindlessImageSampleDrefExplicitLod:
|
||||
return true;
|
||||
case IR::Opcode::BoundImageSampleImplicitLod:
|
||||
case IR::Opcode::BoundImageSampleExplicitLod:
|
||||
case IR::Opcode::BoundImageSampleDrefImplicitLod:
|
||||
case IR::Opcode::BoundImageSampleDrefExplicitLod:
|
||||
return false;
|
||||
default:
|
||||
throw InvalidArgument("Invalid opcode {}", inst.Opcode());
|
||||
}
|
||||
}
|
||||
|
||||
bool IsTextureInstruction(const IR::Inst& inst) {
|
||||
return IndexedInstruction(inst) != IR::Opcode::Void;
|
||||
}
|
||||
|
||||
std::optional<ConstBufferAddr> Track(IR::Block* block, const IR::Value& value,
|
||||
VisitedBlocks& visited) {
|
||||
if (value.IsImmediate()) {
|
||||
// Immediates can't be a storage buffer
|
||||
return std::nullopt;
|
||||
}
|
||||
const IR::Inst* const inst{value.InstRecursive()};
|
||||
if (inst->Opcode() == IR::Opcode::GetCbuf) {
|
||||
const IR::Value index{inst->Arg(0)};
|
||||
const IR::Value offset{inst->Arg(1)};
|
||||
if (!index.IsImmediate()) {
|
||||
// Reading a bindless texture from variable indices is valid
|
||||
// but not supported here at the moment
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!offset.IsImmediate()) {
|
||||
// TODO: Support arrays of textures
|
||||
return std::nullopt;
|
||||
}
|
||||
return ConstBufferAddr{
|
||||
.index{index.U32()},
|
||||
.offset{offset.U32()},
|
||||
};
|
||||
}
|
||||
// Reversed loops are more likely to find the right result
|
||||
for (size_t arg = inst->NumArgs(); arg--;) {
|
||||
IR::Block* inst_block{block};
|
||||
if (inst->Opcode() == IR::Opcode::Phi) {
|
||||
// If we are going through a phi node, mark the current block as visited
|
||||
visited.insert(block);
|
||||
// and skip already visited blocks to avoid looping forever
|
||||
IR::Block* const phi_block{inst->PhiBlock(arg)};
|
||||
if (visited.contains(phi_block)) {
|
||||
// Already visited, skip
|
||||
continue;
|
||||
}
|
||||
inst_block = phi_block;
|
||||
}
|
||||
const std::optional storage_buffer{Track(inst_block, inst->Arg(arg), visited)};
|
||||
if (storage_buffer) {
|
||||
return *storage_buffer;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
|
||||
ConstBufferAddr addr;
|
||||
if (IsBindless(inst)) {
|
||||
VisitedBlocks visited;
|
||||
const std::optional<ConstBufferAddr> track_addr{Track(block, IR::Value{&inst}, visited)};
|
||||
if (!track_addr) {
|
||||
throw NotImplementedException("Failed to track bindless texture constant buffer");
|
||||
}
|
||||
addr = *track_addr;
|
||||
} else {
|
||||
addr = ConstBufferAddr{
|
||||
.index{env.TextureBoundBuffer()},
|
||||
.offset{inst.Arg(0).U32()},
|
||||
};
|
||||
}
|
||||
return TextureInst{
|
||||
.cbuf{addr},
|
||||
.inst{&inst},
|
||||
.block{block},
|
||||
};
|
||||
}
|
||||
|
||||
class Descriptors {
|
||||
public:
|
||||
explicit Descriptors(TextureDescriptors& descriptors_) : descriptors{descriptors_} {}
|
||||
|
||||
u32 Add(const TextureDescriptor& descriptor) {
|
||||
// TODO: Handle arrays
|
||||
auto it{std::ranges::find_if(descriptors, [&descriptor](const TextureDescriptor& existing) {
|
||||
return descriptor.cbuf_index == existing.cbuf_index &&
|
||||
descriptor.cbuf_offset == existing.cbuf_offset &&
|
||||
descriptor.type == existing.type;
|
||||
})};
|
||||
if (it != descriptors.end()) {
|
||||
return static_cast<u32>(std::distance(descriptors.begin(), it));
|
||||
}
|
||||
descriptors.push_back(descriptor);
|
||||
return static_cast<u32>(descriptors.size()) - 1;
|
||||
}
|
||||
|
||||
private:
|
||||
TextureDescriptors& descriptors;
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
void TexturePass(Environment& env, IR::Program& program) {
|
||||
TextureInstVector to_replace;
|
||||
for (IR::Function& function : program.functions) {
|
||||
for (IR::Block* const block : function.post_order_blocks) {
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
if (!IsTextureInstruction(inst)) {
|
||||
continue;
|
||||
}
|
||||
to_replace.push_back(MakeInst(env, block, inst));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort instructions to visit textures by constant buffer index, then by offset
|
||||
std::ranges::sort(to_replace, [](const auto& lhs, const auto& rhs) {
|
||||
return lhs.cbuf.offset < rhs.cbuf.offset;
|
||||
});
|
||||
std::stable_sort(to_replace.begin(), to_replace.end(), [](const auto& lhs, const auto& rhs) {
|
||||
return lhs.cbuf.index < rhs.cbuf.index;
|
||||
});
|
||||
Descriptors descriptors{program.info.texture_descriptors};
|
||||
for (TextureInst& texture_inst : to_replace) {
|
||||
// TODO: Handle arrays
|
||||
IR::Inst* const inst{texture_inst.inst};
|
||||
const u32 index{descriptors.Add(TextureDescriptor{
|
||||
.type{inst->Flags<IR::TextureInstInfo>().type},
|
||||
.cbuf_index{texture_inst.cbuf.index},
|
||||
.cbuf_offset{texture_inst.cbuf.offset},
|
||||
.count{1},
|
||||
})};
|
||||
inst->ReplaceOpcode(IndexedInstruction(*inst));
|
||||
inst->SetArg(0, IR::Value{index});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Optimization
|
Loading…
Reference in New Issue