SPIRV: Add the support of missing image functions #72
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index 0a069c2..baa6dea 100755
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -95,7 +95,7 @@
void makeGlobalInitializers(const glslang::TIntermSequence&);
void visitFunctions(const glslang::TIntermSequence&);
void handleFunctionEntry(const glslang::TIntermAggregate* node);
- void translateArguments(const glslang::TIntermSequence& glslangArguments, std::vector<spv::Id>& arguments);
+ void translateArguments(glslang::TIntermAggregate& node, std::vector<spv::Id>& arguments);
void translateArguments(glslang::TIntermUnary& node, std::vector<spv::Id>& arguments);
spv::Id createImageTextureFunctionCall(glslang::TIntermOperator* node);
spv::Id handleUserFunctionCall(const glslang::TIntermAggregate*);
@@ -177,6 +177,8 @@
else if (type.getQualifier().isUniformOrBuffer()) {
if (type.getBasicType() == glslang::EbtBlock)
return spv::StorageClassUniform;
+ else if (type.getBasicType() == glslang::EbtAtomicUint)
+ return spv::StorageClassAtomicCounter;
else
return spv::StorageClassUniformConstant;
// TODO: how are we distuingishing between default and non-default non-writable uniforms? Do default uniforms even exist?
@@ -343,6 +345,56 @@
}
}
+// Translate glslang image layout format to SPIR-V image format.
+spv::ImageFormat TranslateImageFormat(const glslang::TType& type)
+{
+ assert(type.getBasicType() == glslang::EbtSampler);
+
+ switch (type.getQualifier().layoutFormat) {
+ case glslang::ElfNone: return spv::ImageFormatUnknown;
+ case glslang::ElfRgba32f: return spv::ImageFormatRgba32f;
+ case glslang::ElfRgba16f: return spv::ImageFormatRgba16f;
+ case glslang::ElfR32f: return spv::ImageFormatR32f;
+ case glslang::ElfRgba8: return spv::ImageFormatRgba8;
+ case glslang::ElfRgba8Snorm: return spv::ImageFormatRgba8Snorm;
+ case glslang::ElfRg32f: return spv::ImageFormatRg32f;
+ case glslang::ElfRg16f: return spv::ImageFormatRg16f;
+ case glslang::ElfR11fG11fB10f: return spv::ImageFormatR11fG11fB10f;
+ case glslang::ElfR16f: return spv::ImageFormatR16f;
+ case glslang::ElfRgba16: return spv::ImageFormatRgba16;
+ case glslang::ElfRgb10A2: return spv::ImageFormatRgb10A2;
+ case glslang::ElfRg16: return spv::ImageFormatRg16;
+ case glslang::ElfRg8: return spv::ImageFormatRg8;
+ case glslang::ElfR16: return spv::ImageFormatR16;
+ case glslang::ElfR8: return spv::ImageFormatR8;
+ case glslang::ElfRgba16Snorm: return spv::ImageFormatRgba16Snorm;
+ case glslang::ElfRg16Snorm: return spv::ImageFormatRg16Snorm;
+ case glslang::ElfRg8Snorm: return spv::ImageFormatRg8Snorm;
+ case glslang::ElfR16Snorm: return spv::ImageFormatR16Snorm;
+ case glslang::ElfR8Snorm: return spv::ImageFormatR8Snorm;
+ case glslang::ElfRgba32i: return spv::ImageFormatRgba32i;
+ case glslang::ElfRgba16i: return spv::ImageFormatRgba16i;
+ case glslang::ElfRgba8i: return spv::ImageFormatRgba8i;
+ case glslang::ElfR32i: return spv::ImageFormatR32i;
+ case glslang::ElfRg32i: return spv::ImageFormatRg32i;
+ case glslang::ElfRg16i: return spv::ImageFormatRg16i;
+ case glslang::ElfRg8i: return spv::ImageFormatRg8i;
+ case glslang::ElfR16i: return spv::ImageFormatR16i;
+ case glslang::ElfR8i: return spv::ImageFormatR8i;
+ case glslang::ElfRgba32ui: return spv::ImageFormatRgba32ui;
+ case glslang::ElfRgba16ui: return spv::ImageFormatRgba16ui;
+ case glslang::ElfRgba8ui: return spv::ImageFormatRgba8ui;
+ case glslang::ElfR32ui: return spv::ImageFormatR32ui;
+ case glslang::ElfRg32ui: return spv::ImageFormatRg32ui;
+ case glslang::ElfRg16ui: return spv::ImageFormatRg16ui;
+ case glslang::ElfRgb10a2ui: return spv::ImageFormatRgb10a2ui;
+ case glslang::ElfRg8ui: return spv::ImageFormatRg8ui;
+ case glslang::ElfR16ui: return spv::ImageFormatR16ui;
+ case glslang::ElfR8ui: return spv::ImageFormatR8ui;
+ default: return (spv::ImageFormat)spv::BadValue;
+ }
+}
+
//
// Implement the TGlslangToSpvTraverser class.
//
@@ -680,7 +732,14 @@
builder.clearAccessChain();
node->getOperand()->traverse(this);
- spv::Id operand = builder.accessChainLoad(TranslatePrecisionDecoration(node->getOperand()->getType()));
+ spv::Id operand = spv::NoResult;
+
+ if (node->getOp() == glslang::EOpAtomicCounterIncrement ||
+ node->getOp() == glslang::EOpAtomicCounterDecrement ||
+ node->getOp() == glslang::EOpAtomicCounter)
+ operand = builder.accessChainGetLValue(); // Special case l-value operands
+ else
+ operand = builder.accessChainLoad(TranslatePrecisionDecoration(node->getOperand()->getType()));
spv::Decoration precision = TranslatePrecisionDecoration(node->getType());
@@ -764,6 +823,11 @@
return false;
}
+ else if (node->getOp() == glslang::EOpImageStore)
+ {
+ // "imageStore" is a special case, which has no result
+ return false;
+ }
glslang::TOperator binOp = glslang::EOpNull;
bool reduceComparison = true;
@@ -901,7 +965,7 @@
case glslang::EOpConstructStruct:
{
std::vector<spv::Id> arguments;
- translateArguments(node->getSequence(), arguments);
+ translateArguments(*node, arguments);
spv::Id resultTypeId = convertGlslangToSpvType(node->getType());
spv::Id constructed;
if (node->getOp() == glslang::EOpConstructStruct || node->getType().isArray()) {
@@ -1361,7 +1425,7 @@
{
const glslang::TSampler& sampler = type.getSampler();
spvType = builder.makeImageType(getSampledType(sampler), TranslateDimensionality(sampler), sampler.shadow, sampler.arrayed, sampler.ms,
- sampler.image ? 2 : 1, spv::ImageFormatUnknown); // TODO: translate format, needed for GLSL image ops
+ sampler.image ? 2 : 1, TranslateImageFormat(type));
// OpenGL "textures" need to be combined with a sampler
if (! sampler.image)
spvType = builder.makeSampledImageType(spvType);
@@ -1609,12 +1673,37 @@
builder.setBuildPoint(functionBlock);
}
-void TGlslangToSpvTraverser::translateArguments(const glslang::TIntermSequence& glslangArguments, std::vector<spv::Id>& arguments)
+void TGlslangToSpvTraverser::translateArguments(glslang::TIntermAggregate& node, std::vector<spv::Id>& arguments)
{
+ const glslang::TIntermSequence& glslangArguments = node.getSequence();
for (int i = 0; i < (int)glslangArguments.size(); ++i) {
builder.clearAccessChain();
glslangArguments[i]->traverse(this);
- arguments.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangArguments[i]->getAsTyped()->getType())));
+
+ // Special case l-value operands
+ bool lvalue = false;
+ switch (node.getOp()) {
+ case glslang::EOpImageAtomicAdd:
+ case glslang::EOpImageAtomicMin:
+ case glslang::EOpImageAtomicMax:
+ case glslang::EOpImageAtomicAnd:
+ case glslang::EOpImageAtomicOr:
+ case glslang::EOpImageAtomicXor:
+ case glslang::EOpImageAtomicExchange:
+ case glslang::EOpImageAtomicCompSwap:
+ if (i == 0)
+ lvalue = true;
+ break;
+ default:
+ break;
+ }
+
+ if (lvalue) {
+ arguments.push_back(builder.accessChainGetLValue());
+ }
+ else {
+ arguments.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangArguments[i]->getAsTyped()->getType())));
+ }
}
}
@@ -1627,10 +1716,7 @@
spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermOperator* node)
{
- if (node->isImage()) {
- spv::MissingFunctionality("GLSL image function");
- return spv::NoResult;
- } else if (! node->isTexture()) {
+ if (! node->isImage() && ! node->isTexture()) {
return spv::NoResult;
}
@@ -1643,7 +1729,7 @@
: node->getAsUnaryNode()->getOperand()->getAsTyped()->getType().getSampler();
std::vector<spv::Id> arguments;
if (node->getAsAggregate())
- translateArguments(node->getAsAggregate()->getSequence(), arguments);
+ translateArguments(*node->getAsAggregate(), arguments);
else
translateArguments(*node->getAsUnaryNode(), arguments);
spv::Decoration precision = TranslatePrecisionDecoration(node->getType());
@@ -1675,8 +1761,37 @@
}
}
- // This is no longer a query....
+ // Check for image functions other than queries
+ if (node->isImage()) {
+ if (node->getOp() == glslang::EOpImageLoad) {
+ return builder.createOp(spv::OpImageRead, convertGlslangToSpvType(node->getType()), arguments);
+ }
+ else if (node->getOp() == glslang::EOpImageStore) {
+ builder.createNoResultOp(spv::OpImageWrite, arguments);
+ return spv::NoResult;
+ }
+ else {
+ // Process image atomic operations. GLSL "IMAGE_PARAMS" will involve in constructing an image texel
+ // pointer and this pointer, as the first source operand, is required by SPIR-V atomic operations.
+ std::vector<spv::Id> imageParams;
+ auto opIt = arguments.begin();
+ imageParams.push_back(*(opIt++));
+ imageParams.push_back(*(opIt++));
+ imageParams.push_back(sampler.ms ? *(opIt++) : 0); // For non-MS, the value should be 0
+ spv::Id resultTypeId = builder.makePointer(spv::StorageClassImage, convertGlslangToSpvType(node->getType()));
+ spv::Id pointer = builder.createOp(spv::OpImageTexelPointer, resultTypeId, imageParams);
+
+ std::vector<spv::Id> operands;
+ operands.push_back(pointer);
+ for (; opIt != arguments.end(); ++opIt)
+ operands.push_back(*opIt);
+
+ return createAtomicOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands);
+ }
+ }
+
+ // Check for texture functions other than queries
if (cracked.fetch)
spv::MissingFunctionality("texel fetch");
if (cracked.gather)
@@ -2438,27 +2553,35 @@
switch (op) {
case glslang::EOpAtomicAdd:
+ case glslang::EOpImageAtomicAdd:
opCode = spv::OpAtomicIAdd;
break;
case glslang::EOpAtomicMin:
- opCode = spv::OpAtomicSMin;
+ case glslang::EOpImageAtomicMin:
+ opCode = builder.isSignedType(typeId) ? spv::OpAtomicUMin : spv::OpAtomicSMin;
break;
case glslang::EOpAtomicMax:
- opCode = spv::OpAtomicSMax;
+ case glslang::EOpImageAtomicMax:
+ opCode = builder.isSignedType(typeId) ? spv::OpAtomicUMax : spv::OpAtomicSMax;
break;
case glslang::EOpAtomicAnd:
+ case glslang::EOpImageAtomicAnd:
opCode = spv::OpAtomicAnd;
break;
case glslang::EOpAtomicOr:
+ case glslang::EOpImageAtomicOr:
opCode = spv::OpAtomicOr;
break;
case glslang::EOpAtomicXor:
+ case glslang::EOpImageAtomicXor:
opCode = spv::OpAtomicXor;
break;
case glslang::EOpAtomicExchange:
+ case glslang::EOpImageAtomicExchange:
opCode = spv::OpAtomicExchange;
break;
case glslang::EOpAtomicCompSwap:
+ case glslang::EOpImageAtomicCompSwap:
opCode = spv::OpAtomicCompareExchange;
break;
case glslang::EOpAtomicCounterIncrement:
diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp
index 79665de..d780d19 100755
--- a/SPIRV/SpvBuilder.cpp
+++ b/SPIRV/SpvBuilder.cpp
@@ -822,6 +822,7 @@
case StorageClassWorkgroupLocal:
case StorageClassPrivateGlobal:
case StorageClassWorkgroupGlobal:
+ case StorageClassAtomicCounter:
constantsTypesGlobals.push_back(inst);
module.mapInstruction(inst);
break;
@@ -975,6 +976,15 @@
buildPoint->addInstruction(op);
}
+// An opcode that has one operand, no result id, and no type
+void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands)
+{
+ Instruction* op = new Instruction(opCode);
+ for (auto operand : operands)
+ op->addIdOperand(operand);
+ buildPoint->addInstruction(op);
+}
+
void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics)
{
Instruction* op = new Instruction(OpControlBarrier);
diff --git a/SPIRV/SpvBuilder.h b/SPIRV/SpvBuilder.h
index 7a9f666..698201b 100755
--- a/SPIRV/SpvBuilder.h
+++ b/SPIRV/SpvBuilder.h
@@ -137,6 +137,11 @@
bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; }
unsigned int getConstantScalar(Id resultId) const { return module.getInstruction(resultId)->getImmediateOperand(0); }
+ bool isSignedType(Id typeId) const
+ {
+ assert(getTypeClass(typeId) == OpTypeInt);
+ return module.getInstruction(typeId)->getImmediateOperand(1) == 0u;
+ }
int getTypeNumColumns(Id typeId) const
{
@@ -241,6 +246,7 @@
void createNoResultOp(Op);
void createNoResultOp(Op, Id operand);
+ void createNoResultOp(Op, const std::vector<Id>& operands);
void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask);
void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics);
Id createUnaryOp(Op, Id typeId, Id operand);
diff --git a/glslang/MachineIndependent/Constant.cpp b/glslang/MachineIndependent/Constant.cpp
index e64ee3d..6a797f0 100644
--- a/glslang/MachineIndependent/Constant.cpp
+++ b/glslang/MachineIndependent/Constant.cpp
@@ -768,7 +768,7 @@
}
break;
case EOpReflect:
- // I 2 * dot(N, I) * N: Arguments are (I, N).
+ // I - 2 * dot(N, I) * N: Arguments are (I, N).
dot = childConstUnions[0].dot(childConstUnions[1]);
dot *= 2.0;
for (int comp = 0; comp < numComps; ++comp)