mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 13:56:59 +01:00
shader: reflect HS patch constants and lower TES patch inputs through predicated addresses
This commit is contained in:
@@ -25,13 +25,16 @@ struct TessellationAddress {
|
|||||||
bool operator==(const TessellationAddress&) const = default;
|
bool operator==(const TessellationAddress&) const = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t ReflectTessellationStride(const Decoder::Program& program, bool local,
|
// Reflects the LS output stride, or the HS output stride and patch constant range.
|
||||||
uint32_t control_points, uint32_t input_stride) {
|
void ReflectTessellationLayout(const Decoder::Program& program, bool local,
|
||||||
|
ShaderTessellationInputInfo& info) {
|
||||||
using namespace Decoder;
|
using namespace Decoder;
|
||||||
using Address = TessellationAddress;
|
using Address = TessellationAddress;
|
||||||
using Kind = Address::Kind;
|
using Kind = Address::Kind;
|
||||||
using Registers = std::array<Address, IR::NumVectorRegs>;
|
using Registers = std::array<Address, IR::NumVectorRegs>;
|
||||||
Registers registers {};
|
|
||||||
|
const auto control_points = local ? info.input_control_points : info.output_control_points;
|
||||||
|
Registers registers {};
|
||||||
registers[local ? 3u : 1u] =
|
registers[local ? 3u : 1u] =
|
||||||
local ? Address {Kind::Affine, 1u, 0u} : Address {Kind::PackedControlPoint};
|
local ? Address {Kind::Affine, 1u, 0u} : Address {Kind::PackedControlPoint};
|
||||||
const auto constant = [](uint32_t value) { return Address {Kind::Affine, 0u, value}; };
|
const auto constant = [](uint32_t value) { return Address {Kind::Affine, 0u, value}; };
|
||||||
@@ -85,13 +88,13 @@ uint32_t ReflectTessellationStride(const Decoder::Program& program, bool local,
|
|||||||
// A forward branch joins its target with the fallthrough path. Definitions that
|
// A forward branch joins its target with the fallthrough path. Definitions that
|
||||||
// differ between the paths are unknown there.
|
// differ between the paths are unknown there.
|
||||||
std::unordered_map<uint32_t, Registers> joins;
|
std::unordered_map<uint32_t, Registers> joins;
|
||||||
|
|
||||||
const auto merge = [](Registers& into, const Registers& from) {
|
const auto merge = [](Registers& into, const Registers& from) {
|
||||||
for (uint32_t index = 0; index < into.size(); index++) {
|
for (uint32_t index = 0; index < into.size(); index++) {
|
||||||
if (into[index] != from[index]) into[index] = {};
|
if (into[index] != from[index]) into[index] = {};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
uint32_t stride = 0;
|
|
||||||
|
uint32_t stride = 0, patch_begin = UINT32_MAX, patch_end = 0;
|
||||||
for (const auto& inst: program.instructions) {
|
for (const auto& inst: program.instructions) {
|
||||||
if (const auto join = joins.find(inst.pc); join != joins.end()) {
|
if (const auto join = joins.find(inst.pc); join != joins.end()) {
|
||||||
merge(registers, join->second);
|
merge(registers, join->second);
|
||||||
@@ -119,10 +122,15 @@ uint32_t ReflectTessellationStride(const Decoder::Program& program, bool local,
|
|||||||
inst.pc);
|
inst.pc);
|
||||||
}
|
}
|
||||||
if (address.coefficient != 0u) {
|
if (address.coefficient != 0u) {
|
||||||
const auto expected = control_read ? input_stride : stride;
|
const auto expected = control_read ? info.ls_stride : stride;
|
||||||
EXIT_NOT_IMPLEMENTED((address.coefficient & 3u) != 0u ||
|
EXIT_NOT_IMPLEMENTED((address.coefficient & 3u) != 0u ||
|
||||||
(expected != 0u && expected != address.coefficient));
|
(expected != 0u && expected != address.coefficient));
|
||||||
if (!control_read) stride = address.coefficient;
|
if (!control_read) stride = address.coefficient;
|
||||||
|
} else if (control_store) {
|
||||||
|
// Control-point-independent HS outputs are patch constants.
|
||||||
|
const auto begin = address.constant + inst.offset;
|
||||||
|
patch_begin = std::min(patch_begin, begin);
|
||||||
|
patch_end = std::max(patch_end, begin + 4u * inst.data_dwords);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Store vdata is a source, despite occupying the decoder's dst field.
|
// Store vdata is a source, despite occupying the decoder's dst field.
|
||||||
@@ -145,6 +153,7 @@ uint32_t ReflectTessellationStride(const Decoder::Program& program, bool local,
|
|||||||
(rhs.kind == Kind::PackedControlPoint && lhs == constant(0xffu)))
|
(rhs.kind == Kind::PackedControlPoint && lhs == constant(0xffu)))
|
||||||
value = constant(0u);
|
value = constant(0u);
|
||||||
break;
|
break;
|
||||||
|
case Opcode::V_ADD_NC_U32: value = add(lhs, rhs); break;
|
||||||
case Opcode::V_MUL_LO_U32: value = multiply(lhs, rhs); break;
|
case Opcode::V_MUL_LO_U32: value = multiply(lhs, rhs); break;
|
||||||
case Opcode::V_MUL_U32_U24: value = multiply(low24(lhs), low24(rhs)); break;
|
case Opcode::V_MUL_U32_U24: value = multiply(low24(lhs), low24(rhs)); break;
|
||||||
case Opcode::V_MAD_U32_U24: value = add(multiply(low24(lhs), low24(rhs)), third); break;
|
case Opcode::V_MAD_U32_U24: value = add(multiply(low24(lhs), low24(rhs)), third); break;
|
||||||
@@ -173,7 +182,12 @@ uint32_t ReflectTessellationStride(const Decoder::Program& program, bool local,
|
|||||||
registers.at(inst.dst.reg) = value;
|
registers.at(inst.dst.reg) = value;
|
||||||
}
|
}
|
||||||
EXIT_NOT_IMPLEMENTED(!joins.empty() || stride == 0u);
|
EXIT_NOT_IMPLEMENTED(!joins.empty() || stride == 0u);
|
||||||
return stride;
|
(local ? info.ls_stride : info.hs_stride) = stride;
|
||||||
|
if (patch_end != 0u) {
|
||||||
|
EXIT_NOT_IMPLEMENTED(patch_begin < stride * control_points);
|
||||||
|
info.patch_base = patch_begin;
|
||||||
|
info.patch_size = patch_end - patch_begin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const IR::Inst* TessellationBufferBase(const IR::Inst& inst) {
|
const IR::Inst* TessellationBufferBase(const IR::Inst& inst) {
|
||||||
@@ -184,6 +198,15 @@ const IR::Inst* TessellationBufferBase(const IR::Inst& inst) {
|
|||||||
: nullptr;
|
: nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whether an access predicate, possibly a conjunction of EXEC masks, implies a condition.
|
||||||
|
bool Implies(IR::Value predicate, IR::Value condition) {
|
||||||
|
predicate = predicate.Resolve();
|
||||||
|
if (predicate == condition.Resolve()) return true;
|
||||||
|
const auto* inst = predicate.TryInstruction();
|
||||||
|
return inst != nullptr && inst->GetOpcode() == IR::ValueOpcode::LogicalAnd &&
|
||||||
|
(Implies(inst->Arg(0), condition) || Implies(inst->Arg(1), condition));
|
||||||
|
}
|
||||||
|
|
||||||
IR::Value ActiveAddress(IR::Block& block, IR::Block::iterator before, IR::Value value,
|
IR::Value ActiveAddress(IR::Block& block, IR::Block::iterator before, IR::Value value,
|
||||||
IR::Value predicate, std::unordered_map<IR::Inst*, IR::Value>& resolved) {
|
IR::Value predicate, std::unordered_map<IR::Inst*, IR::Value>& resolved) {
|
||||||
using namespace IR;
|
using namespace IR;
|
||||||
@@ -192,18 +215,28 @@ IR::Value ActiveAddress(IR::Block& block, IR::Block::iterator before, IR::Value
|
|||||||
if (source == nullptr) return value;
|
if (source == nullptr) return value;
|
||||||
if (const auto found = resolved.find(source); found != resolved.end()) return found->second;
|
if (const auto found = resolved.find(source); found != resolved.end()) return found->second;
|
||||||
if (source->GetOpcode() == ValueOpcode::SelectU32) {
|
if (source->GetOpcode() == ValueOpcode::SelectU32) {
|
||||||
return source->Arg(0).Resolve() == predicate.Resolve()
|
return Implies(predicate, source->Arg(0))
|
||||||
? ActiveAddress(block, before, source->Arg(1), predicate, resolved)
|
? ActiveAddress(block, before, source->Arg(1), predicate, resolved)
|
||||||
: value;
|
: value;
|
||||||
}
|
}
|
||||||
if (source->GetOpcode() != ValueOpcode::IAdd32 && source->GetOpcode() != ValueOpcode::ISub32)
|
// Pure address arithmetic that stays behind EXEC selects; mul24 lowers to IMul32 of BFEs.
|
||||||
|
const auto opcode = source->GetOpcode();
|
||||||
|
if (opcode != ValueOpcode::IAdd32 && opcode != ValueOpcode::ISub32 &&
|
||||||
|
opcode != ValueOpcode::IMul32 && opcode != ValueOpcode::BitFieldUExtract)
|
||||||
return value;
|
return value;
|
||||||
const auto lhs = ActiveAddress(block, before, source->Arg(0), predicate, resolved);
|
std::array<Value, 3> args;
|
||||||
const auto rhs = ActiveAddress(block, before, source->Arg(1), predicate, resolved);
|
bool changed = false;
|
||||||
if (lhs != source->Arg(0).Resolve() || rhs != source->Arg(1).Resolve()) {
|
for (size_t index = 0; index < source->NumArgs(); index++) {
|
||||||
auto copy = block.PrependNewInst(before, source->GetOpcode(), {lhs, rhs},
|
args[index] = ActiveAddress(block, before, source->Arg(index), predicate, resolved);
|
||||||
source->Flags<uint64_t>());
|
changed |= args[index] != source->Arg(index).Resolve();
|
||||||
value = Value(&*copy);
|
}
|
||||||
|
if (changed) {
|
||||||
|
const auto flags = source->Flags<uint64_t>();
|
||||||
|
const auto copy =
|
||||||
|
source->NumArgs() == 2u
|
||||||
|
? block.PrependNewInst(before, opcode, {args[0], args[1]}, flags)
|
||||||
|
: block.PrependNewInst(before, opcode, {args[0], args[1], args[2]}, flags);
|
||||||
|
value = Value(&*copy);
|
||||||
}
|
}
|
||||||
resolved.emplace(source, value);
|
resolved.emplace(source, value);
|
||||||
return value;
|
return value;
|
||||||
@@ -216,11 +249,12 @@ void AnalyzeTessellationPrograms(std::span<const uint32_t> local, std::span<cons
|
|||||||
const auto local_program = Decoder::DecodeFrontProgram(local);
|
const auto local_program = Decoder::DecodeFrontProgram(local);
|
||||||
Decoder::Program control_program;
|
Decoder::Program control_program;
|
||||||
Decoder::DecodeProgram(control, control_program);
|
Decoder::DecodeProgram(control, control_program);
|
||||||
info.ls_stride = ReflectTessellationStride(local_program, true, info.input_control_points, 0u);
|
ReflectTessellationLayout(local_program, true, info);
|
||||||
info.hs_stride = ReflectTessellationStride(control_program, false, info.output_control_points,
|
ReflectTessellationLayout(control_program, false, info);
|
||||||
info.ls_stride);
|
LOGF("Tessellation interface: input_cp=%u output_cp=%u ls_stride=%u hs_stride=%u "
|
||||||
LOGF("Tessellation interface: input_cp=%u output_cp=%u ls_stride=%u hs_stride=%u\n",
|
"patch_base=0x%x patch_size=%u\n",
|
||||||
info.input_control_points, info.output_control_points, info.ls_stride, info.hs_stride);
|
info.input_control_points, info.output_control_points, info.ls_stride, info.hs_stride,
|
||||||
|
info.patch_base, info.patch_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LowerTessellationMemory(IR::Program& program, const CompileOptions& options) {
|
void LowerTessellationMemory(IR::Program& program, const CompileOptions& options) {
|
||||||
@@ -241,7 +275,7 @@ void LowerTessellationMemory(IR::Program& program, const CompileOptions& options
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConstantPropagationPass(program.blocks, program.wave_size);
|
ConstantPropagationPass(program.blocks, program.wave_size);
|
||||||
uint32_t reads = 0, writes = 0, factors = 0;
|
uint32_t reads = 0, writes = 0, factors = 0, patches = 0;
|
||||||
for (auto* block: program.blocks) {
|
for (auto* block: program.blocks) {
|
||||||
for (auto it = block->begin(); it != block->end(); ++it) {
|
for (auto it = block->begin(); it != block->end(); ++it) {
|
||||||
auto& inst = *it;
|
auto& inst = *it;
|
||||||
@@ -291,8 +325,13 @@ void LowerTessellationMemory(IR::Program& program, const CompileOptions& options
|
|||||||
: options.stage != ShaderType::TessellationEvaluation);
|
: options.stage != ShaderType::TessellationEvaluation);
|
||||||
if (address.IsImmediate() &&
|
if (address.IsImmediate() &&
|
||||||
address.U32() >= tess.hs_stride * tess.output_control_points) {
|
address.U32() >= tess.hs_stride * tess.output_control_points) {
|
||||||
EXIT_NOT_IMPLEMENTED(!write);
|
const auto begin = address.U32() + memory.offset;
|
||||||
kind = TessellationAttribute::PatchOutput;
|
EXIT_NOT_IMPLEMENTED(begin < tess.patch_base ||
|
||||||
|
begin + 4u * components >
|
||||||
|
tess.patch_base + tess.patch_size);
|
||||||
|
kind = write ? TessellationAttribute::PatchOutput
|
||||||
|
: TessellationAttribute::PatchInput;
|
||||||
|
patches += components;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -343,11 +382,14 @@ void LowerTessellationMemory(IR::Program& program, const CompileOptions& options
|
|||||||
ConstantPropagationPass(program.blocks, program.wave_size);
|
ConstantPropagationPass(program.blocks, program.wave_size);
|
||||||
RemoveIdentities(program);
|
RemoveIdentities(program);
|
||||||
EliminateDeadCode(program.blocks);
|
EliminateDeadCode(program.blocks);
|
||||||
LOGF("%s tessellation lowering: reads=%u writes=%u factors=%u\n",
|
// Patch constant stores whose EXEC selects were not stripped would land in control points.
|
||||||
|
EXIT_NOT_IMPLEMENTED(options.stage == ShaderType::TessellationControl &&
|
||||||
|
tess.patch_size != 0u && patches == 0u);
|
||||||
|
LOGF("%s tessellation lowering: reads=%u writes=%u factors=%u patches=%u\n",
|
||||||
options.stage == ShaderType::Local ? "LS"
|
options.stage == ShaderType::Local ? "LS"
|
||||||
: options.stage == ShaderType::TessellationControl ? "HS"
|
: options.stage == ShaderType::TessellationControl ? "HS"
|
||||||
: "TES",
|
: "TES",
|
||||||
reads, writes, factors);
|
reads, writes, factors, patches);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libs::Graphics::ShaderRecompiler
|
} // namespace Libs::Graphics::ShaderRecompiler
|
||||||
|
|||||||
@@ -104,9 +104,8 @@ struct EmitterState {
|
|||||||
std::array<uint32_t, 3> u32_vector_types {};
|
std::array<uint32_t, 3> u32_vector_types {};
|
||||||
std::array<uint32_t, 3> i32_vector_types {};
|
std::array<uint32_t, 3> i32_vector_types {};
|
||||||
std::array<uint32_t, 3> f32_vector_types {};
|
std::array<uint32_t, 3> f32_vector_types {};
|
||||||
std::array<uint32_t, 6> tess_variables {};
|
std::array<uint32_t, IR::NumTessellationAttributes> tess_variables {};
|
||||||
uint32_t tess_inner_variable = 0;
|
uint32_t tess_inner_variable = 0;
|
||||||
uint32_t tess_patch_base = 0;
|
|
||||||
|
|
||||||
uint32_t lane_count = 1;
|
uint32_t lane_count = 1;
|
||||||
uint32_t lane_half = 0;
|
uint32_t lane_half = 0;
|
||||||
|
|||||||
@@ -5,6 +5,17 @@
|
|||||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
bool IsTessellationInput(IR::TessellationAttribute kind) {
|
||||||
|
using Attribute = IR::TessellationAttribute;
|
||||||
|
return kind == Attribute::ControlInput || kind == Attribute::EvaluationInput ||
|
||||||
|
kind == Attribute::PatchInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsTessellationPatch(IR::TessellationAttribute kind) {
|
||||||
|
using Attribute = IR::TessellationAttribute;
|
||||||
|
return kind == Attribute::PatchOutput || kind == Attribute::PatchInput;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t TessellationPointer(ValueEmitContext& ctx, const IR::Inst& inst) {
|
uint32_t TessellationPointer(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||||
auto& state = ctx.state;
|
auto& state = ctx.state;
|
||||||
using Attribute = IR::TessellationAttribute;
|
using Attribute = IR::TessellationAttribute;
|
||||||
@@ -12,8 +23,9 @@ uint32_t TessellationPointer(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
const auto& tess = state.input_info.vertex->tess;
|
const auto& tess = state.input_info.vertex->tess;
|
||||||
const auto variable = state.tess_variables.at(static_cast<uint32_t>(kind));
|
const auto variable = state.tess_variables.at(static_cast<uint32_t>(kind));
|
||||||
EXIT_IF(variable == 0);
|
EXIT_IF(variable == 0);
|
||||||
const auto input = kind == Attribute::ControlInput || kind == Attribute::EvaluationInput;
|
const bool patch = IsTessellationPatch(kind);
|
||||||
const auto storage = input ? spv::StorageClassInput : spv::StorageClassOutput;
|
const auto storage =
|
||||||
|
IsTessellationInput(kind) ? spv::StorageClassInput : spv::StorageClassOutput;
|
||||||
const auto pointer = state.builder.AllocateId();
|
const auto pointer = state.builder.AllocateId();
|
||||||
if (kind == Attribute::Factor) {
|
if (kind == Attribute::Factor) {
|
||||||
EXIT_NOT_IMPLEMENTED(!inst.Arg(1).IsImmediate());
|
EXIT_NOT_IMPLEMENTED(!inst.Arg(1).IsImmediate());
|
||||||
@@ -26,15 +38,13 @@ uint32_t TessellationPointer(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
return pointer;
|
return pointer;
|
||||||
}
|
}
|
||||||
auto address = ctx.Arg(inst, 1);
|
auto address = ctx.Arg(inst, 1);
|
||||||
if (kind == Attribute::PatchOutput) {
|
if (patch) {
|
||||||
address =
|
address = EmitBinaryU32(state, spv::OpISub, address, ConstantU32(state, tess.patch_base));
|
||||||
EmitBinaryU32(state, spv::OpISub, address, ConstantU32(state, state.tess_patch_base));
|
|
||||||
}
|
}
|
||||||
const bool local = kind == Attribute::LocalOutput || kind == Attribute::ControlInput;
|
const bool local = kind == Attribute::LocalOutput || kind == Attribute::ControlInput;
|
||||||
const auto stride = local ? tess.ls_stride : tess.hs_stride;
|
const auto stride = local ? tess.ls_stride : tess.hs_stride;
|
||||||
const auto offset = kind == Attribute::PatchOutput ? address
|
const auto offset =
|
||||||
: EmitBinaryU32(state, spv::OpUMod, address,
|
patch ? address : EmitBinaryU32(state, spv::OpUMod, address, ConstantU32(state, stride));
|
||||||
ConstantU32(state, stride));
|
|
||||||
const auto attribute =
|
const auto attribute =
|
||||||
EmitBinaryU32(state, spv::OpShiftRightLogical, offset, ConstantU32(state, 4));
|
EmitBinaryU32(state, spv::OpShiftRightLogical, offset, ConstantU32(state, 4));
|
||||||
const auto component =
|
const auto component =
|
||||||
@@ -42,7 +52,7 @@ uint32_t TessellationPointer(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
EmitBinaryU32(state, spv::OpShiftRightLogical, offset, ConstantU32(state, 2)),
|
EmitBinaryU32(state, spv::OpShiftRightLogical, offset, ConstantU32(state, 2)),
|
||||||
ConstantU32(state, 3));
|
ConstantU32(state, 3));
|
||||||
const auto type = TypePointer(state, storage, TypeU32(state));
|
const auto type = TypePointer(state, storage, TypeU32(state));
|
||||||
if (kind == Attribute::LocalOutput || kind == Attribute::PatchOutput) {
|
if (kind == Attribute::LocalOutput || patch) {
|
||||||
state.builder.AddFunction(spv::OpAccessChain, type, pointer, variable, attribute,
|
state.builder.AddFunction(spv::OpAccessChain, type, pointer, variable, attribute,
|
||||||
component);
|
component);
|
||||||
} else {
|
} else {
|
||||||
@@ -60,20 +70,12 @@ uint32_t TessellationPointer(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
|
|
||||||
void DefineTessellationInterfaces(EmitterState& state) {
|
void DefineTessellationInterfaces(EmitterState& state) {
|
||||||
using Attribute = IR::TessellationAttribute;
|
using Attribute = IR::TessellationAttribute;
|
||||||
std::array<bool, 6> used {};
|
std::array<bool, IR::NumTessellationAttributes> used {};
|
||||||
uint32_t patch_begin = UINT32_MAX, patch_end = 0;
|
|
||||||
for (const auto* block: state.program.blocks) {
|
for (const auto* block: state.program.blocks) {
|
||||||
for (const auto& inst: *block) {
|
for (const auto& inst: *block) {
|
||||||
if (inst.GetOpcode() != IR::ValueOpcode::GetTessellationAttribute &&
|
if (inst.GetOpcode() == IR::ValueOpcode::GetTessellationAttribute ||
|
||||||
inst.GetOpcode() != IR::ValueOpcode::SetTessellationAttribute)
|
inst.GetOpcode() == IR::ValueOpcode::SetTessellationAttribute)
|
||||||
continue;
|
used.at(inst.Arg(0).U32()) = true;
|
||||||
const auto kind = inst.Arg(0).U32();
|
|
||||||
used.at(kind) = true;
|
|
||||||
if (kind == static_cast<uint32_t>(Attribute::PatchOutput)) {
|
|
||||||
EXIT_NOT_IMPLEMENTED(!inst.Arg(1).IsImmediate());
|
|
||||||
patch_begin = std::min(patch_begin, inst.Arg(1).U32());
|
|
||||||
patch_end = std::max(patch_end, inst.Arg(1).U32() + 4u);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (std::ranges::none_of(used, [](bool value) { return value; })) return;
|
if (std::ranges::none_of(used, [](bool value) { return value; })) return;
|
||||||
@@ -83,15 +85,16 @@ void DefineTessellationInterfaces(EmitterState& state) {
|
|||||||
};
|
};
|
||||||
for (uint32_t index = 0; index < used.size(); index++) {
|
for (uint32_t index = 0; index < used.size(); index++) {
|
||||||
if (!used[index]) continue;
|
if (!used[index]) continue;
|
||||||
const auto kind = static_cast<Attribute>(index);
|
const auto kind = static_cast<Attribute>(index);
|
||||||
const bool input = kind == Attribute::ControlInput || kind == Attribute::EvaluationInput;
|
const bool patch = IsTessellationPatch(kind);
|
||||||
const auto storage = input ? spv::StorageClassInput : spv::StorageClassOutput;
|
const auto storage =
|
||||||
uint32_t type;
|
IsTessellationInput(kind) ? spv::StorageClassInput : spv::StorageClassOutput;
|
||||||
|
uint32_t type;
|
||||||
if (kind == Attribute::Factor) {
|
if (kind == Attribute::Factor) {
|
||||||
type = array(TypeF32(state), 4u);
|
type = array(TypeF32(state), 4u);
|
||||||
} else if (kind == Attribute::PatchOutput) {
|
} else if (patch) {
|
||||||
state.tess_patch_base = patch_begin;
|
// HS and TES share the reflected patch layout so their interfaces match.
|
||||||
type = array(TypeU32Vector(state, 4), (patch_end - patch_begin + 15u) / 16u);
|
type = array(TypeU32Vector(state, 4), (tess.patch_size + 15u) / 16u);
|
||||||
} else {
|
} else {
|
||||||
const bool local = kind == Attribute::LocalOutput || kind == Attribute::ControlInput;
|
const bool local = kind == Attribute::LocalOutput || kind == Attribute::ControlInput;
|
||||||
const auto stride = local ? tess.ls_stride : tess.hs_stride;
|
const auto stride = local ? tess.ls_stride : tess.hs_stride;
|
||||||
@@ -112,11 +115,10 @@ void DefineTessellationInterfaces(EmitterState& state) {
|
|||||||
state.builder.AddAnnotation(spv::OpDecorate, state.tess_inner_variable,
|
state.builder.AddAnnotation(spv::OpDecorate, state.tess_inner_variable,
|
||||||
spv::DecorationPatch);
|
spv::DecorationPatch);
|
||||||
} else {
|
} else {
|
||||||
state.builder.AddAnnotation(
|
state.builder.AddAnnotation(spv::OpDecorate, variable, spv::DecorationLocation,
|
||||||
spv::OpDecorate, variable, spv::DecorationLocation,
|
patch ? (tess.hs_stride + 15u) / 16u : 0u);
|
||||||
kind == Attribute::PatchOutput ? (tess.hs_stride + 15u) / 16u : 0u);
|
|
||||||
}
|
}
|
||||||
if (kind == Attribute::Factor || kind == Attribute::PatchOutput) {
|
if (kind == Attribute::Factor || patch) {
|
||||||
state.builder.AddAnnotation(spv::OpDecorate, variable, spv::DecorationPatch);
|
state.builder.AddAnnotation(spv::OpDecorate, variable, spv::DecorationPatch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,8 +220,11 @@ enum class TessellationAttribute {
|
|||||||
ControlOutput,
|
ControlOutput,
|
||||||
EvaluationInput,
|
EvaluationInput,
|
||||||
PatchOutput,
|
PatchOutput,
|
||||||
|
PatchInput,
|
||||||
Factor
|
Factor
|
||||||
};
|
};
|
||||||
|
constexpr uint32_t NumTessellationAttributes =
|
||||||
|
static_cast<uint32_t>(TessellationAttribute::Factor) + 1u;
|
||||||
|
|
||||||
enum class StageInputKind {
|
enum class StageInputKind {
|
||||||
VertexIndex,
|
VertexIndex,
|
||||||
|
|||||||
@@ -441,7 +441,8 @@ void BuildStageStaticKey(const ShaderVertexInputInfo& info, std::vector<uint32_t
|
|||||||
if (info.tess.input_control_points != 0) {
|
if (info.tess.input_control_points != 0) {
|
||||||
const auto& tess = info.tess;
|
const auto& tess = info.tess;
|
||||||
key.insert(key.end(), {tess.output_control_points, tess.ls_stride, tess.hs_stride,
|
key.insert(key.end(), {tess.output_control_points, tess.ls_stride, tess.hs_stride,
|
||||||
tess.domain, tess.partitioning, tess.output_topology});
|
tess.patch_base, tess.patch_size, tess.domain, tess.partitioning,
|
||||||
|
tess.output_topology});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < info.resources_num; i++) {
|
for (int i = 0; i < info.resources_num; i++) {
|
||||||
|
|||||||
@@ -130,6 +130,8 @@ struct ShaderTessellationInputInfo {
|
|||||||
uint32_t output_control_points = 0;
|
uint32_t output_control_points = 0;
|
||||||
uint32_t ls_stride = 0;
|
uint32_t ls_stride = 0;
|
||||||
uint32_t hs_stride = 0;
|
uint32_t hs_stride = 0;
|
||||||
|
uint32_t patch_base = 0;
|
||||||
|
uint32_t patch_size = 0;
|
||||||
uint32_t domain = 0;
|
uint32_t domain = 0;
|
||||||
uint32_t partitioning = 0;
|
uint32_t partitioning = 0;
|
||||||
uint32_t output_topology = 0;
|
uint32_t output_topology = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user