Skip to content

Commit f217515

Browse files
mdenton8Angle LUCI CQ
authored andcommitted
WGSL: implement comma operator
There is no commma operator in WGSL, nor a way to nest multiple expressions inside one. Similarly to ternaries, this is implemented by pulling the expressions into a functions, and all temporaries inside the expression are converted into globals. Bug: angleproject:42267100 Change-Id: I7b663571fc2beb92fa1e8b1bcb3e79cc1b989590 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7057452 Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Commit-Queue: Matthew Denton <mpdenton@chromium.org> Reviewed-by: Geoff Lang <geofflang@chromium.org>
1 parent 5e06057 commit f217515

3 files changed

Lines changed: 218 additions & 2 deletions

File tree

src/compiler/translator/tree_ops/wgsl/PullExpressionsIntoFunctions.cpp

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,18 @@ class PullExpressionsIntoFunctionsTraverser : public TIntermTraverser
116116
return true;
117117
}
118118

119+
bool visitBinary(Visit visit, TIntermBinary *binary) override
120+
{
121+
if (binary->getOp() == EOpComma)
122+
{
123+
handleUntranslatableConstruct(
124+
visit, UntranslatableConstructAndMetadata{UntranslatableCommaOperator{binary},
125+
getParentNode(), mCurrentFunction});
126+
}
127+
128+
return true;
129+
}
130+
119131
bool foundUntranslatableConstruct() const { return !mUntranslatableConstructs.empty(); }
120132

121133
bool update(TIntermBlock *root)
@@ -126,8 +138,13 @@ class PullExpressionsIntoFunctionsTraverser : public TIntermTraverser
126138

127139
private:
128140
using UntranslatableTernary = TIntermTernary *;
141+
struct UntranslatableCommaOperator
142+
{
143+
TIntermBinary *commaOperator;
144+
};
129145

130-
using UntranslatableConstruct = std::variant<UntranslatableTernary>;
146+
using UntranslatableConstruct =
147+
std::variant<UntranslatableTernary, UntranslatableCommaOperator>;
131148

132149
struct UntranslatableConstructAndMetadata
133150
{
@@ -228,6 +245,64 @@ class PullExpressionsIntoFunctionsTraverser : public TIntermTraverser
228245
return substituteFunction;
229246
}
230247

248+
TFunction *replaceSequenceOperator(TIntermBinary *sequenceOperator,
249+
const TIntermFunctionDefinition *parentFunction,
250+
TIntermSequence &newFunctionPrototypes,
251+
TIntermSequence &newFunctionDefinitions)
252+
{
253+
ASSERT(sequenceOperator->getOp() == EOpComma);
254+
255+
// Pull into function that just puts one statement after the other.
256+
257+
TIntermSequence extractedStmts;
258+
259+
// Flatten the nested comma operators into a sequence of statements.
260+
std::stack<TIntermTyped *, TVector<TIntermTyped *>> stmts;
261+
stmts.push(sequenceOperator->getRight());
262+
stmts.push(sequenceOperator->getLeft());
263+
while (!stmts.empty())
264+
{
265+
TIntermTyped *stmt = stmts.top();
266+
stmts.pop();
267+
268+
if (TIntermBinary *nestedSequenceOperator = stmt->getAsBinaryNode();
269+
nestedSequenceOperator && nestedSequenceOperator->getOp() == EOpComma)
270+
{
271+
stmts.push(nestedSequenceOperator->getRight());
272+
stmts.push(nestedSequenceOperator->getLeft());
273+
continue;
274+
}
275+
276+
extractedStmts.push_back(stmt);
277+
}
278+
279+
// The last statement needs a return, if it is not of type void (i.e. the type of a function
280+
// call to a void-returning function).
281+
TIntermNode *lastStmt = extractedStmts.back();
282+
if (lastStmt->getAsTyped()->getBasicType() != EbtVoid)
283+
{
284+
extractedStmts.back() = new TIntermBranch(TOperator::EOpReturn, lastStmt->getAsTyped());
285+
}
286+
287+
TIntermBlock *substituteFunctionBody = new TIntermBlock(std::move(extractedStmts));
288+
289+
TFunction *substituteFunction =
290+
new TFunction(mSymbolTable, kEmptyImmutableString, SymbolType::AngleInternal,
291+
GetHelperType(sequenceOperator->getType(), EvqTemporary),
292+
!sequenceOperator->hasSideEffects());
293+
294+
// Make sure to insert new function definitions and prototypes.
295+
newFunctionPrototypes.push_back(new TIntermFunctionPrototype(substituteFunction));
296+
TIntermFunctionDefinition *substituteFunctionDef = new TIntermFunctionDefinition(
297+
new TIntermFunctionPrototype(substituteFunction), substituteFunctionBody);
298+
newFunctionDefinitions.push_back(substituteFunctionDef);
299+
300+
addParamsFromOtherFunctionAndReplace(substituteFunction, substituteFunctionDef,
301+
parentFunction);
302+
303+
return substituteFunction;
304+
}
305+
231306
bool replaceTempVarsWithGlobals(TIntermBlock *root)
232307
{
233308
TIntermSequence globalDeclarations;
@@ -326,6 +401,14 @@ class PullExpressionsIntoFunctionsTraverser : public TIntermTraverser
326401
substituteFunction = replaceTernary(*ternary, constructAndMetadata.parentFunction,
327402
newFunctionPrototypes, newFunctionDefinitions);
328403
}
404+
else if (UntranslatableCommaOperator *commaOperator =
405+
std::get_if<UntranslatableCommaOperator>(&construct))
406+
{
407+
untranslatableNode = commaOperator->commaOperator;
408+
substituteFunction = replaceSequenceOperator(
409+
commaOperator->commaOperator, constructAndMetadata.parentFunction,
410+
newFunctionPrototypes, newFunctionDefinitions);
411+
}
329412
else
330413
{
331414
UNREACHABLE();

src/compiler/translator/wgsl/TranslatorWGSL.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,8 @@ OperatorInfo OutputWGSLTraverser::useOperatorAndGetInfo(TIntermNode *current,
506506
case TOperator::EOpComma:
507507
// WGSL does not have a comma operator or any other way to implement "statement list as
508508
// an expression", so nested expressions will have to be pulled out into statements.
509-
UNIMPLEMENTED();
509+
// This should have been done by a preprocessing.
510+
UNREACHABLE();
510511
return {"TODO_operator"};
511512
case TOperator::EOpAssign:
512513
return {"="};

src/tests/compiler_tests/WGSLOutput_test.cpp

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,138 @@ fn wgslMain(ANGLE_input_annotated : ANGLE_Input_Annotated) -> ANGLE_Output_Annot
13701370
EXPECT_TRUE(foundInCode(outputString.c_str()));
13711371
}
13721372

1373+
TEST_F(WGSLOutputTest, CommaOperator)
1374+
{
1375+
const std::string &shaderString =
1376+
R"(#version 300 es
1377+
precision mediump float;
1378+
1379+
float globVar;
1380+
1381+
in float inVar;
1382+
out vec4 fragColor;
1383+
1384+
void setGlobVar() {
1385+
globVar = 1.0;
1386+
}
1387+
1388+
void main() {
1389+
fragColor = vec4(0.0);
1390+
float tempVar;
1391+
fragColor.x = (globVar = inVar, tempVar = globVar, tempVar);
1392+
1393+
(tempVar = 5.0, globVar = 6.0, setGlobVar());
1394+
1395+
float a,b,c,d,e;
1396+
fragColor.x += ((a = 1.0, b = a), (c = b, (d = c)), (setGlobVar(), e = d, e));
1397+
})";
1398+
const std::string &outputString =
1399+
R"(diagnostic(warning,derivative_uniformity);
1400+
struct ANGLE_Input_Global {
1401+
inVar : f32,
1402+
};
1403+
1404+
var<private> ANGLE_input_global : ANGLE_Input_Global;
1405+
1406+
struct ANGLE_Input_Annotated {
1407+
@location(@@@@@@) inVar : f32,
1408+
};
1409+
1410+
struct ANGLE_Output_Global {
1411+
fragColor : vec4<f32>,
1412+
};
1413+
1414+
var<private> ANGLE_output_global : ANGLE_Output_Global;
1415+
1416+
struct ANGLE_Output_Annotated {
1417+
@location(@@@@@@) fragColor : vec4<f32>,
1418+
};
1419+
1420+
@group(2) @binding(0) var<uniform> ANGLEUniforms : ANGLEUniformBlock;
1421+
1422+
struct ANGLEDepthRangeParams
1423+
{
1424+
near : f32,
1425+
far : f32,
1426+
diff : f32,
1427+
};
1428+
1429+
var<private> _uglobVar : f32;
1430+
;
1431+
;
1432+
var<private> _utempVar : f32;
1433+
var<private> _ua : f32;
1434+
var<private> _ub : f32;
1435+
var<private> _uc : f32;
1436+
var<private> _ud : f32;
1437+
var<private> _ue : f32;
1438+
1439+
struct ANGLEUniformBlock
1440+
{
1441+
@align(16) acbBufferOffsets : vec2<u32>,
1442+
depthRange : vec2<f32>,
1443+
renderArea : u32,
1444+
flipXY : u32,
1445+
dither : u32,
1446+
misc : u32,
1447+
};
1448+
1449+
;
1450+
;
1451+
;
1452+
;
1453+
1454+
fn _usetGlobVar()
1455+
{
1456+
(_uglobVar) = (1.0f);
1457+
}
1458+
1459+
fn _umain()
1460+
{
1461+
(ANGLE_output_global.fragColor) = (vec4<f32>(0.0f, 0.0f, 0.0f, 0.0f));
1462+
((ANGLE_output_global.fragColor).x) = (sbca());
1463+
sbcb();
1464+
((ANGLE_output_global.fragColor).x) += (sbcc());
1465+
}
1466+
1467+
fn sbca() -> f32
1468+
{
1469+
(_uglobVar) = (ANGLE_input_global.inVar);
1470+
(_utempVar) = (_uglobVar);
1471+
return _utempVar;
1472+
}
1473+
1474+
fn sbcb()
1475+
{
1476+
(_utempVar) = (5.0f);
1477+
(_uglobVar) = (6.0f);
1478+
_usetGlobVar();
1479+
}
1480+
1481+
fn sbcc() -> f32
1482+
{
1483+
(_ua) = (1.0f);
1484+
(_ub) = (_ua);
1485+
(_uc) = (_ub);
1486+
(_ud) = (_uc);
1487+
_usetGlobVar();
1488+
(_ue) = (_ud);
1489+
return _ue;
1490+
}
1491+
@fragment
1492+
fn wgslMain(ANGLE_input_annotated : ANGLE_Input_Annotated) -> ANGLE_Output_Annotated
1493+
{
1494+
ANGLE_input_global.inVar = ANGLE_input_annotated.inVar;
1495+
_umain();
1496+
var ANGLE_output_annotated : ANGLE_Output_Annotated;
1497+
ANGLE_output_annotated.fragColor = ANGLE_output_global.fragColor;
1498+
return ANGLE_output_annotated;
1499+
}
1500+
)";
1501+
compile(shaderString);
1502+
EXPECT_TRUE(foundInCode(outputString.c_str()));
1503+
}
1504+
13731505
TEST_F(WGSLOutputTest, UniformsWithMatCx2)
13741506
{
13751507
const std::string &shaderString =

0 commit comments

Comments
 (0)