Skip to content

Commit 95b3572

Browse files
baggins183raphaelthegreat
authored andcommitted
Tessellation (shadps4-emu#1528)
* shader_recompiler: Tessellation WIP * fix compiler errors after merge DONT MERGE set log file to /dev/null DONT MERGE linux pthread bb fix save work DONT MERGE dump ir save more work fix mistake with ES shader skip list add input patch control points dynamic state random stuff * WIP Tessellation partial implementation. Squash commits * test: make local/tcs use attr arrays * attr arrays in TCS/TES * dont define empty attr arrays * switch to special opcodes for tess tcs/tes reads and tcs writes * impl tcs/tes read attr insts * rebase fix * save some work * save work probably broken and slow * put Vertex LogicalStage after TCS and TES to fix bindings * more refactors * refactor pattern matching and optimize modulos (disabled) * enable modulo opt * copyright * rebase fixes * remove some prints * remove some stuff * Add TCS/TES support for shader patching and use LogicalStage * refactor and handle wider DS instructions * get rid of GetAttributes for special tess constants reads. Immediately replace some upon seeing readconstbuffer. Gets rid of some extra passes over IR * stop relying on GNMX HsConstants struct. Change runtime_info.hs_info and some regs * delete some more stuff * update comments for current implementation * some cleanup * uint error * more cleanup * remove patch control points dynamic state (because runtime_info already depends on it) * fix potential problem with determining passthrough --------- Co-authored-by: IndecisiveTurtle <[email protected]>
1 parent 55e2de3 commit 95b3572

23 files changed

+351
-456
lines changed

src/core/debug_state.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,11 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
177177
}
178178
}
179179

180-
void DebugStateImpl::CollectShader(const std::string& name, vk::ShaderModule module,
181-
std::span<const u32> spv, std::span<const u32> raw_code,
182-
std::span<const u32> patch_spv, bool is_patched) {
183-
shader_dump_list.emplace_back(name, module, std::vector<u32>{spv.begin(), spv.end()},
180+
void DebugStateImpl::CollectShader(const std::string& name, Shader::LogicalStage l_stage,
181+
vk::ShaderModule module, std::span<const u32> spv,
182+
std::span<const u32> raw_code, std::span<const u32> patch_spv,
183+
bool is_patched) {
184+
shader_dump_list.emplace_back(name, l_stage, module, std::vector<u32>{spv.begin(), spv.end()},
184185
std::vector<u32>{raw_code.begin(), raw_code.end()},
185186
std::vector<u32>{patch_spv.begin(), patch_spv.end()}, is_patched);
186187
}

src/core/debug_state.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ struct FrameDump {
7676

7777
struct ShaderDump {
7878
std::string name;
79+
Shader::LogicalStage l_stage;
7980
vk::ShaderModule module;
8081

8182
std::vector<u32> spv;
@@ -90,16 +91,17 @@ struct ShaderDump {
9091
std::string cache_isa_disasm{};
9192
std::string cache_patch_disasm{};
9293

93-
ShaderDump(std::string name, vk::ShaderModule module, std::vector<u32> spv,
94-
std::vector<u32> isa, std::vector<u32> patch_spv, bool is_patched)
95-
: name(std::move(name)), module(module), spv(std::move(spv)), isa(std::move(isa)),
96-
patch_spv(std::move(patch_spv)), is_patched(is_patched) {}
94+
ShaderDump(std::string name, Shader::LogicalStage l_stage, vk::ShaderModule module,
95+
std::vector<u32> spv, std::vector<u32> isa, std::vector<u32> patch_spv,
96+
bool is_patched)
97+
: name(std::move(name)), l_stage(l_stage), module(module), spv(std::move(spv)),
98+
isa(std::move(isa)), patch_spv(std::move(patch_spv)), is_patched(is_patched) {}
9799

98100
ShaderDump(const ShaderDump& other) = delete;
99101
ShaderDump(ShaderDump&& other) noexcept
100-
: name{std::move(other.name)}, module{std::move(other.module)}, spv{std::move(other.spv)},
101-
isa{std::move(other.isa)}, patch_spv{std::move(other.patch_spv)},
102-
patch_source{std::move(other.patch_source)},
102+
: name{std::move(other.name)}, l_stage(other.l_stage), module{std::move(other.module)},
103+
spv{std::move(other.spv)}, isa{std::move(other.isa)},
104+
patch_spv{std::move(other.patch_spv)}, patch_source{std::move(other.patch_source)},
103105
cache_spv_disasm{std::move(other.cache_spv_disasm)},
104106
cache_isa_disasm{std::move(other.cache_isa_disasm)},
105107
cache_patch_disasm{std::move(other.cache_patch_disasm)} {}
@@ -108,6 +110,7 @@ struct ShaderDump {
108110
if (this == &other)
109111
return *this;
110112
name = std::move(other.name);
113+
l_stage = other.l_stage;
111114
module = std::move(other.module);
112115
spv = std::move(other.spv);
113116
isa = std::move(other.isa);
@@ -203,7 +206,8 @@ class DebugStateImpl {
203206
void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
204207
const AmdGpu::Liverpool::Regs& regs, bool is_compute = false);
205208

206-
void CollectShader(const std::string& name, vk::ShaderModule module, std::span<const u32> spv,
209+
void CollectShader(const std::string& name, Shader::LogicalStage l_stage,
210+
vk::ShaderModule module, std::span<const u32> spv,
207211
std::span<const u32> raw_code, std::span<const u32> patch_spv,
208212
bool is_patched);
209213
};

src/core/devtools/widget/shader_list.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,16 +158,17 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
158158
DebugState.ShowDebugMessage(msg);
159159
}
160160
if (compile) {
161-
static std::map<std::string, std::string> stage_arg = {
162-
{"vs", "vert"},
163-
{"gs", "geom"},
164-
{"fs", "frag"},
165-
{"cs", "comp"},
161+
static std::map<Shader::LogicalStage, std::string> stage_arg = {
162+
{Shader::LogicalStage::Vertex, "vert"},
163+
{Shader::LogicalStage::TessellationControl, "tesc"},
164+
{Shader::LogicalStage::TessellationEval, "tese"},
165+
{Shader::LogicalStage::Geometry, "geom"},
166+
{Shader::LogicalStage::Fragment, "frag"},
167+
{Shader::LogicalStage::Compute, "comp"},
166168
};
167-
auto stage = stage_arg.find(value.name.substr(0, 2));
169+
auto stage = stage_arg.find(value.l_stage);
168170
if (stage == stage_arg.end()) {
169-
DebugState.ShowDebugMessage(std::string{"Invalid shader stage: "} +
170-
value.name.substr(0, 2));
171+
DebugState.ShowDebugMessage(std::string{"Invalid shader stage"});
171172
} else {
172173
std::string cmd =
173174
fmt::format("glslc --target-env=vulkan1.3 --target-spv=spv1.6 "

src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,16 @@ void MemoryBarrier(EmitContext& ctx, spv::Scope scope) {
1818

1919
void EmitBarrier(EmitContext& ctx) {
2020
const auto execution{spv::Scope::Workgroup};
21-
const auto memory{spv::Scope::Workgroup};
22-
const auto memory_semantics{spv::MemorySemanticsMask::AcquireRelease |
23-
spv::MemorySemanticsMask::WorkgroupMemory};
21+
spv::Scope memory;
22+
spv::MemorySemanticsMask memory_semantics;
23+
if (ctx.l_stage == Shader::LogicalStage::TessellationControl) {
24+
memory = spv::Scope::Invocation;
25+
memory_semantics = spv::MemorySemanticsMask::MaskNone;
26+
} else {
27+
memory = spv::Scope::Workgroup;
28+
memory_semantics =
29+
spv::MemorySemanticsMask::AcquireRelease | spv::MemorySemanticsMask::WorkgroupMemory;
30+
}
2431
ctx.OpControlBarrier(ctx.ConstU32(static_cast<u32>(execution)),
2532
ctx.ConstU32(static_cast<u32>(memory)),
2633
ctx.ConstU32(static_cast<u32>(memory_semantics)));

src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ Id EmitGetAttributeForGeometry(EmitContext& ctx, IR::Attribute attr, u32 comp, I
207207
}
208208

209209
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, Id index) {
210-
if (ctx.info.stage == Stage::Geometry) {
210+
if (ctx.info.l_stage == LogicalStage::Geometry) {
211211
return EmitGetAttributeForGeometry(ctx, attr, comp, index);
212212
} else if (ctx.info.l_stage == LogicalStage::TessellationControl ||
213213
ctx.info.l_stage == LogicalStage::TessellationEval) {
@@ -302,7 +302,6 @@ Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp) {
302302
return ctx.OpSelect(ctx.U32[1], ctx.OpLoad(ctx.U1[1], ctx.front_facing), ctx.u32_one_value,
303303
ctx.u32_zero_value);
304304
case IR::Attribute::PrimitiveId:
305-
case IR::Attribute::TessPatchIdInVgt: // TODO see why this isnt DCEd
306305
return ctx.OpLoad(ctx.U32[1], ctx.primitive_id);
307306
case IR::Attribute::InvocationId:
308307
ASSERT(ctx.info.l_stage == LogicalStage::Geometry ||
@@ -311,10 +310,22 @@ Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp) {
311310
case IR::Attribute::PatchVertices:
312311
ASSERT(ctx.info.l_stage == LogicalStage::TessellationControl);
313312
return ctx.OpLoad(ctx.U32[1], ctx.patch_vertices);
314-
case IR::Attribute::PackedHullInvocationInfo:
315-
// TODO figure out what to do with this
316-
// should be dead code, but otherwise return 0 or concat PrimitiveId and InvocationId
317-
return ctx.u32_zero_value;
313+
case IR::Attribute::PackedHullInvocationInfo: {
314+
ASSERT(ctx.info.l_stage == LogicalStage::TessellationControl);
315+
// [0:8]: patch id within VGT
316+
// [8:12]: output control point id
317+
// But 0:8 should be treated as 0 for attribute addressing purposes
318+
if (ctx.runtime_info.hs_info.IsPassthrough()) {
319+
// Gcn shader would run with 1 thread, but we need to run a thread for
320+
// each output control point.
321+
// If Gcn shader uses this value, we should make sure all threads in the
322+
// Vulkan shader use 0
323+
return ctx.ConstU32(0u);
324+
} else {
325+
const Id invocation_id = ctx.OpLoad(ctx.U32[1], ctx.invocation_id);
326+
return ctx.OpShiftLeftLogical(ctx.U32[1], invocation_id, ctx.ConstU32(8u));
327+
}
328+
}
318329
default:
319330
UNREACHABLE_MSG("Read U32 attribute {}", attr);
320331
}
@@ -352,7 +363,8 @@ void EmitSetTcsGenericAttribute(EmitContext& ctx, Id value, Id attr_index, Id co
352363
Id EmitGetPatch(EmitContext& ctx, IR::Patch patch) {
353364
const u32 index{IR::GenericPatchIndex(patch)};
354365
const Id element{ctx.ConstU32(IR::GenericPatchElement(patch))};
355-
const Id type{ctx.stage == Stage::Hull ? ctx.output_f32 : ctx.input_f32};
366+
const Id type{ctx.l_stage == LogicalStage::TessellationControl ? ctx.output_f32
367+
: ctx.input_f32};
356368
const Id pointer{ctx.OpAccessChain(type, ctx.patches.at(index), element)};
357369
return ctx.OpLoad(ctx.F32[1], pointer);
358370
}

src/shader_recompiler/backend/spirv/spirv_emit_context.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
32
// SPDX-License-Identifier: GPL-2.0-or-later
43

@@ -511,7 +510,6 @@ void EmitContext::DefineOutputs() {
511510
break;
512511
}
513512
case LogicalStage::TessellationEval: {
514-
// TODO copied from logical vertex, figure this out
515513
output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output);
516514
const bool has_extra_pos_stores = info.stores.Get(IR::Attribute::Position1) ||
517515
info.stores.Get(IR::Attribute::Position2) ||

src/shader_recompiler/frontend/tessellation.h

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,31 @@
88
namespace Shader {
99

1010
struct TessellationDataConstantBuffer {
11-
u32 m_lsStride;
12-
u32 m_hsCpStride; // HullStateConstants::m_cpStride != 0 ? HullStateConstants::m_cpStride :
11+
u32 ls_stride;
12+
u32 hs_cp_stride; // HullStateConstants::m_cpStride != 0 ? HullStateConstants::m_cpStride :
1313
// ls_stride
14-
u32 m_hsNumPatch; // num patches submitted in threadgroup
15-
u32 m_hsOutputBase; // HullStateConstants::m_numInputCP::m_cpStride != 0 ?
14+
u32 num_patches; // num patches submitted in threadgroup
15+
u32 hs_output_base; // HullStateConstants::m_numInputCP::m_cpStride != 0 ?
1616
// HullStateConstants::m_numInputCP * ls_stride * num_patches : 0
17-
u32 m_patchConstSize; // 16 * num_patch_attrs
18-
u32 m_patchConstBase; // hs_output_base + patch_output_size
19-
u32 m_patchOutputSize; // output_cp_stride * num_output_cp
20-
f32 m_offChipTessellationFactorThreshold;
21-
u32 m_firstEdgeTessFactorIndex;
17+
// basically 0 when passthrough
18+
u32 patch_const_size; // 16 * num_patch_attrs
19+
u32 patch_const_base; // hs_output_base + patch_output_size
20+
u32 patch_output_size; // output_cp_stride * num_output_cp_per_patch
21+
f32 off_chip_tessellation_factor_threshold;
22+
u32 first_edge_tess_factor_index;
23+
};
24+
25+
// Assign names to dword fields of TessellationDataConstantBuffer
26+
enum class TessConstantAttribute : u32 {
27+
LsStride,
28+
HsCpStride,
29+
HsNumPatch,
30+
HsOutputBase,
31+
PatchConstSize,
32+
PatchConstBase,
33+
PatchOutputSize,
34+
OffChipTessellationFactorThreshold,
35+
FirstEdgeTessFactorIndex,
2236
};
2337

2438
} // namespace Shader

src/shader_recompiler/frontend/translate/translate.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,20 +124,26 @@ void Translator::EmitPrologue() {
124124
}
125125
break;
126126
case LogicalStage::TessellationControl: {
127+
// Should be laid out like:
128+
// [0:8]: patch id within VGT
129+
// [8:12]: output control point id
127130
ir.SetVectorReg(IR::VectorReg::V1,
128131
ir.GetAttributeU32(IR::Attribute::PackedHullInvocationInfo));
129-
// TODO need PrimitiveId also like TES?
132+
// TODO PrimitiveId is probably V2 but haven't seen it yet
130133
break;
131134
}
132135
case LogicalStage::TessellationEval:
133136
ir.SetVectorReg(IR::VectorReg::V0,
134137
ir.GetAttribute(IR::Attribute::TessellationEvaluationPointU));
135138
ir.SetVectorReg(IR::VectorReg::V1,
136139
ir.GetAttribute(IR::Attribute::TessellationEvaluationPointV));
137-
// I think V2 is actually the patch id within the patches running on the local CU, used in
138-
// compiler generated address calcs,
139-
// and V3 is the patch id within the draw
140-
ir.SetVectorReg(IR::VectorReg::V2, ir.GetAttributeU32(IR::Attribute::TessPatchIdInVgt));
140+
// V2 is similar to PrimitiveID but not the same. It seems to only be used in
141+
// compiler-generated address calculations. Its probably the patch id within the
142+
// patches running locally on a given VGT (or CU, whichever is the granularity of LDS
143+
// memory)
144+
// Set to 0. See explanation in comment describing hull/domain passes
145+
ir.SetVectorReg(IR::VectorReg::V2, ir.Imm32(0u));
146+
// V3 is the actual PrimitiveID as intended by the shader author.
141147
ir.SetVectorReg(IR::VectorReg::V3, ir.GetAttributeU32(IR::Attribute::PrimitiveId));
142148
break;
143149
case LogicalStage::Compute:

src/shader_recompiler/info.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ struct Info {
175175
PersistentSrtInfo srt_info;
176176
std::vector<u32> flattened_ud_buf;
177177

178-
// TODO handle indirection
179178
IR::ScalarReg tess_consts_ptr_base = IR::ScalarReg::Max;
180179
s32 tess_consts_dword_offset = -1;
181180

@@ -254,13 +253,8 @@ struct Info {
254253
}
255254
}
256255

257-
// TODO probably not needed
258-
bool FoundTessConstantsSharp() const {
259-
return tess_consts_dword_offset >= 0;
260-
}
261-
262256
void ReadTessConstantBuffer(TessellationDataConstantBuffer& tess_constants) const {
263-
ASSERT(FoundTessConstantsSharp());
257+
ASSERT(tess_consts_dword_offset >= 0); // We've already tracked the V# UD
264258
auto buf = ReadUdReg<AmdGpu::Buffer>(static_cast<u32>(tess_consts_ptr_base),
265259
static_cast<u32>(tess_consts_dword_offset));
266260
VAddr tess_constants_addr = buf.base_address;

src/shader_recompiler/ir/attribute.cpp

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -126,26 +126,6 @@ std::string NameOf(Attribute attribute) {
126126
return "TessellationEvaluationPointV";
127127
case Attribute::PackedHullInvocationInfo:
128128
return "PackedHullInvocationInfo";
129-
case Attribute::TcsLsStride:
130-
return "TcsLsStride";
131-
case Attribute::TcsCpStride:
132-
return "TcsCpStride";
133-
case Attribute::TcsNumPatches:
134-
return "TcsNumPatches";
135-
case Attribute::TcsOutputBase:
136-
return "TcsOutputBase";
137-
case Attribute::TcsPatchConstSize:
138-
return "TcsPatchConstSize";
139-
case Attribute::TcsPatchConstBase:
140-
return "TcsPatchConstBase";
141-
case Attribute::TcsPatchOutputSize:
142-
return "TcsPatchOutputSize";
143-
case Attribute::TcsOffChipTessellationFactorThreshold:
144-
return "TcsOffChipTessellationFactorThreshold";
145-
case Attribute::TcsFirstEdgeTessFactorIndex:
146-
return "TcsFirstEdgeTessFactorIndex";
147-
case Attribute::TessPatchIdInVgt:
148-
return "TessPatchIdInVgt";
149129
default:
150130
break;
151131
}

src/shader_recompiler/ir/attribute.h

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -78,20 +78,7 @@ enum class Attribute : u64 {
7878
PatchVertices = 81,
7979
TessellationEvaluationPointU = 82,
8080
TessellationEvaluationPointV = 83,
81-
PackedHullInvocationInfo =
82-
84, // PrimitiveId (patch id) and InvocationId (output control point id)
83-
// Probably don't need all these.
84-
// Most should be dead after hull shader transform
85-
TcsLsStride = 85,
86-
TcsCpStride = 86,
87-
TcsNumPatches = 87,
88-
TcsOutputBase = 88,
89-
TcsPatchConstSize = 89,
90-
TcsPatchConstBase = 90,
91-
TcsPatchOutputSize = 91,
92-
TcsOffChipTessellationFactorThreshold = 92,
93-
TcsFirstEdgeTessFactorIndex = 93,
94-
TessPatchIdInVgt = 94,
81+
PackedHullInvocationInfo = 84, // contains patch id within the VGT and invocation ID
9582
Max,
9683
};
9784

0 commit comments

Comments
 (0)