mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 03:16:54 +02:00
gpu: implement PA_CL_UCP user clip planes through draw push constants
This commit is contained in:
@@ -349,6 +349,38 @@ uint32_t ExportVector(ValueEmitContext& ctx, uint32_t data, const IR::ExportInfo
|
||||
return vector;
|
||||
}
|
||||
|
||||
uint32_t DistancePointer(EmitterState& state, IR::StageOutputKind kind, uint32_t index) {
|
||||
if (state.program.stage == ShaderType::Mesh) {
|
||||
return MeshOutputPointer(state, kind, index);
|
||||
}
|
||||
const auto variable = kind == IR::StageOutputKind::ClipDistance ? state.clip_distance_variable
|
||||
: state.cull_distance_variable;
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpAccessChain,
|
||||
TypePointer(state, spv::StorageClassOutput, TypeF32(state)), pointer,
|
||||
variable, ConstantU32(state, index));
|
||||
return pointer;
|
||||
}
|
||||
|
||||
// The clipper keeps the half space where dot(plane, position) >= 0 for each UCP_ENA plane.
|
||||
void EmitUserClipDistances(EmitterState& state, uint32_t position) {
|
||||
uint32_t index = 0;
|
||||
for (auto mask = state.user_clip_planes; mask != 0; mask &= mask - 1u) {
|
||||
const auto dword = IR::PushData::UserClipPlaneDword + 4u * std::countr_zero(mask);
|
||||
uint32_t components[4] {};
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
components[i] = Unary(state, spv::OpBitcast, TypeF32(state),
|
||||
EmitPushDataLoad(state, ConstantU32(state, dword + i)));
|
||||
}
|
||||
const auto plane = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpCompositeConstruct, TypeF32Vector(state, 4), plane,
|
||||
components[0], components[1], components[2], components[3]);
|
||||
state.builder.AddFunction(
|
||||
spv::OpStore, DistancePointer(state, IR::StageOutputKind::ClipDistance, index++),
|
||||
Binary(state, spv::OpDot, TypeF32(state), plane, position));
|
||||
}
|
||||
}
|
||||
|
||||
void EmitAuxPositionExport(ValueEmitContext& ctx, uint32_t data, const IR::ExportInfo& exp) {
|
||||
auto& state = ctx.state;
|
||||
for (uint32_t component = 0; component < 4; component++) {
|
||||
@@ -389,23 +421,13 @@ void EmitAuxPositionExport(ValueEmitContext& ctx, uint32_t data, const IR::Expor
|
||||
state.builder.AddFunction(spv::OpStore, state.point_size_variable, f32);
|
||||
continue;
|
||||
}
|
||||
auto StoreDistance = [&](IR::StageOutputKind kind, uint32_t variable, uint32_t index) {
|
||||
if (index == UINT32_MAX) {
|
||||
return;
|
||||
auto StoreDistance = [&](IR::StageOutputKind kind, uint32_t index) {
|
||||
if (index != UINT32_MAX) {
|
||||
state.builder.AddFunction(spv::OpStore, DistancePointer(state, kind, index), f32);
|
||||
}
|
||||
uint32_t pointer;
|
||||
if (state.program.stage == ShaderType::Mesh) {
|
||||
pointer = MeshOutputPointer(state, kind, index);
|
||||
} else {
|
||||
pointer = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpAccessChain,
|
||||
TypePointer(state, spv::StorageClassOutput, TypeF32(state)),
|
||||
pointer, variable, ConstantU32(state, index));
|
||||
}
|
||||
state.builder.AddFunction(spv::OpStore, pointer, f32);
|
||||
};
|
||||
StoreDistance(IR::StageOutputKind::ClipDistance, state.clip_distance_variable, output.clip_distance);
|
||||
StoreDistance(IR::StageOutputKind::CullDistance, state.cull_distance_variable, output.cull_distance);
|
||||
StoreDistance(IR::StageOutputKind::ClipDistance, output.clip_distance);
|
||||
StoreDistance(IR::StageOutputKind::CullDistance, output.cull_distance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,6 +618,9 @@ void EmitSetAttribute(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
state.input_info.vertex->clip_space.enabled) {
|
||||
value = ConvertPositionToClipSpace(state, value);
|
||||
}
|
||||
if (exp.kind == IR::ExportTargetKind::Position && state.user_clip_planes != 0) {
|
||||
EmitUserClipDistances(state, value);
|
||||
}
|
||||
if (state.program.stage == ShaderType::Mesh) {
|
||||
const auto kind = exp.kind == IR::ExportTargetKind::Position
|
||||
? IR::StageOutputKind::Position
|
||||
@@ -608,7 +633,6 @@ void EmitSetAttribute(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
const auto equal = state.builder.AllocateId();
|
||||
const auto invalid = state.builder.AllocateId();
|
||||
const auto distance = state.builder.AllocateId();
|
||||
const auto distance_pointer = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpFOrdEqual, TypeBoolVector(state, 4), equal,
|
||||
value, zero);
|
||||
state.builder.AddFunction(spv::OpAll, TypeBool(state), invalid, equal);
|
||||
@@ -617,11 +641,10 @@ void EmitSetAttribute(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
state.builder.AddFunction(spv::OpSelect, TypeF32(state), distance, invalid,
|
||||
ConstantF32Value(state, -1.0f),
|
||||
ConstantF32Value(state, 0.0f));
|
||||
state.builder.AddFunction(
|
||||
spv::OpAccessChain, TypePointer(state, spv::StorageClassOutput, TypeF32(state)),
|
||||
distance_pointer, state.clip_distance_variable,
|
||||
ConstantU32(state, state.invalid_position_clip_distance));
|
||||
state.builder.AddFunction(spv::OpStore, distance_pointer, distance);
|
||||
state.builder.AddFunction(spv::OpStore,
|
||||
DistancePointer(state, IR::StageOutputKind::ClipDistance,
|
||||
state.invalid_position_clip_distance),
|
||||
distance);
|
||||
static std::atomic_bool logged = false;
|
||||
if (!logged.exchange(true, std::memory_order_relaxed)) {
|
||||
Log::WriteToConsoleAndLog(
|
||||
|
||||
@@ -148,6 +148,7 @@ struct EmitterState {
|
||||
uint32_t point_size_variable = 0;
|
||||
uint32_t clip_distance_variable = 0;
|
||||
uint32_t invalid_position_clip_distance = UINT32_MAX;
|
||||
uint32_t user_clip_planes = 0;
|
||||
uint32_t cull_distance_variable = 0;
|
||||
uint32_t layer_variable = 0;
|
||||
uint32_t viewport_index_variable = 0;
|
||||
|
||||
@@ -244,7 +244,7 @@ void DefineDescriptors(EmitterState& state) {
|
||||
counts[static_cast<size_t>(Kind::ShaderData)] =
|
||||
bindings.ShaderDataDwords() != 0 && !bindings.UsesPushData();
|
||||
if (state.program.bindings.UsesPushData() || state.program.stage == ShaderType::Mesh ||
|
||||
state.object_primitive_id_variable != 0) {
|
||||
state.object_primitive_id_variable != 0 || state.user_clip_planes != 0) {
|
||||
const auto type = PushConstantBlockType(state);
|
||||
state.push_constant_variable = state.builder.DefineGlobalVariable(
|
||||
TypePointer(state, spv::StorageClassPushConstant, type), spv::StorageClassPushConstant);
|
||||
@@ -600,14 +600,24 @@ void DefineOutputs(EmitterState& state) {
|
||||
cull_distance_count = std::max(cull_distance_count, output.index + 1);
|
||||
}
|
||||
}
|
||||
const bool position = std::ranges::any_of(state.outputs, [](const OutputBinding& output) {
|
||||
return output.kind == IR::StageOutputKind::Position;
|
||||
});
|
||||
if (position && state.input_info.vertex->user_clip_planes != 0) {
|
||||
// PipelineCache rejects UCP_ENA with shader clip or cull distances.
|
||||
EXIT_IF(clip_distance_count + cull_distance_count != 0);
|
||||
state.user_clip_planes = state.input_info.vertex->user_clip_planes;
|
||||
for (auto mask = state.user_clip_planes; mask != 0; mask &= mask - 1u) {
|
||||
state.outputs.push_back(
|
||||
{{IR::StageOutputKind::ClipDistance, clip_distance_count++, 0, "gl_ClipDistance"}});
|
||||
}
|
||||
}
|
||||
if (state.program.stage == ShaderType::Mesh) {
|
||||
DefineMeshOutputs(state, clip_distance_count, cull_distance_count);
|
||||
return;
|
||||
}
|
||||
if (state.program.stage == ShaderType::Vertex && clip_distance_count + cull_distance_count < 8u &&
|
||||
std::ranges::any_of(state.outputs, [](const OutputBinding& output) {
|
||||
return output.kind == IR::StageOutputKind::Position;
|
||||
})) {
|
||||
if (state.program.stage == ShaderType::Vertex &&
|
||||
clip_distance_count + cull_distance_count < 8u && position) {
|
||||
// Reserve one plane for the enabled PA_CL_CLIP_CNTL clipping-error cull.
|
||||
state.invalid_position_clip_distance = clip_distance_count++;
|
||||
state.outputs.push_back({{IR::StageOutputKind::ClipDistance,
|
||||
|
||||
@@ -356,7 +356,14 @@ struct PushData {
|
||||
static constexpr uint32_t NoStart = UINT32_MAX;
|
||||
std::array<uint32_t, DwordCount> dwords {};
|
||||
|
||||
[[nodiscard]] static constexpr uint32_t GraphicsDrawDwords(bool mesh, bool object_id) {
|
||||
// User clip planes 0..N follow the mesh draw parameters or object ID, four dwords each.
|
||||
static constexpr uint32_t UserClipPlaneDword = MeshDrawDwordCount;
|
||||
|
||||
[[nodiscard]] static constexpr uint32_t GraphicsDrawDwords(bool mesh, bool object_id,
|
||||
uint32_t user_clip_planes = 0) {
|
||||
if (user_clip_planes != 0) {
|
||||
return UserClipPlaneDword + 4u * std::bit_width(user_clip_planes);
|
||||
}
|
||||
return mesh ? MeshDrawDwordCount : static_cast<uint32_t>(object_id);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user