Skip to content

Commit 8e7301f

Browse files
committed
[SPIR-V] Use a helper class for most if/else branching
Simplifies emission of the blocks themselves (including inserting blocks into the function's block list in the correct order), as well as phi after the branching. Also fixes 64bpp storing with blending in the fragment shader interlock render backend implementation (had a typo that caused the high 32 bits to overwrite the low ones).
1 parent 3189a0e commit 8e7301f

File tree

8 files changed

+998
-1324
lines changed

8 files changed

+998
-1324
lines changed

src/xenia/gpu/spirv_builder.cc

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include <utility>
1414
#include <vector>
1515

16+
#include "xenia/base/assert.h"
17+
1618
namespace xe {
1719
namespace gpu {
1820

@@ -101,5 +103,105 @@ spv::Id SpirvBuilder::createTriBuiltinCall(spv::Id result_type,
101103
return result;
102104
}
103105

106+
SpirvBuilder::IfBuilder::IfBuilder(spv::Id condition, unsigned int control,
107+
SpirvBuilder& builder,
108+
unsigned int thenWeight,
109+
unsigned int elseWeight)
110+
: builder(builder),
111+
condition(condition),
112+
control(control),
113+
thenWeight(thenWeight),
114+
elseWeight(elseWeight),
115+
function(builder.getBuildPoint()->getParent()) {
116+
// Make the blocks, but only put the then-block into the function, the
117+
// else-block and merge-block will be added later, in order, after earlier
118+
// code is emitted.
119+
thenBlock = new spv::Block(builder.getUniqueId(), function);
120+
elseBlock = nullptr;
121+
mergeBlock = new spv::Block(builder.getUniqueId(), function);
122+
123+
// Save the current block, so that we can add in the flow control split when
124+
// makeEndIf is called.
125+
headerBlock = builder.getBuildPoint();
126+
127+
spv::Id headerBlockId = headerBlock->getId();
128+
thenPhiParent = headerBlockId;
129+
elsePhiParent = headerBlockId;
130+
131+
function.addBlock(thenBlock);
132+
builder.setBuildPoint(thenBlock);
133+
}
134+
135+
void SpirvBuilder::IfBuilder::makeBeginElse(bool branchToMerge) {
136+
#ifndef NDEBUG
137+
assert_true(currentBranch == Branch::kThen);
138+
#endif
139+
140+
if (branchToMerge) {
141+
// Close out the "then" by having it jump to the mergeBlock.
142+
thenPhiParent = builder.getBuildPoint()->getId();
143+
builder.createBranch(mergeBlock);
144+
}
145+
146+
// Make the first else block and add it to the function.
147+
elseBlock = new spv::Block(builder.getUniqueId(), function);
148+
function.addBlock(elseBlock);
149+
150+
// Start building the else block.
151+
builder.setBuildPoint(elseBlock);
152+
153+
#ifndef NDEBUG
154+
currentBranch = Branch::kElse;
155+
#endif
156+
}
157+
158+
void SpirvBuilder::IfBuilder::makeEndIf(bool branchToMerge) {
159+
#ifndef NDEBUG
160+
assert_true(currentBranch == Branch::kThen || currentBranch == Branch::kElse);
161+
#endif
162+
163+
if (branchToMerge) {
164+
// Jump to the merge block.
165+
(elseBlock ? elsePhiParent : thenPhiParent) =
166+
builder.getBuildPoint()->getId();
167+
builder.createBranch(mergeBlock);
168+
}
169+
170+
// Go back to the headerBlock and make the flow control split.
171+
builder.setBuildPoint(headerBlock);
172+
builder.createSelectionMerge(mergeBlock, control);
173+
{
174+
spv::Block* falseBlock = elseBlock ? elseBlock : mergeBlock;
175+
std::unique_ptr<spv::Instruction> branch =
176+
std::make_unique<spv::Instruction>(spv::OpBranchConditional);
177+
branch->addIdOperand(condition);
178+
branch->addIdOperand(thenBlock->getId());
179+
branch->addIdOperand(falseBlock->getId());
180+
if (thenWeight || elseWeight) {
181+
branch->addImmediateOperand(thenWeight);
182+
branch->addImmediateOperand(elseWeight);
183+
}
184+
builder.getBuildPoint()->addInstruction(std::move(branch));
185+
thenBlock->addPredecessor(builder.getBuildPoint());
186+
falseBlock->addPredecessor(builder.getBuildPoint());
187+
}
188+
189+
// Add the merge block to the function.
190+
function.addBlock(mergeBlock);
191+
builder.setBuildPoint(mergeBlock);
192+
193+
#ifndef NDEBUG
194+
currentBranch = Branch::kMerge;
195+
#endif
196+
}
197+
198+
spv::Id SpirvBuilder::IfBuilder::createMergePhi(spv::Id then_variable,
199+
spv::Id else_variable) const {
200+
assert_true(builder.getBuildPoint() == mergeBlock);
201+
return builder.createQuadOp(spv::OpPhi, builder.getTypeId(then_variable),
202+
then_variable, getThenPhiParent(), else_variable,
203+
getElsePhiParent());
204+
}
205+
104206
} // namespace gpu
105207
} // namespace xe

src/xenia/gpu/spirv_builder.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
#ifndef XENIA_GPU_SPIRV_BUILDER_H_
1111
#define XENIA_GPU_SPIRV_BUILDER_H_
1212

13+
#include <optional>
14+
1315
#include "third_party/glslang/SPIRV/SpvBuilder.h"
16+
#include "xenia/base/assert.h"
1417

1518
namespace xe {
1619
namespace gpu {
@@ -42,6 +45,60 @@ class SpirvBuilder : public spv::Builder {
4245
spv::Id createTriBuiltinCall(spv::Id result_type, spv::Id builtins,
4346
int entry_point, spv::Id operand1,
4447
spv::Id operand2, spv::Id operand3);
48+
49+
// Helper to use for building nested control flow with if-then-else with
50+
// additions over SpvBuilder::If.
51+
class IfBuilder {
52+
public:
53+
IfBuilder(spv::Id condition, unsigned int control, SpirvBuilder& builder,
54+
unsigned int thenWeight = 0, unsigned int elseWeight = 0);
55+
56+
~IfBuilder() {
57+
#ifndef NDEBUG
58+
assert_true(currentBranch == Branch::kMerge);
59+
#endif
60+
}
61+
62+
void makeBeginElse(bool branchToMerge = true);
63+
void makeEndIf(bool branchToMerge = true);
64+
65+
// If there's no then/else block that branches to the merge block, the phi
66+
// parent is the header block - this simplifies then-only usage.
67+
spv::Id getThenPhiParent() const { return thenPhiParent; }
68+
spv::Id getElsePhiParent() const { return elsePhiParent; }
69+
70+
spv::Id createMergePhi(spv::Id then_variable, spv::Id else_variable) const;
71+
72+
private:
73+
enum class Branch {
74+
kThen,
75+
kElse,
76+
kMerge,
77+
};
78+
79+
IfBuilder(const IfBuilder& ifBuilder) = delete;
80+
IfBuilder& operator=(const IfBuilder& ifBuilder) = delete;
81+
82+
SpirvBuilder& builder;
83+
spv::Id condition;
84+
unsigned int control;
85+
unsigned int thenWeight;
86+
unsigned int elseWeight;
87+
88+
spv::Function& function;
89+
90+
spv::Block* headerBlock;
91+
spv::Block* thenBlock;
92+
spv::Block* elseBlock;
93+
spv::Block* mergeBlock;
94+
95+
spv::Id thenPhiParent;
96+
spv::Id elsePhiParent;
97+
98+
#ifndef NDEBUG
99+
Branch currentBranch = Branch::kThen;
100+
#endif
101+
};
45102
};
46103

47104
} // namespace gpu

0 commit comments

Comments
 (0)