mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
shader: keep fragment layer values under lane masks
Co-authored-by: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com>
This commit is contained in:
@@ -651,6 +651,9 @@ TranslateResult TranslateProgram(std::span<const uint32_t> code, const CompileOp
|
||||
IR::ResolveControlFlowIdentities(ir);
|
||||
IR::RemoveIdentities(ir.blocks);
|
||||
IR::EliminateDeadCode(ir.blocks);
|
||||
IR::EliminateMaskedValues(ir);
|
||||
IR::RemoveIdentities(ir.blocks);
|
||||
IR::EliminateDeadCode(ir.blocks);
|
||||
const auto read_lane_stats = IR::EliminateReadLane(ir, ir.wave_size);
|
||||
if (read_lane_stats.rewritten_reads != 0) {
|
||||
LOGF("%s read-lane elimination: reads=%" PRIu32 "\n", GetDumpLabel(options),
|
||||
|
||||
@@ -32,6 +32,10 @@ uint32_t Builder::AllocateId() {
|
||||
return m_next_id++;
|
||||
}
|
||||
|
||||
void Builder::RequireVersion(uint32_t version) {
|
||||
m_version = std::max(m_version, version);
|
||||
}
|
||||
|
||||
void Builder::RequireCapability(uint32_t capability) {
|
||||
if (m_required_capabilities.insert(capability).second) {
|
||||
AddCapability({capability});
|
||||
|
||||
@@ -28,6 +28,7 @@ public:
|
||||
KYTY_CLASS_DEFAULT_COPY(Builder);
|
||||
|
||||
uint32_t AllocateId();
|
||||
void RequireVersion(uint32_t version);
|
||||
void RequireCapability(uint32_t capability);
|
||||
void RequireExtension(const char* name);
|
||||
uint32_t Import(const char* name);
|
||||
|
||||
@@ -71,7 +71,8 @@ uint32_t EmitBuiltinU32(ValueEmitContext& ctx, IR::StageInputKind kind, uint32_t
|
||||
{OpSelect, TypeU32(state), bits, value, ConstantU32(state, 1), ConstantU32(state, 0)});
|
||||
return bits;
|
||||
}
|
||||
if (kind == IR::StageInputKind::VertexIndex || kind == IR::StageInputKind::InstanceIndex) {
|
||||
if (kind == IR::StageInputKind::VertexIndex || kind == IR::StageInputKind::InstanceIndex ||
|
||||
kind == IR::StageInputKind::Layer) {
|
||||
const auto value = state.builder.AllocateId();
|
||||
const auto bits = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpLoad, TypeI32(state), value, variable});
|
||||
|
||||
@@ -50,8 +50,8 @@ enum : uint32_t {
|
||||
CapabilityGroupNonUniform = 61,
|
||||
CapabilityGroupNonUniformBallot = 64,
|
||||
CapabilityGroupNonUniformShuffle = 65,
|
||||
CapabilityShaderLayer = 69,
|
||||
CapabilitySignedZeroInfNanPreserve = 4466,
|
||||
CapabilityShaderViewportIndexLayerEXT = 5254,
|
||||
CapabilityFragmentBarycentricKHR = 5284,
|
||||
CapabilityComputeDerivativeGroupQuadsKHR = 5288,
|
||||
CapabilityPhysicalStorageBufferAddresses = 5347,
|
||||
|
||||
@@ -518,6 +518,7 @@ uint32_t BuiltInForInput(IR::StageInputKind kind) {
|
||||
case IR::StageInputKind::InstanceIndex: return BuiltInInstanceIndex;
|
||||
case IR::StageInputKind::FragCoord: return BuiltInFragCoord;
|
||||
case IR::StageInputKind::FrontFacing: return BuiltInFrontFacing;
|
||||
case IR::StageInputKind::Layer: return BuiltInLayer;
|
||||
case IR::StageInputKind::BaryCoordSmooth: return BuiltInBaryCoordKHR;
|
||||
case IR::StageInputKind::BaryCoordNoPerspective: return BuiltInBaryCoordNoPerspKHR;
|
||||
case IR::StageInputKind::WorkgroupId: return BuiltInWorkgroupId;
|
||||
@@ -541,6 +542,9 @@ void AddInputAnnotationsAndNames(EmitterState& state) {
|
||||
}
|
||||
for (const auto& input: state.inputs) {
|
||||
state.builder.AddName(input.variable_id, input.debug_name.c_str());
|
||||
if (input.kind == IR::StageInputKind::Layer) {
|
||||
state.builder.AddAnnotation({OpDecorate, input.variable_id, DecorationFlat});
|
||||
}
|
||||
if (input.kind == IR::StageInputKind::Parameter) {
|
||||
const auto flat = PixelParameterIsFlat(state, input.location);
|
||||
if (input.per_vertex) {
|
||||
@@ -710,9 +714,9 @@ void DefineModule(EmitterState& state) {
|
||||
if (state.cull_distance_variable != 0) {
|
||||
state.builder.RequireCapability(CapabilityCullDistance);
|
||||
}
|
||||
if (state.layer_variable != 0) {
|
||||
state.builder.RequireCapability(CapabilityShaderViewportIndexLayerEXT);
|
||||
state.builder.RequireExtension("SPV_EXT_shader_viewport_index_layer");
|
||||
if (state.layer_variable != 0 || InputVariableForKind(state, IR::StageInputKind::Layer) != 0) {
|
||||
state.builder.RequireVersion(0x00010500u);
|
||||
state.builder.RequireCapability(CapabilityShaderLayer);
|
||||
}
|
||||
if (state.requirements.image_gather_extended) {
|
||||
state.builder.RequireCapability(CapabilityImageGatherExtended);
|
||||
@@ -805,6 +809,7 @@ void DefineModule(EmitterState& state) {
|
||||
switch (input.kind) {
|
||||
case IR::StageInputKind::VertexIndex:
|
||||
case IR::StageInputKind::InstanceIndex:
|
||||
case IR::StageInputKind::Layer:
|
||||
ptr_type = TypePointer(state, StorageClassInput, TypeI32(state));
|
||||
break;
|
||||
case IR::StageInputKind::WorkgroupId:
|
||||
|
||||
@@ -259,13 +259,20 @@ void Translator::S_MOV_B64(const Decoder::Instruction& inst) {
|
||||
}
|
||||
|
||||
void Translator::S_WQM_B64(const Decoder::Instruction& inst) {
|
||||
const auto mask_valid = ReadMaskValid(inst.src0);
|
||||
const auto source_mask = ReadMask(inst.src0);
|
||||
const auto mask_valid = ReadMaskValid(inst.src0);
|
||||
const auto result =
|
||||
IR::U64(ir.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src0, IR::Type::U64)}));
|
||||
// WQM activates whole quads and cannot clear an already active lane.
|
||||
const auto widened_mask = ir.LogicalOr(source_mask, ThreadBit(ExtractU64(result)));
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
|
||||
if (inst.dst.kind == Decoder::OperandKind::ExecLo) {
|
||||
ir.SetExec(widened_mask);
|
||||
} else if (inst.dst.kind == Decoder::OperandKind::VccLo) {
|
||||
ir.SetVcc(widened_mask);
|
||||
} else if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
|
||||
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
|
||||
ir.SetThreadBitScalarReg(dst, ThreadBit(ExtractU64(result)));
|
||||
ir.SetThreadBitScalarReg(dst, widened_mask);
|
||||
ir.SetScalarMaskTag(dst, mask_valid);
|
||||
}
|
||||
ir.SetScc(IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
|
||||
|
||||
@@ -1137,9 +1137,13 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
|
||||
builtin(IR::StageInputKind::FragCoord, 3));
|
||||
}
|
||||
if (ps->ps_front_face) {
|
||||
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(reg),
|
||||
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(reg++),
|
||||
builtin(IR::StageInputKind::FrontFacing));
|
||||
}
|
||||
if (ps->ps_ancillary) {
|
||||
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(reg),
|
||||
builtin(IR::StageInputKind::PackedAncillary));
|
||||
}
|
||||
} else if (options.stage == ShaderType::Vertex) {
|
||||
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(5),
|
||||
builtin(IR::StageInputKind::VertexIndex));
|
||||
|
||||
@@ -160,6 +160,8 @@ enum class StageInputKind {
|
||||
InstanceIndex,
|
||||
FragCoord,
|
||||
FrontFacing,
|
||||
PackedAncillary,
|
||||
Layer,
|
||||
BaryCoordSmooth,
|
||||
BaryCoordNoPerspective,
|
||||
WorkgroupId,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/ConstantPropagation.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
namespace {
|
||||
@@ -116,6 +118,87 @@ bool FoldSelect(Inst& inst) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsSelect(ValueOpcode opcode) {
|
||||
return opcode == ValueOpcode::SelectU1 || opcode == ValueOpcode::SelectU32 ||
|
||||
opcode == ValueOpcode::SelectF32;
|
||||
}
|
||||
|
||||
bool Implies(Value condition, Value required, uint32_t depth = 0) {
|
||||
condition = condition.Resolve();
|
||||
if (condition == required) return true;
|
||||
const auto* inst = condition.TryInstruction();
|
||||
return depth < 64u && inst != nullptr && inst->GetOpcode() == ValueOpcode::LogicalAnd &&
|
||||
(Implies(inst->Arg(0), required, depth + 1u) ||
|
||||
Implies(inst->Arg(1), required, depth + 1u));
|
||||
}
|
||||
|
||||
bool IsAcyclic(const BlockList& blocks) {
|
||||
std::unordered_map<const Block*, bool> completed;
|
||||
const auto visit = [&](auto&& self, const Block* block) -> bool {
|
||||
const auto [entry, added] = completed.emplace(block, false);
|
||||
if (!added) return entry->second;
|
||||
for (const auto* successor: block->ImmSuccessors()) {
|
||||
if (!self(self, successor)) return false;
|
||||
}
|
||||
completed[block] = true;
|
||||
return true;
|
||||
};
|
||||
for (const auto* block: blocks) {
|
||||
if (!visit(visit, block)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class MaskedUses {
|
||||
public:
|
||||
MaskedUses(const Program& program, Value condition)
|
||||
: program(program), condition(condition.Resolve()) {}
|
||||
|
||||
bool OnlyWhenActive(const Inst& inst) {
|
||||
const auto [entry, added] = completed.emplace(&inst, false);
|
||||
if (!added) return entry->second;
|
||||
for (const auto& use: inst.Uses()) {
|
||||
const auto& user = *use.user;
|
||||
const auto op = user.GetOpcode();
|
||||
if (IsSelect(op)) {
|
||||
if (use.operand == 0) return false;
|
||||
if (use.operand == 1 && Implies(user.Arg(0), condition)) continue;
|
||||
} else if (op == ValueOpcode::SetAttribute) {
|
||||
if (use.operand == 0 && Implies(user.Arg(1), condition)) continue;
|
||||
return false;
|
||||
} else if (!IsLaneLocal(user, use.operand)) {
|
||||
return false;
|
||||
}
|
||||
if (!OnlyWhenActive(user)) return false;
|
||||
}
|
||||
completed[&inst] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool IsLaneLocal(const Inst& inst, size_t operand) const {
|
||||
const auto op = inst.GetOpcode();
|
||||
// These opcode-table groups contain only construction, conversion and arithmetic.
|
||||
if ((op >= ValueOpcode::BitCastU16F16 && op <= ValueOpcode::BitFieldSExtract) ||
|
||||
(op >= ValueOpcode::WqmU64 && op <= ValueOpcode::FPFract32) ||
|
||||
op == ValueOpcode::Identity || op == ValueOpcode::Phi ||
|
||||
op == ValueOpcode::MakeImageAddress) {
|
||||
return true;
|
||||
}
|
||||
if (op != ValueOpcode::ImageSampleRaw || operand != 2) return false;
|
||||
// Explicit LOD or gradients keep coordinates local; implicit derivatives read neighbors.
|
||||
const auto index = inst.Flags<MemoryFlags>().index;
|
||||
return index < program.memory_info.size() &&
|
||||
(program.memory_info[index].image_sample_flags &
|
||||
(Decoder::ImageSampleFlagLod | Decoder::ImageSampleFlagLevelZero |
|
||||
Decoder::ImageSampleFlagDerivative)) != 0;
|
||||
}
|
||||
|
||||
const Program& program;
|
||||
Value condition;
|
||||
std::unordered_map<const Inst*, bool> completed;
|
||||
};
|
||||
|
||||
bool FoldPhi(Inst& inst) {
|
||||
Value same;
|
||||
for (size_t index = 0; index < inst.NumArgs(); index++) {
|
||||
@@ -192,7 +275,8 @@ bool FoldCompositeExtract(Inst& inst, ValueOpcode construct, size_t components)
|
||||
return false;
|
||||
}
|
||||
|
||||
void FoldInstruction(Inst& inst) {
|
||||
void FoldInstruction(Block& block, Block::iterator instruction) {
|
||||
auto& inst = *instruction;
|
||||
switch (inst.GetOpcode()) {
|
||||
case ValueOpcode::Phi: FoldPhi(inst); return;
|
||||
case ValueOpcode::SelectU1:
|
||||
@@ -223,6 +307,21 @@ void FoldInstruction(Inst& inst) {
|
||||
const auto value = Arg(inst, 0);
|
||||
const auto offset = Arg(inst, 1);
|
||||
const auto count = Arg(inst, 2);
|
||||
const auto* source = value.TryInstruction();
|
||||
if (source != nullptr && source->GetOpcode() == ValueOpcode::GetBuiltin &&
|
||||
source->Arg(0) == Value(static_cast<uint32_t>(StageInputKind::PackedAncillary)) &&
|
||||
IsImmediate(offset, Type::U32) && IsImmediate(count, Type::U32) &&
|
||||
offset.U32() >= 16u && offset.U32() < 27u && count.U32() != 0u &&
|
||||
count.U32() <= 27u - offset.U32()) {
|
||||
// Preserve the extraction (including sign extension) while exposing only its
|
||||
// used hardware field. Other live ancillary fields remain unsupported.
|
||||
const auto layer = block.PrependNewInst(
|
||||
instruction, ValueOpcode::GetBuiltin,
|
||||
{Value(static_cast<uint32_t>(StageInputKind::Layer)), Value(0u)});
|
||||
inst.SetArg(0, Value(&*layer));
|
||||
inst.SetArg(1, Value(offset.U32() - 16u));
|
||||
return;
|
||||
}
|
||||
if (!IsImmediate(value, Type::U32) || !IsImmediate(offset, Type::U32) ||
|
||||
!IsImmediate(count, Type::U32) || offset.U32() > 32u ||
|
||||
count.U32() > 32u - offset.U32()) {
|
||||
@@ -584,8 +683,22 @@ void FoldInstruction(Inst& inst) {
|
||||
|
||||
void ConstantPropagationPass(const BlockList& blocks) {
|
||||
for (auto* block: blocks) {
|
||||
for (auto& inst: block->Instructions()) {
|
||||
FoldInstruction(inst);
|
||||
for (auto inst = block->begin(); inst != block->end(); ++inst) {
|
||||
FoldInstruction(*block, inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EliminateMaskedValues(Program& program) {
|
||||
// A loop-carried value may have been produced under a previous iteration's predicate.
|
||||
if (!IsAcyclic(program.blocks)) return;
|
||||
for (auto block = program.blocks.rbegin(); block != program.blocks.rend(); ++block) {
|
||||
auto& instructions = (*block)->Instructions();
|
||||
for (auto inst = instructions.rbegin(); inst != instructions.rend(); ++inst) {
|
||||
if (IsSelect(inst->GetOpcode()) &&
|
||||
MaskedUses(program, inst->Arg(0)).OnlyWhenActive(*inst)) {
|
||||
Replace(*inst, inst->Arg(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
|
||||
struct Program;
|
||||
|
||||
void ConstantPropagationPass(const BlockList& blocks);
|
||||
void EliminateMaskedValues(Program& program);
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
|
||||
@@ -115,6 +115,13 @@ void ValidateValueReferences(const Program& program, const ShaderInfoOptions& op
|
||||
const auto kind = static_cast<StageInputKind>(inst.Arg(0).U32());
|
||||
const auto component = inst.Arg(1).U32();
|
||||
switch (kind) {
|
||||
case StageInputKind::PackedAncillary:
|
||||
return Fail("packed pixel ancillary input has an unsupported live use");
|
||||
case StageInputKind::Layer:
|
||||
if (program.stage != ShaderType::Pixel || component != 0u) {
|
||||
return Fail("typed layer input is invalid");
|
||||
}
|
||||
break;
|
||||
case StageInputKind::VertexIndex:
|
||||
case StageInputKind::InstanceIndex:
|
||||
case StageInputKind::FrontFacing:
|
||||
@@ -251,6 +258,7 @@ void CollectBuiltinInputs(const Program& program, ShaderInfo& info) {
|
||||
case StageInputKind::FrontFacing:
|
||||
AddInput(info, kind, 0, 1, "gl_FrontFacing");
|
||||
break;
|
||||
case StageInputKind::Layer: AddInput(info, kind, 0, 1, "gl_Layer"); break;
|
||||
case StageInputKind::BaryCoordSmooth:
|
||||
AddInput(info, kind, 0, 3, "gl_BaryCoordKHR");
|
||||
break;
|
||||
@@ -269,6 +277,7 @@ void CollectBuiltinInputs(const Program& program, ShaderInfo& info) {
|
||||
case StageInputKind::GlobalInvocationId:
|
||||
AddInput(info, kind, 0, 3, "gl_GlobalInvocationID");
|
||||
break;
|
||||
case StageInputKind::PackedAncillary:
|
||||
case StageInputKind::Parameter: break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,6 +699,7 @@ static void ShaderGetStaticInputInfoPS(
|
||||
ps_info.ps_pos_z = (active_inputs & 0x00000400u) != 0;
|
||||
ps_info.ps_pos_w = (active_inputs & 0x00000800u) != 0;
|
||||
ps_info.ps_front_face = (active_inputs & 0x00001000u) != 0;
|
||||
ps_info.ps_ancillary = (active_inputs & 0x00002000u) != 0;
|
||||
ps_info.ps_sample_shading = (active_inputs & 0x00000011u) != 0;
|
||||
ps_info.ps_no_perspective = (sh.ps_input_ena & sh.ps_input_addr & 0x00000020u) != 0;
|
||||
ps_info.ps_pixel_kill_enable = sh.db_shader_control.shader_kill_enable;
|
||||
@@ -808,6 +809,7 @@ void BuildStageStaticKey(const ShaderPixelInputInfo& info, std::vector<uint32_t>
|
||||
key.push_back(static_cast<uint32_t>(info.ps_pos_z));
|
||||
key.push_back(static_cast<uint32_t>(info.ps_pos_w));
|
||||
key.push_back(static_cast<uint32_t>(info.ps_front_face));
|
||||
key.push_back(static_cast<uint32_t>(info.ps_ancillary));
|
||||
key.push_back(static_cast<uint32_t>(info.ps_no_perspective));
|
||||
key.push_back(static_cast<uint32_t>(info.ps_pixel_kill_enable));
|
||||
key.push_back(static_cast<uint32_t>(info.ps_depth_export_enable));
|
||||
@@ -985,6 +987,7 @@ void ShaderDbgDumpInputInfo(const ShaderPixelInputInfo& info) {
|
||||
"\t ps_pos_z = %s\n"
|
||||
"\t ps_pos_w = %s\n"
|
||||
"\t ps_front_face = %s\n"
|
||||
"\t ps_ancillary = %s\n"
|
||||
"\t ps_sample_shading = %s\n"
|
||||
"\t ps_no_perspective = %s\n"
|
||||
"\t ps_pixel_kill_enable = %s\n"
|
||||
@@ -994,6 +997,7 @@ void ShaderDbgDumpInputInfo(const ShaderPixelInputInfo& info) {
|
||||
info.ps_perspective_center_vgpr, info.ps_pos_x ? "true" : "false",
|
||||
info.ps_pos_y ? "true" : "false", info.ps_pos_z ? "true" : "false",
|
||||
info.ps_pos_w ? "true" : "false", info.ps_front_face ? "true" : "false",
|
||||
info.ps_ancillary ? "true" : "false",
|
||||
info.ps_sample_shading ? "true" : "false", info.ps_no_perspective ? "true" : "false",
|
||||
info.ps_pixel_kill_enable ? "true" : "false", info.ps_early_z ? "true" : "false",
|
||||
info.ps_execute_on_noop ? "true" : "false");
|
||||
|
||||
@@ -125,6 +125,7 @@ struct ShaderPixelInputInfo {
|
||||
bool ps_pos_z = false;
|
||||
bool ps_pos_w = false;
|
||||
bool ps_front_face = false;
|
||||
bool ps_ancillary = false;
|
||||
bool ps_no_perspective = false;
|
||||
bool ps_pixel_kill_enable = false;
|
||||
bool ps_depth_export_enable = false;
|
||||
|
||||
@@ -1032,6 +1032,9 @@ struct GraphicsCase {
|
||||
std::vector<u32> pixel_interpolator_settings;
|
||||
bool pixel_no_perspective = false;
|
||||
std::vector<u32> vertices;
|
||||
bool pixel_ancillary = false;
|
||||
bool pixel_front_face = false;
|
||||
u32 layers = 1;
|
||||
};
|
||||
|
||||
struct CompiledShader {
|
||||
@@ -1373,6 +1376,9 @@ CompiledShader CompileFragmentCase(const GraphicsCase &test) {
|
||||
? 1u
|
||||
: static_cast<u32>(test.pixel_interpolator_settings.size());
|
||||
pixel_info.ps_no_perspective = test.pixel_no_perspective;
|
||||
pixel_info.ps_ancillary = test.pixel_ancillary;
|
||||
pixel_info.ps_front_face = test.pixel_front_face;
|
||||
pixel_info.ps_system_input_base = 2;
|
||||
for (u32 i = 0; i < std::size(pixel_info.interpolator_settings); i++) {
|
||||
pixel_info.interpolator_settings[i] = i;
|
||||
}
|
||||
@@ -1457,12 +1463,13 @@ enum : u32 {
|
||||
OpReturn = 253,
|
||||
};
|
||||
|
||||
std::vector<u32> MakePassthroughVertexSpirv() {
|
||||
std::vector<u32> MakePassthroughVertexSpirv(bool layered) {
|
||||
using ShaderRecompiler::Spirv::Builder;
|
||||
|
||||
Builder b;
|
||||
const auto void_type = b.Type(OpTypeVoid);
|
||||
const auto uint_type = b.Type(OpTypeInt, {32, 0});
|
||||
const auto int_type = b.Type(OpTypeInt, {32, 1});
|
||||
const auto float_type = b.Type(OpTypeFloat, {32});
|
||||
const auto vec2_type = b.Type(OpTypeVector, {float_type, 2});
|
||||
const auto vec4_type = b.Type(OpTypeVector, {float_type, 4});
|
||||
@@ -1489,6 +1496,20 @@ std::vector<u32> MakePassthroughVertexSpirv() {
|
||||
b.DefineGlobalVariable(ptr_output_vec4, StorageClassOutput);
|
||||
const auto per_vertex =
|
||||
b.DefineGlobalVariable(ptr_output_per_vertex, StorageClassOutput);
|
||||
u32 instance = 0;
|
||||
u32 layer = 0;
|
||||
std::vector<u32> interfaces = {in_pos, in_color, per_vertex, out_color};
|
||||
if (layered) {
|
||||
instance = b.DefineGlobalVariable(
|
||||
b.Type(OpTypePointer, {StorageClassInput, int_type}), StorageClassInput);
|
||||
layer = b.DefineGlobalVariable(
|
||||
b.Type(OpTypePointer, {StorageClassOutput, int_type}), StorageClassOutput);
|
||||
b.AddAnnotation({OpDecorate, instance, DecorationBuiltIn, 43}); // InstanceIndex
|
||||
b.AddAnnotation({OpDecorate, layer, DecorationBuiltIn, 9}); // Layer
|
||||
b.RequireVersion(0x00010500u);
|
||||
b.RequireCapability(69); // ShaderLayer
|
||||
interfaces.insert(interfaces.end(), {instance, layer});
|
||||
}
|
||||
const auto main = b.AllocateId();
|
||||
const auto label = b.AllocateId();
|
||||
const auto pos2 = b.AllocateId();
|
||||
@@ -1500,8 +1521,7 @@ std::vector<u32> MakePassthroughVertexSpirv() {
|
||||
|
||||
b.RequireCapability(CapabilityShader);
|
||||
b.AddMemoryModel({AddressingModelLogical, MemoryModelGLSL450});
|
||||
b.AddEntryPoint(ExecutionModelVertex, main, "main",
|
||||
{in_pos, in_color, per_vertex, out_color});
|
||||
b.AddEntryPoint(ExecutionModelVertex, main, "main", interfaces);
|
||||
b.AddAnnotation({OpDecorate, in_pos, DecorationLocation, 0});
|
||||
b.AddAnnotation({OpDecorate, in_color, DecorationLocation, 1});
|
||||
b.AddAnnotation({OpDecorate, out_color, DecorationLocation, 0});
|
||||
@@ -1518,6 +1538,11 @@ std::vector<u32> MakePassthroughVertexSpirv() {
|
||||
{OpAccessChain, ptr_output_vec4, position_ptr, per_vertex, const_u32_0});
|
||||
b.AddFunction({OpStore, position_ptr, position});
|
||||
b.AddFunction({OpStore, out_color, color4});
|
||||
if (layered) {
|
||||
const auto index = b.AllocateId();
|
||||
b.AddFunction({OpLoad, int_type, index, instance});
|
||||
b.AddFunction({OpStore, layer, index});
|
||||
}
|
||||
b.AddFunction({OpReturn});
|
||||
b.AddFunction({OpFunctionEnd});
|
||||
return b.Build();
|
||||
@@ -10343,13 +10368,15 @@ public:
|
||||
|
||||
std::vector<u32> RenderFragment(const GraphicsCase &test,
|
||||
const CompiledShader &fragment) {
|
||||
const auto vertex_spirv = TestSpv::MakePassthroughVertexSpirv();
|
||||
const auto vertex_spirv = TestSpv::MakePassthroughVertexSpirv(test.layers > 1);
|
||||
ValidateSpirv(test.name, vertex_spirv);
|
||||
|
||||
Image target =
|
||||
CreateImage2D(test.name, 1, 1, vk::Format::eR32G32B32A32Sfloat,
|
||||
CreateImageMips(test.name, 1, 1, vk::Format::eR32G32B32A32Sfloat,
|
||||
vk::ImageUsageFlagBits::eColorAttachment, {}, 4,
|
||||
vk::ImageLayout::eGeneral);
|
||||
vk::ImageLayout::eGeneral, vk::ImageType::e2D,
|
||||
test.layers > 1 ? vk::ImageViewType::e2DArray : vk::ImageViewType::e2D,
|
||||
test.layers);
|
||||
const std::vector<u32> default_vertices = {
|
||||
0xbf800000u, 0xbf800000u, 0x3e800000u, 0x3f000000u, 0x3f400000u,
|
||||
0x3f800000u, 0x40400000u, 0xbf800000u, 0x3e800000u, 0x3f000000u,
|
||||
@@ -10507,7 +10534,7 @@ public:
|
||||
vk::RenderingInfo rendering{};
|
||||
rendering.sType = vk::StructureType::eRenderingInfo;
|
||||
rendering.renderArea.extent = {1, 1};
|
||||
rendering.layerCount = 1;
|
||||
rendering.layerCount = test.layers;
|
||||
rendering.colorAttachmentCount = 1;
|
||||
rendering.pColorAttachments = &color;
|
||||
cmd.beginRendering(rendering);
|
||||
@@ -10521,13 +10548,13 @@ public:
|
||||
}
|
||||
vk::DeviceSize offset = 0;
|
||||
cmd.bindVertexBuffers(0, 1, &vertex_buffer.buffer, &offset);
|
||||
cmd.draw(3, 1, 0, 0);
|
||||
cmd.draw(3, test.layers, 0, 0);
|
||||
cmd.endRendering();
|
||||
EndSubmitAndFree(test.name, "graphics", cmd);
|
||||
target.layout = vk::ImageLayout::eGeneral;
|
||||
|
||||
auto pixel = ReadImage(test.name, &target);
|
||||
pixel.resize(4);
|
||||
pixel.resize(4 * test.layers);
|
||||
|
||||
m_device.destroyPipeline(pipeline, nullptr);
|
||||
m_device.destroyPipelineLayout(pipeline_layout, nullptr);
|
||||
@@ -11762,6 +11789,8 @@ private:
|
||||
Require("VulkanHarness", "dispatch",
|
||||
available_features12.bufferDeviceAddress == true,
|
||||
"bufferDeviceAddress is not supported");
|
||||
Require("VulkanHarness", "graphics", available_features12.shaderOutputLayer == true,
|
||||
"vertex layer output is not supported");
|
||||
|
||||
float priority = 1.0f;
|
||||
vk::DeviceQueueCreateInfo queue_info{};
|
||||
@@ -11779,6 +11808,7 @@ private:
|
||||
vk::StructureType::ePhysicalDeviceVulkan12Features;
|
||||
device_features12.timelineSemaphore = true;
|
||||
device_features12.bufferDeviceAddress = true;
|
||||
device_features12.shaderOutputLayer = true;
|
||||
vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR barycentric{};
|
||||
barycentric.sType = vk::StructureType::ePhysicalDeviceFragmentShaderBarycentricFeaturesKHR;
|
||||
barycentric.pNext = &device_features12;
|
||||
@@ -13822,6 +13852,50 @@ TestCase ScalarWqmB64SelectsSccDomain() {
|
||||
O::S_ENDPGM}};
|
||||
}
|
||||
|
||||
TestCase ScalarWqmB64PreservesPartialMasks() {
|
||||
using O = ShaderOpcode;
|
||||
TestCase test;
|
||||
test.name = "ScalarWqmB64PreservesPartialMasks";
|
||||
auto &code = test.code;
|
||||
code.push_back(EncodeSop1(0x04, 16, 126)); // Save full EXEC.
|
||||
AppendVMovU32(&code, 1, 3);
|
||||
AppendVMovU32(&code, 2, 9);
|
||||
u32 offset = 0;
|
||||
for (const u32 destination : {126u, 106u, 8u}) {
|
||||
code.push_back(EncodeVopc(0xc2, Vgpr(0), 1)); // Only lane 3.
|
||||
code.push_back(EncodeSop1(0x04, destination, 106));
|
||||
code.push_back(EncodeSop1(0x0a, destination, destination));
|
||||
code.push_back(EncodeSop1(0x04, 126, destination));
|
||||
AppendStoreVgprAtLaneDwordOffset(&code, 2, 0, offset);
|
||||
code.push_back(EncodeSop1(0x04, 126, 16));
|
||||
offset += 64;
|
||||
}
|
||||
code.push_back(EncodeSMovB32(8, InlineU32(32))); // Raw bits 5 and 33.
|
||||
code.push_back(EncodeSMovB32(9, InlineU32(2)));
|
||||
code.push_back(EncodeSop1(0x0a, 10, 8));
|
||||
code.push_back(EncodeSop1(0x04, 126, 10));
|
||||
AppendStoreVgprAtLaneDwordOffset(&code, 2, 0, offset);
|
||||
AppendEnd(&code);
|
||||
test.initial.assign(256, 0xdeadbeef);
|
||||
test.expected = test.initial;
|
||||
for (u32 lane = 0; lane < 64; ++lane) {
|
||||
for (u32 region = 0; region < 3; ++region) {
|
||||
if (lane < 4) test.expected[region * 64 + lane] = 9;
|
||||
}
|
||||
if ((lane >= 4 && lane < 8) || (lane >= 32 && lane < 36)) {
|
||||
test.expected[192 + lane] = 9;
|
||||
}
|
||||
}
|
||||
test.opcodes = {O::S_MOV_B64, O::S_MOV_B32, O::S_WQM_B64, O::V_MOV_B32,
|
||||
O::V_CMP_EQ_U32, O::V_LSHLREV_B32, O::BUFFER_STORE_DWORD,
|
||||
O::S_ENDPGM};
|
||||
test.compute_info.threads_num[0] = 64;
|
||||
test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1;
|
||||
test.compute_info.thread_ids_num = 1;
|
||||
test.has_compute_info = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase ScalarMaskProvenanceOverlapAndMixedBinary() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -21701,6 +21775,42 @@ GraphicsCase GraphicsInterpolationExport() {
|
||||
O::V_MOV_B32, O::EXP, O::S_ENDPGM}};
|
||||
}
|
||||
|
||||
GraphicsCase GraphicsAncillaryLayer(bool front_face) {
|
||||
GraphicsCase test;
|
||||
test.name = front_face ? "GraphicsAncillaryAfterFrontFace" : "GraphicsAncillaryLayer";
|
||||
test.pixel_ancillary = true;
|
||||
test.pixel_front_face = front_face;
|
||||
test.layers = 8;
|
||||
const auto ancillary = Vgpr(front_face ? 3 : 2);
|
||||
test.fragment_code.push_back(EncodeSop1(0x04, 8, 126));
|
||||
test.fragment_code.push_back(EncodeSop1(0x0a, 126, 126));
|
||||
AppendVop3(&test.fragment_code, 0x148, 4, ancillary, InlineU32(16), InlineU32(11));
|
||||
AppendVop3(&test.fragment_code, 0x149, 5, ancillary, InlineU32(16), InlineU32(3));
|
||||
test.fragment_code.push_back(EncodeSop1(0x04, 126, 8));
|
||||
AppendVMovU32(&test.fragment_code, 6, test.layers);
|
||||
test.fragment_code.push_back(EncodeVopc(0xc1, Vgpr(4), 6)); // Dynamic layer < count.
|
||||
test.fragment_code.push_back(EncodeSop1(0x04, 126, 106));
|
||||
test.fragment_code.push_back(EncodeVop1(0x06, 0, Vgpr(4))); // float(unsigned layer)
|
||||
test.fragment_code.push_back(EncodeVop1(0x05, 1, Vgpr(5))); // float(signed low 3 bits)
|
||||
AppendVMovLiteral(&test.fragment_code, 2, 0);
|
||||
AppendVMovLiteral(&test.fragment_code, 3, 0x3f800000u);
|
||||
test.fragment_code.push_back(EncodeExp0(0x00, 0xf));
|
||||
test.fragment_code.push_back(EncodeExp1(0, 1, 2, 3));
|
||||
AppendEnd(&test.fragment_code);
|
||||
test.opcodes = {ShaderOpcode::V_BFE_U32, ShaderOpcode::V_BFE_I32,
|
||||
ShaderOpcode::V_CVT_F32_U32, ShaderOpcode::V_CVT_F32_I32,
|
||||
ShaderOpcode::V_MOV_B32, ShaderOpcode::S_MOV_B64,
|
||||
ShaderOpcode::S_WQM_B64, ShaderOpcode::V_CMP_LT_U32,
|
||||
ShaderOpcode::EXP, ShaderOpcode::S_ENDPGM};
|
||||
for (u32 layer = 0; layer < test.layers; ++layer) {
|
||||
const auto signed_bits = layer < 4 ? static_cast<int>(layer) : static_cast<int>(layer) - 8;
|
||||
test.expected_pixel.insert(test.expected_pixel.end(),
|
||||
{std::bit_cast<u32>(static_cast<float>(layer)),
|
||||
std::bit_cast<u32>(static_cast<float>(signed_bits)), 0, 0x3f800000u});
|
||||
}
|
||||
return test;
|
||||
}
|
||||
|
||||
GraphicsCase GraphicsFlatInterpolatorExport() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -22000,6 +22110,7 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(ScalarMaskHighWriteInvalidatesProvenance);
|
||||
AddCase(ScalarSelectB64PreservesMaskProvenance);
|
||||
AddCase(ScalarWqmB64SelectsSccDomain);
|
||||
AddCase(ScalarWqmB64PreservesPartialMasks);
|
||||
AddCase(ScalarMaskProvenanceOverlapAndMixedBinary);
|
||||
AddCase(ScalarLiteral);
|
||||
AddCase(VectorMoves);
|
||||
@@ -22257,6 +22368,8 @@ std::vector<TestCase> MakeCases() {
|
||||
std::vector<GraphicsCase> MakeGraphicsCases() {
|
||||
return {
|
||||
GraphicsInterpolationExport(),
|
||||
GraphicsAncillaryLayer(false),
|
||||
GraphicsAncillaryLayer(true),
|
||||
GraphicsFlatInterpolatorExport(),
|
||||
GraphicsDsAddtidScratchExport(),
|
||||
GraphicsDirectSgprPushConstantExport(),
|
||||
@@ -26535,6 +26648,7 @@ int main(int argc, char **argv) {
|
||||
RunCase(&vulkan, ScalarNotB64UpdatesScc());
|
||||
RunCase(&vulkan, ScalarSelectB64PreservesMaskProvenance());
|
||||
RunCase(&vulkan, ScalarWqmB64SelectsSccDomain());
|
||||
RunCase(&vulkan, ScalarWqmB64PreservesPartialMasks());
|
||||
RunCase(&vulkan, ScalarMaskProvenanceOverlapAndMixedBinary());
|
||||
RunCase(&vulkan, ScratchIsPrivatePerInvocation());
|
||||
RunCase(&vulkan, Vop1MoveRelDestination());
|
||||
|
||||
+166
-4
@@ -4778,7 +4778,6 @@ void TestImageAddressOperands() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TestNewShaderRecompilerImageSampleVariants() {
|
||||
const uint32_t shader[] = {
|
||||
EncodeMimg0(0x24, 0xf),
|
||||
@@ -5932,6 +5931,166 @@ void TestPsInputCountRegisterDecode() {
|
||||
"SPI_PS_IN_CONTROL NUM_INTERP decoding failed");
|
||||
}
|
||||
|
||||
void TestPixelAncillaryLayerInput() {
|
||||
using namespace ShaderRecompiler::IR;
|
||||
std::vector<uint32_t> shader = {
|
||||
EncodeVop3Word0(0x148, 6), EncodeVop3Word1(5 + 256, 128 + 16, 128 + 11),
|
||||
EncodeVop3Word0(0x149, 7), EncodeVop3Word1(5 + 256, 128 + 16, 128 + 3),
|
||||
EncodeExp0(0x00, 0x3), EncodeExp1(6, 7, 0, 0), EncodeSopp(0x01),
|
||||
};
|
||||
HW::PixelShaderInfo regs{};
|
||||
regs.ps_regs.data_addr = reinterpret_cast<uint64_t>(shader.data());
|
||||
ShaderMappedData mapped{};
|
||||
mapped.code_size_bytes = shader.size() * sizeof(uint32_t);
|
||||
ShaderMapUserData(regs.ps_regs.data_addr, mapped);
|
||||
HW::ShaderRegisters sh{};
|
||||
sh.ps_input_ena = sh.ps_input_addr = 0x3320; // Linear I/J, X/Y, front-face, ancillary.
|
||||
const std::array<Prospero::ColorComponentMapping, 8> mappings{};
|
||||
ShaderPixelInputInfo pixel{};
|
||||
(void)PrepareProgram(regs, sh, mappings, pixel);
|
||||
Check(pixel.ps_system_input_base == 2 && pixel.ps_front_face && pixel.ps_ancillary,
|
||||
"pixel ancillary register flags were not retained");
|
||||
const auto ancillary_key = MakeStageStaticKey(pixel);
|
||||
pixel.ps_ancillary = false;
|
||||
Check(ancillary_key != MakeStageStaticKey(pixel),
|
||||
"pixel ancillary input is missing from the shader cache key");
|
||||
pixel.ps_ancillary = true;
|
||||
auto options = MakeCompileOptions(ShaderType::Pixel);
|
||||
options.input_info.pixel = &pixel;
|
||||
auto result = RecompileForTest(shader, options);
|
||||
Check(ProgramHasInput(result.program, StageInputKind::Layer),
|
||||
"ancillary after X/Y and front-face did not provide the layer input");
|
||||
Check(!ProgramHasInput(result.program, StageInputKind::PackedAncillary),
|
||||
"symbolic ancillary input survived its layer extraction");
|
||||
Check(SpirvHasDecorationValueWithDecoration(result.spirv, 11u, 9u, 14u) &&
|
||||
result.spirv[1] == 0x00010500u &&
|
||||
SpirvContainsCapability(result.spirv, 69u) &&
|
||||
!SpirvContainsCapability(result.spirv, 5254u),
|
||||
"fragment layer input lacks its builtin, flat decoration or capability");
|
||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||
Check(SpirvSourceHasInstructionUsing(source, "OpLoad", "%int %gl_Layer") &&
|
||||
SpirvInstructionOpcodeCount(result.spirv, 202u) == 1u &&
|
||||
SpirvInstructionOpcodeCount(result.spirv, 203u) == 1u,
|
||||
"layer extraction lost its scalar integer load or signedness");
|
||||
Check(!SpirvHasDecorationValue(result.spirv, 11u, 18u),
|
||||
"layer extraction unexpectedly enabled SampleId");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
|
||||
for (const uint32_t destination : {126u, 106u, 0u}) {
|
||||
std::vector<uint32_t> wqm = {
|
||||
EncodeSop1(0x04, 8, 126), // Save the incoming EXEC predicate.
|
||||
EncodeSop1(0x0a, destination, 126),
|
||||
EncodeSop1(0x04, 126, destination),
|
||||
EncodeVop3Word0(0x148, 6), EncodeVop3Word1(5 + 256, 128 + 16, 128 + 11),
|
||||
EncodeVop1(0x01, 5, 6 + 256), // Overwrite the packed ancillary register.
|
||||
EncodeSop1(0x04, 126, 8),
|
||||
EncodeExp0(0x00, 0x1), EncodeExp1(5, 0, 0, 0), EncodeSopp(0x01),
|
||||
};
|
||||
auto wqm_result = RecompileForTest(wqm, options);
|
||||
Check(ProgramHasInput(wqm_result.program, StageInputKind::Layer) &&
|
||||
!ProgramHasInput(wqm_result.program, StageInputKind::PackedAncillary),
|
||||
"WQM retained an unreachable old ancillary register value");
|
||||
CheckSpirvBinaryValidates(wqm_result.spirv);
|
||||
}
|
||||
|
||||
const uint32_t unused[] = {EncodeSopp(0x01)};
|
||||
auto unused_result = RecompileForTest(unused, options);
|
||||
Check(!ProgramHasInput(unused_result.program, StageInputKind::Layer) &&
|
||||
unused_result.spirv[1] == 0x00010300u &&
|
||||
!SpirvContainsCapability(unused_result.spirv, 69u),
|
||||
"unused ancillary input changed the module requirements");
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
shader[1] = EncodeVop3Word1(5 + 256, 128 + 8, 128 + 4);
|
||||
ExpectFatal([&] { (void)RecompileForTest(shader, options); },
|
||||
"unsupported live ancillary field was silently replaced");
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestMaskedValueDemand() {
|
||||
using namespace ShaderRecompiler::IR;
|
||||
enum class Use { Guarded, Conjunction, TrueArm, Unguarded, OtherGuard, Predicate,
|
||||
ReadLane, ExplicitLod, ExplicitGradient, LevelZero, ImplicitLod,
|
||||
Mixed, Phi, Loop };
|
||||
for (const auto use : {Use::Guarded, Use::Conjunction, Use::TrueArm, Use::Unguarded,
|
||||
Use::OtherGuard, Use::Predicate, Use::ReadLane,
|
||||
Use::ExplicitLod, Use::ExplicitGradient, Use::LevelZero,
|
||||
Use::ImplicitLod, Use::Mixed, Use::Phi, Use::Loop}) {
|
||||
Program program;
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
program.block_storage.push_back(std::make_unique<Block>());
|
||||
program.blocks.push_back(program.block_storage.back().get());
|
||||
}
|
||||
auto &entry = *program.blocks[0];
|
||||
auto &join = *program.blocks[3];
|
||||
entry.AddBranch(program.blocks[1]);
|
||||
entry.AddBranch(program.blocks[2]);
|
||||
program.blocks[1]->AddBranch(&join);
|
||||
program.blocks[2]->AddBranch(&join);
|
||||
if (use == Use::Loop) join.AddBranch(&entry);
|
||||
const auto input = Value(&entry.AppendNewInst(ValueOpcode::GetUserData,
|
||||
{Value(static_cast<ScalarReg>(0))}));
|
||||
const auto predicate = Value(&entry.AppendNewInst(ValueOpcode::INotEqual32,
|
||||
{input, Value(0u)}));
|
||||
const auto other = Value(&entry.AppendNewInst(ValueOpcode::IEqual32,
|
||||
{input, Value(2u)}));
|
||||
auto &masked = entry.AppendNewInst(ValueOpcode::SelectU32,
|
||||
{predicate, Value(10u), Value(20u)});
|
||||
Value value(&masked);
|
||||
if (use == Use::Phi || use == Use::Loop) {
|
||||
auto &phi = join.AppendNewInst(ValueOpcode::Phi, {}, static_cast<uint64_t>(Type::U32));
|
||||
phi.AddPhiOperand(program.blocks[1], value);
|
||||
phi.AddPhiOperand(program.blocks[2], Value(0u));
|
||||
value = Value(&phi);
|
||||
}
|
||||
value = Value(&join.AppendNewInst(ValueOpcode::BitCastF32U32, {value}));
|
||||
value = Value(&join.AppendNewInst(ValueOpcode::FPMul32, {value, Value::F32(2.f)}));
|
||||
value = Value(&join.AppendNewInst(ValueOpcode::BitCastU32F32, {value}));
|
||||
if (use == Use::ReadLane) {
|
||||
value = Value(&join.AppendNewInst(ValueOpcode::ReadLane, {value, Value(0u)}));
|
||||
}
|
||||
if (use == Use::ExplicitLod || use == Use::ExplicitGradient ||
|
||||
use == Use::LevelZero || use == Use::ImplicitLod) {
|
||||
const auto zero = Value(0u);
|
||||
auto &address = join.AppendNewInst(ValueOpcode::MakeImageAddress,
|
||||
{value, zero, zero, zero, zero, zero, zero, zero, zero, zero, zero, zero, zero});
|
||||
auto &image = entry.AppendNewInst(ValueOpcode::GetImageResource,
|
||||
{zero, zero, zero, zero, zero, zero, zero, zero});
|
||||
auto &sampler = entry.AppendNewInst(ValueOpcode::GetSamplerResource,
|
||||
{zero, zero, zero, zero});
|
||||
program.memory_info.emplace_back().image_sample_flags =
|
||||
use == Use::ExplicitLod ? ShaderRecompiler::Decoder::ImageSampleFlagLod :
|
||||
use == Use::ExplicitGradient ? ShaderRecompiler::Decoder::ImageSampleFlagDerivative :
|
||||
use == Use::LevelZero ? ShaderRecompiler::Decoder::ImageSampleFlagLevelZero : 0u;
|
||||
value = Value(&join.AppendNewInst(ValueOpcode::ImageSampleRaw,
|
||||
{Value(&image), Value(&sampler), Value(&address)}));
|
||||
value = Value(&join.AppendNewInst(ValueOpcode::CompositeExtractU32x4, {value, Value(0u)}));
|
||||
}
|
||||
auto guard = predicate;
|
||||
if (use == Use::Conjunction) {
|
||||
guard = Value(&join.AppendNewInst(ValueOpcode::LogicalAnd, {predicate, other}));
|
||||
} else if (use == Use::OtherGuard) {
|
||||
guard = other;
|
||||
} else if (use == Use::Unguarded) {
|
||||
guard = Value(true);
|
||||
} else if (use == Use::Predicate) {
|
||||
guard = Value(&join.AppendNewInst(ValueOpcode::INotEqual32, {value, Value(0u)}));
|
||||
} else if (use == Use::TrueArm) {
|
||||
value = Value(&join.AppendNewInst(ValueOpcode::SelectU32, {predicate, value, Value(0u)}));
|
||||
guard = Value(true);
|
||||
}
|
||||
value = Value(&join.AppendNewInst(ValueOpcode::CompositeConstructU32x4,
|
||||
{value, Value(0u), Value(0u), Value(0u)}));
|
||||
join.AppendNewInst(ValueOpcode::SetAttribute, {value, guard});
|
||||
if (use == Use::Mixed) join.AppendNewInst(ValueOpcode::SetAttribute, {value, Value(true)});
|
||||
EliminateMaskedValues(program);
|
||||
const bool removed = Value(&masked).Resolve() == Value(10u);
|
||||
const bool safe = use == Use::Guarded || use == Use::Conjunction || use == Use::TrueArm ||
|
||||
use == Use::ExplicitLod || use == Use::ExplicitGradient ||
|
||||
use == Use::LevelZero || use == Use::Phi;
|
||||
Check(removed == safe, "masked value demand crossed an unguarded or nonlocal use");
|
||||
}
|
||||
}
|
||||
|
||||
void TestGraphicsCreateInterpolantMapping() {
|
||||
ShaderRegister regs[32] = {};
|
||||
|
||||
@@ -9170,12 +9329,13 @@ void TestNewShaderRecompilerAuxPositionExports() {
|
||||
SpirvHasDecorationValue(all.spirv, 11u, 9u) &&
|
||||
SpirvContainsCapability(all.spirv, 32u) &&
|
||||
SpirvContainsCapability(all.spirv, 33u) &&
|
||||
SpirvContainsCapability(all.spirv, 5254u),
|
||||
SpirvContainsCapability(all.spirv, 69u),
|
||||
"auxiliary vertex BuiltIns or capabilities are missing");
|
||||
const auto all_source = DisassembleSpirvBinary(all.spirv);
|
||||
Check(Common::ContainsStr(all_source, "SPV_EXT_shader_viewport_index_layer") &&
|
||||
Check(all.spirv[1] == 0x00010500u &&
|
||||
!Common::ContainsStr(all_source, "SPV_EXT_shader_viewport_index_layer") &&
|
||||
SpirvBuiltInStoreUsesAndConstant(all.spirv, 9u, 0x7ffu),
|
||||
"layer export extension or GFX10 layer mask is missing");
|
||||
"layer export module version or GFX10 layer mask is incorrect");
|
||||
const auto positions = std::count_if(
|
||||
all.program.info.outputs.begin(), all.program.info.outputs.end(),
|
||||
[](const auto &output) {
|
||||
@@ -12004,6 +12164,8 @@ int main() {
|
||||
TestNewShaderRecompilerIrLookupMissFailsExplicitly();
|
||||
TestNewShaderRecompilerRejectsDppOn64BitCompares();
|
||||
TestPsInputCountRegisterDecode();
|
||||
TestPixelAncillaryLayerInput();
|
||||
TestMaskedValueDemand();
|
||||
TestNewShaderRecompilerUnbasedFlatUsesBda();
|
||||
TestNewShaderRecompilerFlatUserPointerUsesDma();
|
||||
TestNewShaderRecompilerFlatAddressDomainsUseDma();
|
||||
|
||||
Reference in New Issue
Block a user