spv::Id createUnaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, spv::Id operand, bool isFloat);
spv::Id createConversion(glslang::TOperator op, spv::Decoration precision, spv::Id destTypeId, spv::Id operand);
spv::Id makeSmearedConstant(spv::Id constant, int vectorSize);
+ spv::Id createAtomicOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands);
spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands);
spv::Id createNoArgOperation(glslang::TOperator op);
spv::Id getSymbolId(const glslang::TIntermSymbol* node);
builder.createNoResultOp(spv::OpEndStreamPrimitive, operand);
return false;
+ case glslang::EOpAtomicCounterIncrement:
+ case glslang::EOpAtomicCounterDecrement:
+ case glslang::EOpAtomicCounter:
+ {
+ // Handle all of the atomics in one place, in createAtomicOperation()
+ std::vector<spv::Id> operands;
+ operands.push_back(operand);
+ result = createAtomicOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands);
+ return false;
+ }
default:
spv::MissingFunctionality("glslang unary");
break;
bool reduceComparison = true;
bool isMatrix = false;
bool noReturnValue = false;
+ bool atomic = false;
assert(node->getOp());
// These all have 0 operands and will naturally finish up in the code below for 0 operands
break;
+ case glslang::EOpAtomicAdd:
+ case glslang::EOpAtomicMin:
+ case glslang::EOpAtomicMax:
+ case glslang::EOpAtomicAnd:
+ case glslang::EOpAtomicOr:
+ case glslang::EOpAtomicXor:
+ case glslang::EOpAtomicExchange:
+ case glslang::EOpAtomicCompSwap:
+ atomic = true;
+ break;
+
default:
break;
}
//
// See if it maps to a regular operation.
//
-
if (binOp != glslang::EOpNull) {
glslang::TIntermTyped* left = node->getSequence()[0]->getAsTyped();
glslang::TIntermTyped* right = node->getSequence()[1]->getAsTyped();
return false;
}
+ //
+ // Create the list of operands.
+ //
glslang::TIntermSequence& glslangOperands = node->getSequence();
std::vector<spv::Id> operands;
for (int arg = 0; arg < (int)glslangOperands.size(); ++arg) {
else
operands.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangOperands[arg]->getAsTyped()->getType())));
}
- switch (glslangOperands.size()) {
- case 0:
- result = createNoArgOperation(node->getOp());
- break;
- case 1:
- result = createUnaryOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands.front(), node->getType().getBasicType() == glslang::EbtFloat || node->getType().getBasicType() == glslang::EbtDouble);
- break;
- default:
- result = createMiscOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands);
- break;
+
+ if (atomic) {
+ // Handle all atomics
+ result = createAtomicOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands);
+ } else {
+ // Pass through to generic operations.
+ switch (glslangOperands.size()) {
+ case 0:
+ result = createNoArgOperation(node->getOp());
+ break;
+ case 1:
+ result = createUnaryOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands.front(), node->getType().getBasicType() == glslang::EbtFloat || node->getType().getBasicType() == glslang::EbtDouble);
+ break;
+ default:
+ result = createMiscOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands);
+ break;
+ }
}
if (noReturnValue)
case glslang::EbtUint:
spvType = builder.makeUintType(32);
break;
+ case glslang::EbtAtomicUint:
+ spv::TbdFunctionality("Is atomic_uint an opaque handle in the uniform storage class, or an addresses in the atomic storage class?");
+ spvType = builder.makeUintType(32);
+ break;
case glslang::EbtSampler:
{
const glslang::TSampler& sampler = type.getSampler();
return builder.makeCompositeConstant(vectorTypeId, components);
}
+// For glslang ops that map to SPV atomic opCodes
+spv::Id TGlslangToSpvTraverser::createAtomicOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands)
+{
+ spv::Op opCode = spv::OpNop;
+
+ switch (op) {
+ case glslang::EOpAtomicAdd:
+ opCode = spv::OpAtomicIAdd;
+ break;
+ case glslang::EOpAtomicMin:
+ opCode = spv::OpAtomicIMin;
+ break;
+ case glslang::EOpAtomicMax:
+ opCode = spv::OpAtomicIMax;
+ break;
+ case glslang::EOpAtomicAnd:
+ opCode = spv::OpAtomicAnd;
+ break;
+ case glslang::EOpAtomicOr:
+ opCode = spv::OpAtomicOr;
+ break;
+ case glslang::EOpAtomicXor:
+ opCode = spv::OpAtomicXor;
+ break;
+ case glslang::EOpAtomicExchange:
+ opCode = spv::OpAtomicExchange;
+ break;
+ case glslang::EOpAtomicCompSwap:
+ opCode = spv::OpAtomicCompareExchange;
+ break;
+ case glslang::EOpAtomicCounterIncrement:
+ opCode = spv::OpAtomicIIncrement;
+ break;
+ case glslang::EOpAtomicCounterDecrement:
+ opCode = spv::OpAtomicIDecrement;
+ break;
+ case glslang::EOpAtomicCounter:
+ opCode = spv::OpAtomicLoad;
+ break;
+ default:
+ spv::MissingFunctionality("missing nested atomic");
+ break;
+ }
+
+ // Sort out the operands
+ // - mapping from glslang -> SPV
+ // - there are extra SPV operands with no glslang source
+ std::vector<spv::Id> spvAtomicOperands; // hold the spv operands
+ auto opIt = operands.begin(); // walk the glslang operands
+ spvAtomicOperands.push_back(*(opIt++));
+ spvAtomicOperands.push_back(spv::ExecutionScopeDevice); // TBD: what is the correct scope?
+ spvAtomicOperands.push_back( spv::MemorySemanticsMaskNone); // TBD: what are the correct memory semantics?
+
+ // Add the rest of the operands, skipping the first one, which was dealt with above.
+ // For some ops, there are none, for some 1, for compare-exchange, 2.
+ for (; opIt != operands.end(); ++opIt)
+ spvAtomicOperands.push_back(*opIt);
+
+ return builder.createOp(opCode, typeId, spvAtomicOperands);
+}
+
spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands)
{
spv::Op opCode = spv::OpNop;
case glslang::EOpRefract:
libCall = GLSL_STD_450::Refract;
break;
+
default:
return 0;
}
id = builder.createBinOp(opCode, typeId, operands[0], operands[1]);
break;
case 3:
- id = builder.createTernaryOp(opCode, typeId, operands[0], operands[1], operands[2]);
+ id = builder.createTriOp(opCode, typeId, operands[0], operands[1], operands[2]);
break;
default:
// These do not exist yet
#include <stdio.h>
#include <stdlib.h>
+#include <unordered_set>
+
#include "SpvBuilder.h"
#ifndef _WIN32
return op->getResultId();
}
-Id Builder::createTernaryOp(Op opCode, Id typeId, Id op1, Id op2, Id op3)
+Id Builder::createOp(Op opCode, Id typeId, std::vector<Id>& operands)
{
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
- op->addIdOperand(op1);
- op->addIdOperand(op2);
- op->addIdOperand(op3);
+ for (auto operand : operands)
+ op->addIdOperand(operand);
buildPoint->addInstruction(op);
return op->getResultId();
}
}
-void MissingFunctionality(const char* fun)
+void TbdFunctionality(const char* tbd)
{
- printf("Missing functionality: %s\n", fun);
- exit(1);
+ static std::unordered_set<const char*> issued;
+
+ if (issued.find(tbd) == issued.end()) {
+ printf("TBD functionality: %s\n", tbd);
+ issued.insert(tbd);
+ }
}
-void ValidationError(const char* error)
+void MissingFunctionality(const char* fun)
{
- printf("Validation Error: %s\n", error);
+ printf("Missing functionality: %s\n", fun);
+ exit(1);
}
Builder::Loop::Loop(Builder& builder, bool testFirstArg)
Id createUnaryOp(Op, Id typeId, Id operand);
Id createBinOp(Op, Id typeId, Id operand1, Id operand2);
Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3);
- Id createTernaryOp(Op, Id typeId, Id operand1, Id operand2, Id operand3);
+ Id createOp(Op, Id typeId, std::vector<Id>& operands);
Id createFunctionCall(spv::Function*, std::vector<spv::Id>&);
// Take an rvalue (source) and a set of channels to extract from it to
std::stack<Loop> loops;
}; // end Builder class
+// Use for non-fatal notes about what's not complete
+void TbdFunctionality(const char*);
+
+// Use for fatal missing functionality
void MissingFunctionality(const char*);
-void ValidationError(const char* error);
}; // end spv namespace
--- /dev/null
+spv.atomic.comp\r
+Warning, version 310 is not yet complete; most version-specific features are present, but some are missing.\r
+\r
+\r
+Linked compute stage:\r
+\r
+\r
+TBD functionality: Is atomic_uint an opaque handle in the uniform storage class, or an addresses in the atomic storage class?\r
+TBD functionality: Is atomic_uint an opaque handle in the uniform storage class, or an addresses in the atomic storage class?\r
+TBD functionality: Is atomic_uint an opaque handle in the uniform storage class, or an addresses in the atomic storage class?\r
+TBD functionality: Is atomic_uint an opaque handle in the uniform storage class, or an addresses in the atomic storage class?\r
+TBD functionality: Is atomic_uint an opaque handle in the uniform storage class, or an addresses in the atomic storage class?\r
+// Module Version 99\r
+// Generated by (magic number): 51a00bb\r
+// Id's are bound by 75\r
+\r
+ Source ESSL 310\r
+ 1: ExtInstImport "GLSL.std.450"\r
+ MemoryModel Logical GLSL450\r
+ EntryPoint GLCompute 4\r
+ Name 4 "main"\r
+ Name 11 "func(au1;"\r
+ Name 10 "c"\r
+ Name 13 "atoms("\r
+ Name 20 "counter"\r
+ Name 21 "param"\r
+ Name 24 "val"\r
+ Name 28 "countArr"\r
+ Name 38 "origi"\r
+ Name 40 "atomi"\r
+ Name 44 "origu"\r
+ Name 46 "atomu"\r
+ Name 48 "value"\r
+ Name 72 "arrX"\r
+ Name 73 "arrY"\r
+ Name 74 "arrZ"\r
+ Decorate 20(counter) PrecisionHigh \r
+ Decorate 20(counter) Binding 0\r
+ Decorate 24(val) PrecisionHigh \r
+ Decorate 28(countArr) PrecisionHigh \r
+ Decorate 28(countArr) Binding 0\r
+ Decorate 38(origi) PrecisionHigh \r
+ Decorate 40(atomi) PrecisionHigh \r
+ Decorate 44(origu) PrecisionHigh \r
+ Decorate 46(atomu) PrecisionHigh \r
+ Decorate 48(value) PrecisionHigh \r
+ Decorate 72(arrX) PrecisionHigh \r
+ Decorate 72(arrX) NoStaticUse \r
+ Decorate 73(arrY) PrecisionHigh \r
+ Decorate 73(arrY) NoStaticUse \r
+ Decorate 74(arrZ) PrecisionHigh \r
+ Decorate 74(arrZ) NoStaticUse \r
+ 2: TypeVoid\r
+ 3: TypeFunction 2 \r
+ 7: TypeInt 32 0\r
+ 8: TypePointer Function 7(int)\r
+ 9: TypeFunction 7(int) 8(ptr)\r
+ 19: TypePointer UniformConstant 7(int)\r
+ 20(counter): 19(ptr) Variable UniformConstant \r
+ 25: 7(int) Constant 4\r
+ 26: TypeArray 7(int) 25\r
+ 27: TypePointer UniformConstant 26\r
+ 28(countArr): 27(ptr) Variable UniformConstant \r
+ 29: TypeInt 32 1\r
+ 30: 29(int) Constant 2\r
+ 37: TypePointer Function 29(int)\r
+ 39: TypePointer WorkgroupLocal 29(int)\r
+ 40(atomi): 39(ptr) Variable WorkgroupLocal \r
+ 42: 29(int) Constant 3\r
+ 45: TypePointer WorkgroupLocal 7(int)\r
+ 46(atomu): 45(ptr) Variable WorkgroupLocal \r
+ 48(value): 19(ptr) Variable UniformConstant \r
+ 52: 7(int) Constant 7\r
+ 60: 29(int) Constant 7\r
+ 66: 7(int) Constant 10\r
+ 69: 7(int) Constant 1\r
+ 70: TypeArray 29(int) 69\r
+ 71: TypePointer PrivateGlobal 70\r
+ 72(arrX): 71(ptr) Variable PrivateGlobal \r
+ 73(arrY): 71(ptr) Variable PrivateGlobal \r
+ 74(arrZ): 71(ptr) Variable PrivateGlobal \r
+ 4(main): 2 Function None 3\r
+ 5: Label\r
+ 21(param): 8(ptr) Variable Function \r
+ 24(val): 8(ptr) Variable Function \r
+ MemoryBarrier Device AtomicCounterMemory \r
+ 22: 7(int) Load 20(counter) \r
+ Store 21(param) 22 \r
+ 23: 7(int) FunctionCall 11(func(au1;) 21(param)\r
+ 31: 19(ptr) AccessChain 28(countArr) 30\r
+ 32: 7(int) Load 31 \r
+ 33: 7(int) AtomicLoad 32 Device None\r
+ 34: 7(int) Load 31 \r
+ Store 24(val) 34 \r
+ 35: 7(int) Load 20(counter) \r
+ 36: 7(int) AtomicIDecrement 35 Device None\r
+ Branch 6\r
+ 6: Label\r
+ Return\r
+ FunctionEnd\r
+ 11(func(au1;): 7(int) Function None 9\r
+ 10(c): 8(ptr) FunctionParameter\r
+ 12: Label\r
+ 15: 7(int) Load 10(c) \r
+ 16: 7(int) AtomicIIncrement 15 Device None\r
+ 17: 7(int) Load 10(c) \r
+ ReturnValue 17\r
+ FunctionEnd\r
+ 13(atoms(): 2 Function None 3\r
+ 14: Label\r
+ 38(origi): 37(ptr) Variable Function \r
+ 44(origu): 8(ptr) Variable Function \r
+ 41: 29(int) Load 40(atomi) \r
+ 43: 29(int) AtomicIAdd 41 Device None 42\r
+ Store 38(origi) 43 \r
+ 47: 7(int) Load 46(atomu) \r
+ 49: 7(int) Load 48(value) \r
+ 50: 7(int) AtomicAnd 47 Device None 49\r
+ Store 44(origu) 50 \r
+ 51: 7(int) Load 46(atomu) \r
+ 53: 7(int) AtomicOr 51 Device None 52\r
+ Store 44(origu) 53 \r
+ 54: 7(int) Load 46(atomu) \r
+ 55: 7(int) AtomicXor 54 Device None 52\r
+ Store 44(origu) 55 \r
+ 56: 7(int) Load 46(atomu) \r
+ 57: 7(int) Load 48(value) \r
+ 58: 7(int) AtomicIMin 56 Device None 57\r
+ Store 44(origu) 58 \r
+ 59: 29(int) Load 40(atomi) \r
+ 61: 29(int) AtomicIMax 59 Device None 60\r
+ Store 38(origi) 61 \r
+ 62: 29(int) Load 40(atomi) \r
+ 63: 29(int) Load 38(origi) \r
+ 64: 29(int) AtomicExchange 62 Device None 63\r
+ Store 38(origi) 64 \r
+ 65: 7(int) Load 46(atomu) \r
+ 67: 7(int) Load 48(value) \r
+ 68: 7(int) AtomicCompareExchange 65 Device None 66 67\r
+ Store 44(origu) 68 \r
+ Return\r
+ FunctionEnd\r
--- /dev/null
+#version 310 es\r
+\r
+layout(binding = 0) uniform atomic_uint counter;\r
+\r
+layout(binding = 0, offset = 4) uniform atomic_uint countArr[4];\r
+uniform uint value;\r
+\r
+int arrX[gl_WorkGroupSize.x];\r
+int arrY[gl_WorkGroupSize.y];\r
+int arrZ[gl_WorkGroupSize.z];\r
+\r
+uint func(atomic_uint c)\r
+{\r
+ return atomicCounterIncrement(c);\r
+}\r
+\r
+void main()\r
+{\r
+ memoryBarrierAtomicCounter();\r
+ func(counter);\r
+ uint val = atomicCounter(countArr[2]);\r
+ atomicCounterDecrement(counter);\r
+}\r
+\r
+shared int atomi;\r
+shared uint atomu;\r
+\r
+void atoms()\r
+{\r
+ int origi = atomicAdd(atomi, 3);\r
+ uint origu = atomicAnd(atomu, value);\r
+ origu = atomicOr(atomu, 7u);\r
+ origu = atomicXor(atomu, 7u);\r
+ origu = atomicMin(atomu, value);\r
+ origi = atomicMax(atomi, 7);\r
+ origi = atomicExchange(atomi, origi);\r
+ origu = atomicCompSwap(atomu, 10u, value);\r
+}\r
spv.varyingArrayIndirect.frag
spv.voidFunction.frag
spv.whileLoop.frag
+spv.atomic.comp