Unverified Commit b2e5b5c5 by greg-lunarg Committed by GitHub

Merge pull request #2576 from KhronosGroup/revert-2569-revert-2242-GL_EXT_vulkan_glsl_relaxed

Revert "Revert "GL_ext_vulkan_glsl_relaxed extension support, and cross stage aware IO mapper""
parents a36d91e5 4e064eef
......@@ -2784,6 +2784,7 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt
break;
case glslang::EOpAtomicAdd:
case glslang::EOpAtomicSubtract:
case glslang::EOpAtomicMin:
case glslang::EOpAtomicMax:
case glslang::EOpAtomicAnd:
......@@ -2955,6 +2956,7 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt
break;
case glslang::EOpAtomicAdd:
case glslang::EOpAtomicSubtract:
case glslang::EOpAtomicMin:
case glslang::EOpAtomicMax:
case glslang::EOpAtomicAnd:
......@@ -6894,6 +6896,7 @@ spv::Id TGlslangToSpvTraverser::createAtomicOperation(glslang::TOperator op, spv
builder.addCapability(spv::CapabilityAtomicFloat64AddEXT);
}
break;
case glslang::EOpAtomicSubtract:
case glslang::EOpAtomicCounterSubtract:
opCode = spv::OpAtomicISub;
break;
......
......@@ -110,6 +110,7 @@ bool SpvToolsValidate = false;
bool NaNClamp = false;
bool stripDebugInfo = false;
bool beQuiet = false;
bool VulkanRulesRelaxed = false;
//
// Return codes from main/exit().
......@@ -195,6 +196,17 @@ std::array<std::array<unsigned int, EShLangCount>, glslang::EResCount> baseBindi
std::array<std::array<TPerSetBaseBinding, EShLangCount>, glslang::EResCount> baseBindingForSet;
std::array<std::vector<std::string>, EShLangCount> baseResourceSetBinding;
std::vector<std::pair<std::string, glslang::TBlockStorageClass>> blockStorageOverrides;
bool setGlobalUniformBlock = false;
std::string globalUniformName;
unsigned int globalUniformBinding;
unsigned int globalUniformSet;
bool setGlobalBufferBlock = false;
std::string atomicCounterBlockName;
unsigned int atomicCounterBlockSet;
// Add things like "#define ..." to a preamble to use in the beginning of the shader.
class TPreamble {
public:
......@@ -397,6 +409,115 @@ void ProcessResourceSetBindingBase(int& argc, char**& argv, std::array<std::vect
}
//
// Process an optional binding base of one the forms:
// --argname name {uniform|buffer|push_constant}
void ProcessBlockStorage(int& argc, char**& argv, std::vector<std::pair<std::string, glslang::TBlockStorageClass>>& storage)
{
if (argc < 3)
usage();
glslang::TBlockStorageClass blockStorage = glslang::EbsNone;
std::string strBacking(argv[2]);
if (strBacking == "uniform")
blockStorage = glslang::EbsUniform;
else if (strBacking == "buffer")
blockStorage = glslang::EbsStorageBuffer;
else if (strBacking == "push_constant")
blockStorage = glslang::EbsPushConstant;
else {
printf("%s: invalid block storage\n", strBacking.c_str());
usage();
}
storage.push_back(std::make_pair(std::string(argv[1]), blockStorage));
argc -= 2;
argv += 2;
}
inline bool isNonDigit(char c) {
// a non-digit character valid in a glsl identifier
return (c == '_') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
// whether string isa valid identifier to be used in glsl
bool isValidIdentifier(const char* str) {
std::string idn(str);
if (idn.length() == 0) {
return false;
}
if (idn.length() >= 3 && idn.substr(0, 3) == "gl_") {
// identifiers startin with "gl_" are reserved
return false;
}
if (!isNonDigit(idn[0])) {
return false;
}
for (unsigned int i = 1; i < idn.length(); ++i) {
if (!(isdigit(idn[i]) || isNonDigit(idn[i]))) {
return false;
}
}
return true;
}
// Process settings for either the global buffer block or global unfirom block
// of the form:
// --argname name set binding
void ProcessGlobalBlockSettings(int& argc, char**& argv, std::string* name, unsigned int* set, unsigned int* binding)
{
if (argc < 4)
usage();
unsigned int curArg = 1;
assert(name || set || binding);
if (name) {
if (!isValidIdentifier(argv[curArg])) {
printf("%s: invalid identifier\n", argv[curArg]);
usage();
}
*name = argv[curArg];
curArg++;
}
if (set) {
errno = 0;
int setVal = ::strtol(argv[curArg], NULL, 10);
if (errno || setVal < 0) {
printf("%s: invalid set\n", argv[curArg]);
usage();
}
*set = setVal;
curArg++;
}
if (binding) {
errno = 0;
int bindingVal = ::strtol(argv[curArg], NULL, 10);
if (errno || bindingVal < 0) {
printf("%s: invalid binding\n", argv[curArg]);
usage();
}
*binding = bindingVal;
curArg++;
}
argc -= (curArg - 1);
argv += (curArg - 1);
}
//
// Do all command-line argument parsing. This includes building up the work-items
// to be processed later, and saving all the command-line options.
//
......@@ -569,6 +690,17 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
lowerword == "resource-set-binding" ||
lowerword == "rsb") {
ProcessResourceSetBindingBase(argc, argv, baseResourceSetBinding);
} else if (lowerword == "set-block-storage" ||
lowerword == "sbs") {
ProcessBlockStorage(argc, argv, blockStorageOverrides);
} else if (lowerword == "set-atomic-counter-block" ||
lowerword == "sacb") {
ProcessGlobalBlockSettings(argc, argv, &atomicCounterBlockName, &atomicCounterBlockSet, nullptr);
setGlobalBufferBlock = true;
} else if (lowerword == "set-default-uniform-block" ||
lowerword == "sdub") {
ProcessGlobalBlockSettings(argc, argv, &globalUniformName, &globalUniformSet, &globalUniformBinding);
setGlobalUniformBlock = true;
} else if (lowerword == "shift-image-bindings" || // synonyms
lowerword == "shift-image-binding" ||
lowerword == "sib") {
......@@ -721,6 +853,9 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
else
Error("unknown -O option");
break;
case 'R':
VulkanRulesRelaxed = true;
break;
case 'S':
if (argc <= 1)
Error("no <stage> specified for -S");
......@@ -1068,6 +1203,24 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
shader->setUniformLocationBase(uniformBase);
#endif
if (VulkanRulesRelaxed) {
for (auto& storageOverride : blockStorageOverrides) {
shader->addBlockStorageOverride(storageOverride.first.c_str(),
storageOverride.second);
}
if (setGlobalBufferBlock) {
shader->setAtomicCounterBlockName(atomicCounterBlockName.c_str());
shader->setAtomicCounterBlockSet(atomicCounterBlockSet);
}
if (setGlobalUniformBlock) {
shader->setGlobalUniformBlockName(globalUniformName.c_str());
shader->setGlobalUniformSet(globalUniformSet);
shader->setGlobalUniformBinding(globalUniformBinding);
}
}
shader->setNanMinMaxClamp(NaNClamp);
#ifdef ENABLE_HLSL
......@@ -1091,6 +1244,8 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
if (targetHlslFunctionality1)
shader->setEnvTargetHlslFunctionality1();
#endif
if (VulkanRulesRelaxed)
shader->setEnvInputVulkanRulesRelaxed();
}
shaders.push_back(shader);
......@@ -1572,6 +1727,9 @@ void usage()
" is searched first, followed by left-to-right order of -I\n"
" -Od disables optimization; may cause illegal SPIR-V for HLSL\n"
" -Os optimizes SPIR-V to minimize size\n"
" -R use relaxed verification rules for generating Vulkan SPIR-V,\n"
" allowing the use of default uniforms, atomic_uints, and\n"
" gl_VertexID and gl_InstanceID keywords.\n"
" -S <stage> uses specified stage rather than parsing the file extension\n"
" choices for <stage> are vert, tesc, tese, geom, frag, or comp\n"
" -U<name> | --undef-macro <name> | --U <name>\n"
......@@ -1649,6 +1807,22 @@ void usage()
" --resource-set-binding [stage] set\n"
" set descriptor set for all resources\n"
" --rsb synonym for --resource-set-binding\n"
" --set-block-backing name {uniform|buffer|push_constant}\n"
" changes the backing type of a uniform, buffer,\n"
" or push_constant block declared in\n"
" in the program, when using -R option.\n"
" This can be used to change the backing\n"
" for existing blocks as well as implicit ones\n"
" such as 'gl_DefaultUniformBlock'.\n"
" --sbs synonym for set-block-storage\n"
" --set-atomic-counter-block name set\n"
" set name, and descriptor set for\n"
" atomic counter blocks, with -R opt\n"
" --sacb synonym for set-atomic-counter-block\n"
" --set-default-uniform-block name set binding\n"
" set name, descriptor set, and binding for\n"
" global default-uniform-block, with -R opt\n"
" --sdub synonym for set-default-uniform-block\n"
" --shift-image-binding [stage] num\n"
" base binding number for images (uav)\n"
" --shift-image-binding [stage] [num set]...\n"
......
vk.relaxed.errorcheck.vert
Shader version: 460
0:? Sequence
0:9 Function Definition: foo( ( global highp 4-component vector of float)
0:9 Function Parameters:
0:10 Sequence
0:10 Branch: Return with expression
0:10 vector swizzle ( temp highp 4-component vector of float)
0:10 a: direct index for structure ( uniform highp 2-component vector of float)
0:10 'anon@0' (layout( column_major std140) uniform block{ uniform highp 2-component vector of float a})
0:10 Constant:
0:10 0 (const uint)
0:10 Sequence
0:10 Constant:
0:10 0 (const int)
0:10 Constant:
0:10 1 (const int)
0:10 Constant:
0:10 0 (const int)
0:10 Constant:
0:10 1 (const int)
0:13 Function Definition: main( ( global void)
0:13 Function Parameters:
0:14 Sequence
0:14 move second child to first child ( temp highp 4-component vector of float)
0:14 'io' (layout( location=0) smooth out highp 4-component vector of float)
0:14 Function Call: foo( ( global highp 4-component vector of float)
0:? Linker Objects
0:? 'io' (layout( location=0) smooth out highp 4-component vector of float)
0:? 'anon@0' (layout( column_major std140) uniform block{ uniform highp 2-component vector of float a})
0:? 'gl_VertexID' ( in int VertexIndex)
0:? 'gl_InstanceID' ( in int InstanceIndex)
vk.relaxed.errorcheck.frag
Shader version: 460
gl_FragCoord origin is upper left
0:? Sequence
0:10 Function Definition: foo( ( global highp 4-component vector of float)
0:10 Function Parameters:
0:11 Sequence
0:11 Branch: Return with expression
0:11 a: direct index for structure ( uniform highp 4-component vector of float)
0:11 'anon@0' (layout( column_major std140) uniform block{ uniform highp 4-component vector of float a})
0:11 Constant:
0:11 0 (const uint)
0:14 Function Definition: main( ( global void)
0:14 Function Parameters:
0:15 Sequence
0:15 move second child to first child ( temp highp 4-component vector of float)
0:15 'o' ( out highp 4-component vector of float)
0:15 add ( temp highp 4-component vector of float)
0:15 'io' (layout( location=0) smooth in highp 4-component vector of float)
0:15 Function Call: foo( ( global highp 4-component vector of float)
0:? Linker Objects
0:? 'io' (layout( location=0) smooth in highp 4-component vector of float)
0:? 'o' ( out highp 4-component vector of float)
0:? 'anon@0' (layout( column_major std140) uniform block{ uniform highp 4-component vector of float a})
Linked vertex stage:
Linked fragment stage:
ERROR: Linking unknown stage stage: Types must match:
a: " uniform highp 2-component vector of float" versus " uniform highp 4-component vector of float"
Shader version: 460
0:? Sequence
0:9 Function Definition: foo( ( global highp 4-component vector of float)
0:9 Function Parameters:
0:10 Sequence
0:10 Branch: Return with expression
0:10 vector swizzle ( temp highp 4-component vector of float)
0:10 a: direct index for structure ( uniform highp 2-component vector of float)
0:10 'anon@0' (layout( column_major std140) uniform block{ uniform highp 2-component vector of float a})
0:10 Constant:
0:10 0 (const uint)
0:10 Sequence
0:10 Constant:
0:10 0 (const int)
0:10 Constant:
0:10 1 (const int)
0:10 Constant:
0:10 0 (const int)
0:10 Constant:
0:10 1 (const int)
0:13 Function Definition: main( ( global void)
0:13 Function Parameters:
0:14 Sequence
0:14 move second child to first child ( temp highp 4-component vector of float)
0:14 'io' (layout( location=0) smooth out highp 4-component vector of float)
0:14 Function Call: foo( ( global highp 4-component vector of float)
0:? Linker Objects
0:? 'io' (layout( location=0) smooth out highp 4-component vector of float)
0:? 'anon@0' (layout( column_major std140) uniform block{ uniform highp 2-component vector of float a})
0:? 'gl_VertexID' ( in int VertexIndex)
0:? 'gl_InstanceID' ( in int InstanceIndex)
Shader version: 460
gl_FragCoord origin is upper left
0:? Sequence
0:10 Function Definition: foo( ( global highp 4-component vector of float)
0:10 Function Parameters:
0:11 Sequence
0:11 Branch: Return with expression
0:11 a: direct index for structure ( uniform highp 4-component vector of float)
0:11 'anon@0' (layout( column_major std140) uniform block{ uniform highp 4-component vector of float a})
0:11 Constant:
0:11 0 (const uint)
0:14 Function Definition: main( ( global void)
0:14 Function Parameters:
0:15 Sequence
0:15 move second child to first child ( temp highp 4-component vector of float)
0:15 'o' ( out highp 4-component vector of float)
0:15 add ( temp highp 4-component vector of float)
0:15 'io' (layout( location=0) smooth in highp 4-component vector of float)
0:15 Function Call: foo( ( global highp 4-component vector of float)
0:? Linker Objects
0:? 'io' (layout( location=0) smooth in highp 4-component vector of float)
0:? 'o' ( out highp 4-component vector of float)
0:? 'anon@0' (layout( column_major std140) uniform block{ uniform highp 4-component vector of float a})
Validation failed
SPIR-V is not generated for failed compile or link
#version 460
layout(location = 5) in outBlock {
vec4 o3;
};
in vec4 gfo1;
in vec2 gfo2;
out vec4 outColor;
uniform vec2 u1;
uniform vec3 u2; // initializer present in vertex stage
uniform vec4 u3 = vec4(0); // initializer matches initializer in vertex stage
uniform mat2 um2 = mat2(4.0);
layout (location = 0, binding = 0) uniform sampler2D glass;
uniform crossStageBlock1 {
uniform vec4 a;
vec4 b;
};
buffer fragOnlyBlock {
vec2 fb1;
};
uniform crossStageBlock2 {
uniform vec4 a;
vec2 b;
} blockName2 [2]; // instance name different from vert
void main()
{
vec4 color = gfo1 * u1.rgrg * u2.rgbr * u3.rgba; // o1 is statically used
outColor = color;
}
#version 460
layout(points) in;
layout(triangle_strip, max_vertices=3) out;
in vec4 vgo1[];
in vec2 vgo2[];
layout(location = 5) in outBlock {
vec4 o3;
} inBlock[];
out vec4 gfo1;
out vec2 gfo2;
layout(location = 5) out outBlock {
vec4 o3;
} gf_out;
uniform vec2 u1;
uniform vec3 u2 = vec3(0); // initializer not present in fragment stage
uniform vec4 u3 = vec4(0); // initializer matches initializer in fragment stage
uniform crossStageBlock2 {
uniform vec4 a;
vec2 b;
} blockName1 [2]; // instance name different from frag
void main()
{
for (int i = 0; i < 3; i++) {
gfo1 = vec4(0);
gfo2 = vec2(0);
gf_out.o3 = inBlock[i].o3;
EmitVertex();
}
EndPrimitive();
}
#version 460
out vec4 vgo1; // declaration order different than fragment shader
out vec2 vgo2; // declaration order different than fragment shader
layout(location = 5) out outBlock {
vec4 o3;
};
uniform vec2 u1;
uniform vec3 u2 = vec3(0); // initializer not present in fragment stage
uniform vec4 u3 = vec4(0); // initializer matches initializer in fragment stage
uniform mat2 um2 = mat2(4.0);
layout (location = 0, binding = 0) uniform sampler2D glass;
uniform crossStageBlock1 {
uniform vec4 a;
vec4 b;
};
buffer vertOnlyBlock {
vec2 vb1;
};
uniform crossStageBlock2 {
uniform vec4 a;
vec2 b;
} blockName1 [2]; // instance name different from frag
void main()
{
vgo1 = vec4(0);
vgo2 = vec2(0);
o3 = vec4(0);
}
#version 460
layout(location = 5) in outBlock {
vec4 o3;
};
in vec2 o2; // declaration order different than vertex shader
in vec4 o1; // declaration order different than vertex shader
out vec4 outColor;
uniform vec2 u1;
uniform vec3 u2; // initializer present in vertex stage
uniform vec4 u3 = vec4(0); // initializer matches initializer in vertex stage
uniform mat2 um2 = mat2(4.0);
layout (location = 0, binding = 0) uniform sampler2D glass;
uniform crossStageBlock1 {
uniform vec4 a;
vec4 b;
};
buffer fragOnlyBlock {
vec2 fb1;
};
uniform crossStageBlock2 {
uniform vec4 a;
vec2 b;
} blockName2 [2]; // instance name different from vert
void main()
{
vec4 color = o1 * u1.rgrg * u2.rgbr * u3.rgba; // o1 is statically used
outColor = color;
}
#version 460
out vec4 o1; // declaration order different than fragment shader
out vec2 o2; // declaration order different than fragment shader
layout(location = 5) out outBlock {
vec4 o3;
};
uniform vec2 u1;
uniform vec3 u2 = vec3(0); // initializer not present in fragment stage
uniform vec4 u3 = vec4(0); // initializer matches initializer in fragment stage
uniform mat2 um2 = mat2(4.0);
layout (location = 0, binding = 0) uniform sampler2D glass;
uniform crossStageBlock1 {
uniform vec4 a;
vec4 b;
};
buffer vertOnlyBlock {
vec2 vb1;
};
uniform crossStageBlock2 {
uniform vec4 a;
vec2 b;
} blockName1 [2]; // instance name different from frag
void main()
{
o1 = vec4(0);
o2 = vec2(0);
o3 = vec4(0);
}
#version 460
layout(location = 5) in outBlock {
vec4 o3;
};
in vec4 gfo1;
in vec2 gfo2;
out vec4 outColor;
layout (binding = 0) uniform sampler2D glass;
uniform crossStageBlock1 {
uniform vec4 a;
vec4 b;
};
readonly buffer fragOnlyBlock {
vec2 fb1;
};
uniform crossStageBlock2 {
uniform vec4 a;
vec2 b;
} blockName2 [2]; // instance name different from vert
vec2 Bar() {
return fb1 +
blockName2[0].b +
blockName2[1].b;
}
vec4 Foo() {
return a +
b +
blockName2[0].a +
blockName2[1].a +
vec4(Bar(), 0.0, 0.0);
}
void main()
{
vec4 color = gfo1; // o1 is statically used
color = color + Foo();
outColor = color;
}
#version 460
layout(points) in;
layout(triangle_strip, max_vertices=3) out;
in vec4 vgo1[];
in vec2 vgo2[];
layout(location = 5) in outBlock {
vec4 o3;
} inBlock[];
out vec4 gfo1;
out vec2 gfo2;
layout(location = 5) out outBlock {
vec4 o3;
} gf_out;
uniform crossStageBlock2 {
uniform vec4 a;
vec2 b;
} blockName1 [2]; // instance name different from frag
void main()
{
for (int i = 0; i < 3; i++) {
gfo1 = vec4(0);
gfo2 = vec2(0);
gf_out.o3 = inBlock[i].o3;
EmitVertex();
}
EndPrimitive();
}
#version 460
out vec4 vgo1; // declaration order different than fragment shader
out vec2 vgo2; // declaration order different than fragment shader
layout(location = 5) out outBlock {
vec4 o3;
};
layout (binding = 0) uniform sampler2D glass;
uniform crossStageBlock1 {
uniform vec4 a;
vec4 b;
};
readonly buffer vertOnlyBlock {
vec2 vb1;
};
uniform crossStageBlock2 {
uniform vec4 a;
vec2 b;
} blockName1 [2]; // instance name different from frag
void main()
{
vgo1 = vec4(0);
vgo2 = vec2(0);
o3 = vec4(0);
}
#version 460
layout (location = 0) in vec4 io;
out vec4 o;
// default uniforms will be gathered into a uniform block
uniform vec4 a; // declared in both stages with different types
vec4 foo() {
return a;
}
void main() {
o = io + foo();
}
\ No newline at end of file
#version 460
layout (location = 0) out vec4 io;
// default uniforms will be gathered into a uniform block
// final global block will merge uniforms from all linked files
uniform vec2 a; // declared in both stages with different type
vec4 foo() {
return a.xyxy;
}
void main() {
io = foo();
}
\ No newline at end of file
#version 460
out vec4 o;
// default uniforms will be gathered into a uniform block
uniform vec4 a;
uniform vec2 b = vec2(0, 0); // initializer will be ignored
layout(location = 0) uniform vec2 c; // location qualifier will be ignored
uniform vec4 d[10];
uniform struct e {
vec2 x;
float y;
uint z;
} structUniform;
// opaque types will not be grouped into uniform block
uniform sampler2D t1;
// shared and packed layout qualifier are silently ignored
layout(shared) uniform UniformBlock {
float j;
vec4 k;
};
layout(packed) buffer BufferBlock {
float j;
vec4 k;
} bufferInstance;
// atomic_uint will be converted to uint and gathered in a buffer block
layout(binding = 0) uniform atomic_uint counter1; // offset not used
layout(binding = 0) uniform atomic_uint counter2; // offset not used
layout(binding = 1) uniform atomic_uint counter3; // offset not used
// atomic counter functions will be converted to equivalent integer atomic operations
uint bar() {
uint j = 0;
j = atomicCounterIncrement(counter1);
j = atomicCounterDecrement(counter1);
j = atomicCounter(counter1);
j = atomicCounterAdd(counter1, 1);
j = atomicCounterAdd(counter1, -1);
j = atomicCounterSubtract(counter1, 1);
j = atomicCounterMin(counter1, j);
j = atomicCounterMax(counter1, j);
j = atomicCounterAnd(counter1, j);
j = atomicCounterOr(counter1, j);
j = atomicCounterXor(counter1, j);
j = atomicCounterExchange(counter1, j);
j = atomicCounterCompSwap(counter1, 0, j);
atomicCounterIncrement(counter2);
atomicCounterIncrement(counter3);
memoryBarrierAtomicCounter();
return j;
}
vec4 foo() {
float f = j + bufferInstance.j + structUniform.y + structUniform.z;
vec2 v2 = b + c + structUniform.x;
vec4 v4 = a + d[0] + d[1] + d[2] + k + bufferInstance.k + texture(t1, vec2(0, 0));
return vec4(f) * vec4(v2, 1, 1) * v4;
}
void main() {
float j = float(bar());
o = j * foo();
}
\ No newline at end of file
#version 460
out vec4 o;
// default uniforms will be gathered into a uniform block
// final global block will merge uniforms from all linked files
uniform vec4 a; // declared in both stages
uniform vec2 b1; // declaration order swapped in other stage
uniform vec2 b2;
uniform vec4 c1; // not delcared in other file
uniform vec4 d;
// final global buffer will berge buffers from all linked files
layout (binding = 0) uniform atomic_uint counter1;
layout (binding = 0) uniform atomic_uint counter2;
vec4 foo();
vec4 bar() {
uint j = atomicCounterIncrement(counter1) + atomicCounterDecrement(counter2);
vec4 v = a + vec4(b1.x, b1.y, b2.x, b2.y) + c1 + d;
return float(j) * v;
}
void main() {
o = foo() + bar();
}
\ No newline at end of file
#version 460
// default uniforms will be gathered into a uniform block
// final global block will merge uniforms from all linked files
uniform vec4 a; // declared in both stages
uniform vec2 b2; // declaration order swapped in other stage
uniform vec2 b1;
uniform vec4 c2; // not delcared in other file
uniform vec4 d;
layout (binding = 0) uniform atomic_uint counter3;
layout (binding = 0) uniform atomic_uint counter2;
vec4 foo() {
uint j = atomicCounterIncrement(counter2) + atomicCounterDecrement(counter3);
vec4 v = a + vec4(b1.x, b1.y, b2.x, b2.y) + c2 + d;
return float(j) * v;
}
\ No newline at end of file
#version 460
layout (location = 0) in vec4 io;
out vec4 o;
// default uniforms will be gathered into a uniform block
// final global block will merge uniforms from all linked files
uniform vec4 a; // declared in both stages
uniform vec2 b1; // declaration order swapped in other stage
uniform vec2 b2;
uniform vec4 c1; // not delcared in other file
uniform vec4 d;
// final global buffer will berge buffers from all linked files
layout (binding = 0) uniform atomic_uint counter1;
layout (binding = 0) uniform atomic_uint counter2;
vec4 foo() {
uint j = atomicCounterIncrement(counter1) + atomicCounterDecrement(counter2);
vec4 v = a + vec4(b1.x, b1.y, b2.x, b2.y) + c1 + d;
return float(j) * v;
}
void main() {
o = io + foo();
}
\ No newline at end of file
#version 460
layout (location = 0) out vec4 io;
// default uniforms will be gathered into a uniform block
// final global block will merge uniforms from all linked files
uniform vec4 a; // declared in both stages
uniform vec2 b2; // declaration order swapped in other stage
uniform vec2 b1;
uniform vec4 c2; // not delcared in other file
uniform vec4 d;
uniform vec4 s[4];
layout (binding = 0) uniform atomic_uint counter3;
layout (binding = 0) uniform atomic_uint counter2;
vec4 foo() {
uint j = atomicCounterIncrement(counter2) + atomicCounterDecrement(counter3);
vec4 v = a + vec4(b1.x, b1.y, b2.x, b2.y) + c2 + d;
return float(j) * v;
}
void main() {
vec4 v = foo();
v = v + s[gl_VertexID];
v.x = v.x - float(gl_InstanceID);
io = v;
}
\ No newline at end of file
......@@ -501,6 +501,7 @@ public:
noContraction = false;
nullInit = false;
#endif
defaultBlock = false;
}
// drop qualifiers that don't belong in a temporary variable
......@@ -514,6 +515,7 @@ public:
specConstant = false;
nonUniform = false;
nullInit = false;
defaultBlock = false;
clearLayout();
}
......@@ -572,6 +574,7 @@ public:
bool specConstant : 1;
bool nonUniform : 1;
bool explicitOffset : 1;
bool defaultBlock : 1; // default blocks with matching names have structures merged when linking
#ifdef GLSLANG_WEB
bool isWriteOnly() const { return false; }
......@@ -756,6 +759,46 @@ public:
}
}
TBlockStorageClass getBlockStorage() const {
if (storage == EvqUniform && !isPushConstant()) {
return EbsUniform;
}
else if (storage == EvqUniform) {
return EbsPushConstant;
}
else if (storage == EvqBuffer) {
return EbsStorageBuffer;
}
return EbsNone;
}
void setBlockStorage(TBlockStorageClass newBacking) {
#ifndef GLSLANG_WEB
layoutPushConstant = (newBacking == EbsPushConstant);
#endif
switch (newBacking) {
case EbsUniform :
if (layoutPacking == ElpStd430) {
// std430 would not be valid
layoutPacking = ElpStd140;
}
storage = EvqUniform;
break;
case EbsStorageBuffer :
storage = EvqBuffer;
break;
#ifndef GLSLANG_WEB
case EbsPushConstant :
storage = EvqUniform;
layoutSet = TQualifier::layoutSetEnd;
layoutBinding = TQualifier::layoutBindingEnd;
break;
#endif
default:
break;
}
}
#ifdef GLSLANG_WEB
bool isPerView() const { return false; }
bool isTaskMemory() const { return false; }
......@@ -852,6 +895,7 @@ public:
return hasNonXfbLayout() ||
hasXfb();
}
TLayoutMatrix layoutMatrix : 3;
TLayoutPacking layoutPacking : 4;
int layoutOffset;
......
......@@ -593,6 +593,7 @@ enum TOperator {
EOpTime,
EOpAtomicAdd,
EOpAtomicSubtract,
EOpAtomicMin,
EOpAtomicMax,
EOpAtomicAnd,
......@@ -1135,6 +1136,8 @@ public:
virtual TBasicType getBasicType() const { return type.getBasicType(); }
virtual TQualifier& getQualifier() { return type.getQualifier(); }
virtual const TQualifier& getQualifier() const { return type.getQualifier(); }
virtual TArraySizes* getArraySizes() { return type.getArraySizes(); }
virtual const TArraySizes* getArraySizes() const { return type.getArraySizes(); }
virtual void propagatePrecision(TPrecisionQualifier);
virtual int getVectorSize() const { return type.getVectorSize(); }
virtual int getMatrixCols() const { return type.getMatrixCols(); }
......
......@@ -1630,6 +1630,36 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
"\n");
}
}
else if (spvVersion.vulkanRelaxed) {
//
// Atomic counter functions act as aliases to normal atomic functions.
// replace definitions to take 'volatile coherent uint' instead of 'atomic_uint'
// and map to equivalent non-counter atomic op
//
if ((profile != EEsProfile && version >= 300) ||
(profile == EEsProfile && version >= 310)) {
commonBuiltins.append(
"uint atomicCounterIncrement(volatile coherent uint);"
"uint atomicCounterDecrement(volatile coherent uint);"
"uint atomicCounter(volatile coherent uint);"
"\n");
}
if (profile != EEsProfile && version >= 460) {
commonBuiltins.append(
"uint atomicCounterAdd(volatile coherent uint, uint);"
"uint atomicCounterSubtract(volatile coherent uint, uint);"
"uint atomicCounterMin(volatile coherent uint, uint);"
"uint atomicCounterMax(volatile coherent uint, uint);"
"uint atomicCounterAnd(volatile coherent uint, uint);"
"uint atomicCounterOr(volatile coherent uint, uint);"
"uint atomicCounterXor(volatile coherent uint, uint);"
"uint atomicCounterExchange(volatile coherent uint, uint);"
"uint atomicCounterCompSwap(volatile coherent uint, uint, uint);"
"\n");
}
}
#endif // !GLSLANG_ANGLE
// Bitfield
......@@ -4124,7 +4154,7 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
}
#ifndef GLSLANG_WEB
if ((profile != EEsProfile && version >= 420) || esBarrier) {
if (spvVersion.vulkan == 0) {
if (spvVersion.vulkan == 0 || spvVersion.vulkanRelaxed) {
commonBuiltins.append("void memoryBarrierAtomicCounter();");
}
commonBuiltins.append("void memoryBarrierImage();");
......@@ -4848,6 +4878,13 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
"in int gl_VertexIndex;"
"in int gl_InstanceIndex;"
);
if (spvVersion.vulkan > 0 && version >= 140 && spvVersion.vulkanRelaxed)
stageBuiltins[EShLangVertex].append(
"in int gl_VertexID;" // declare with 'in' qualifier
"in int gl_InstanceID;"
);
if (version >= 440) {
stageBuiltins[EShLangVertex].append(
"in int gl_BaseVertexARB;"
......@@ -4885,7 +4922,7 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
"mediump float gl_PointSize;" // needs qualifier fixed later
);
} else {
if (spvVersion.vulkan == 0)
if (spvVersion.vulkan == 0 || spvVersion.vulkanRelaxed)
stageBuiltins[EShLangVertex].append(
"in highp int gl_VertexID;" // needs qualifier fixed later
"in highp int gl_InstanceID;" // needs qualifier fixed later
......@@ -7436,6 +7473,12 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion
SpecialQualifier("gl_InstanceID", EvqInstanceId, EbvInstanceId, symbolTable);
}
if (spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) {
// treat these built-ins as aliases of VertexIndex and InstanceIndex
BuiltInVariable("gl_VertexID", EbvVertexIndex, symbolTable);
BuiltInVariable("gl_InstanceID", EbvInstanceIndex, symbolTable);
}
if (profile != EEsProfile) {
if (version >= 440) {
symbolTable.setVariableExtensions("gl_BaseVertexARB", 1, &E_GL_ARB_shader_draw_parameters);
......@@ -8912,6 +8955,14 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion
symbolTable.relateToOperator("memoryBarrierAtomicCounter", EOpMemoryBarrierAtomicCounter);
symbolTable.relateToOperator("memoryBarrierImage", EOpMemoryBarrierImage);
if (spvVersion.vulkanRelaxed) {
//
// functions signature have been replaced to take uint operations on buffer variables
// remap atomic counter functions to atomic operations
//
symbolTable.relateToOperator("memoryBarrierAtomicCounter", EOpMemoryBarrierBuffer);
}
symbolTable.relateToOperator("atomicLoad", EOpAtomicLoad);
symbolTable.relateToOperator("atomicStore", EOpAtomicStore);
......@@ -8919,6 +8970,20 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion
symbolTable.relateToOperator("atomicCounterDecrement", EOpAtomicCounterDecrement);
symbolTable.relateToOperator("atomicCounter", EOpAtomicCounter);
if (spvVersion.vulkanRelaxed) {
//
// functions signature have been replaced to take uint operations
// remap atomic counter functions to atomic operations
//
// these atomic counter functions do not match signatures of glsl
// atomic functions, so they will be remapped to semantically
// equivalent functions in the parser
//
symbolTable.relateToOperator("atomicCounterIncrement", EOpNull);
symbolTable.relateToOperator("atomicCounterDecrement", EOpNull);
symbolTable.relateToOperator("atomicCounter", EOpNull);
}
symbolTable.relateToOperator("clockARB", EOpReadClockSubgroupKHR);
symbolTable.relateToOperator("clock2x32ARB", EOpReadClockSubgroupKHR);
......@@ -8937,6 +9002,23 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion
symbolTable.relateToOperator("atomicCounterCompSwap", EOpAtomicCounterCompSwap);
}
if (spvVersion.vulkanRelaxed) {
//
// functions signature have been replaced to take 'uint' instead of 'atomic_uint'
// remap atomic counter functions to non-counter atomic ops so
// functions act as aliases to non-counter atomic ops
//
symbolTable.relateToOperator("atomicCounterAdd", EOpAtomicAdd);
symbolTable.relateToOperator("atomicCounterSubtract", EOpAtomicSubtract);
symbolTable.relateToOperator("atomicCounterMin", EOpAtomicMin);
symbolTable.relateToOperator("atomicCounterMax", EOpAtomicMax);
symbolTable.relateToOperator("atomicCounterAnd", EOpAtomicAnd);
symbolTable.relateToOperator("atomicCounterOr", EOpAtomicOr);
symbolTable.relateToOperator("atomicCounterXor", EOpAtomicXor);
symbolTable.relateToOperator("atomicCounterExchange", EOpAtomicExchange);
symbolTable.relateToOperator("atomicCounterCompSwap", EOpAtomicCompSwap);
}
symbolTable.relateToOperator("fma", EOpFma);
symbolTable.relateToOperator("frexp", EOpFrexp);
symbolTable.relateToOperator("ldexp", EOpLdexp);
......
......@@ -601,7 +601,6 @@ void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TStrin
selector.push_back(0);
}
#ifdef ENABLE_HLSL
//
// Make the passed-in variable information become a member of the
// global uniform block. If this doesn't exist yet, make it.
......@@ -646,7 +645,67 @@ void TParseContextBase::growGlobalUniformBlock(const TSourceLoc& loc, TType& mem
++firstNewMember;
}
#endif
void TParseContextBase::growAtomicCounterBlock(int binding, const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList) {
// Make the atomic counter block, if not yet made.
const auto &at = atomicCounterBuffers.find(binding);
if (at == atomicCounterBuffers.end()) {
atomicCounterBuffers.insert({binding, (TVariable*)nullptr });
atomicCounterBlockFirstNewMember.insert({binding, 0});
}
TVariable*& atomicCounterBuffer = atomicCounterBuffers[binding];
int& bufferNewMember = atomicCounterBlockFirstNewMember[binding];
if (atomicCounterBuffer == nullptr) {
TQualifier blockQualifier;
blockQualifier.clear();
blockQualifier.storage = EvqBuffer;
char charBuffer[512];
if (binding != TQualifier::layoutBindingEnd) {
snprintf(charBuffer, 512, "%s_%d", getAtomicCounterBlockName(), binding);
} else {
snprintf(charBuffer, 512, "%s_0", getAtomicCounterBlockName());
}
TType blockType(new TTypeList, *NewPoolTString(charBuffer), blockQualifier);
setUniformBlockDefaults(blockType);
blockType.getQualifier().layoutPacking = ElpStd430;
atomicCounterBuffer = new TVariable(NewPoolTString(""), blockType, true);
// If we arn't auto mapping bindings then set the block to use the same
// binding as what the atomic was set to use
if (!intermediate.getAutoMapBindings()) {
atomicCounterBuffer->getWritableType().getQualifier().layoutBinding = binding;
}
bufferNewMember = 0;
atomicCounterBuffer->getWritableType().getQualifier().layoutSet = atomicCounterBlockSet;
}
// Add the requested member as a member to the global block.
TType* type = new TType;
type->shallowCopy(memberType);
type->setFieldName(memberName);
if (typeList)
type->setStruct(typeList);
TTypeLoc typeLoc = {type, loc};
atomicCounterBuffer->getType().getWritableStruct()->push_back(typeLoc);
// Insert into the symbol table.
if (bufferNewMember == 0) {
// This is the first request; we need a normal symbol table insert
if (symbolTable.insert(*atomicCounterBuffer))
trackLinkage(*atomicCounterBuffer);
else
error(loc, "failed to insert the global constant buffer", "buffer", "");
} else {
// This is a follow-on request; we need to amend the first insert
symbolTable.amend(*atomicCounterBuffer, bufferNewMember);
}
++bufferNewMember;
}
void TParseContextBase::finish()
{
......
......@@ -92,7 +92,8 @@ public:
limits(resources.limits),
globalUniformBlock(nullptr),
globalUniformBinding(TQualifier::layoutBindingEnd),
globalUniformSet(TQualifier::layoutSetEnd)
globalUniformSet(TQualifier::layoutSetEnd),
atomicCounterBlockSet(TQualifier::layoutSetEnd)
{
if (entryPoint != nullptr)
sourceEntryPointName = *entryPoint;
......@@ -154,10 +155,11 @@ public:
extensionCallback(line, extension, behavior);
}
#ifdef ENABLE_HLSL
// Manage the global uniform block (default uniforms in GLSL, $Global in HLSL)
virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr);
#endif
// Manage global buffer (used for backing atomic counters in GLSL when using relaxed Vulkan semantics)
virtual void growAtomicCounterBlock(int binding, const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr);
// Potentially rename shader entry point function
void renameShaderFunction(TString*& name) const
......@@ -230,7 +232,24 @@ protected:
// override this to set the language-specific name
virtual const char* getGlobalUniformBlockName() const { return ""; }
virtual void setUniformBlockDefaults(TType&) const { }
virtual void finalizeGlobalUniformBlockLayout(TVariable&) { }
virtual void finalizeGlobalUniformBlockLayout(TVariable&) {}
// Manage the atomic counter block (used for atomic_uints with Vulkan-Relaxed)
TMap<int, TVariable*> atomicCounterBuffers;
unsigned int atomicCounterBlockSet;
TMap<int, int> atomicCounterBlockFirstNewMember;
// override this to set the language-specific name
virtual const char* getAtomicCounterBlockName() const { return ""; }
virtual void setAtomicCounterBlockDefaults(TType&) const {}
virtual void finalizeAtomicCounterBlockLayout(TVariable&) {}
bool isAtomicCounterBlock(const TSymbol& symbol) {
const TVariable* var = symbol.getAsVariable();
if (!var)
return false;
const auto& at = atomicCounterBuffers.find(var->getType().getQualifier().layoutBinding);
return (at != atomicCounterBuffers.end() && (*at).second->getType() == var->getType());
}
virtual void outputMessage(const TSourceLoc&, const char* szReason, const char* szToken,
const char* szExtraInfoFormat, TPrefixType prefix,
va_list args);
......@@ -293,6 +312,9 @@ public:
bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) override;
void parserError(const char* s); // for bison's yyerror
virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr) override;
virtual void growAtomicCounterBlock(int binding, const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr) override;
void reservedErrorCheck(const TSourceLoc&, const TString&);
void reservedPpErrorCheck(const TSourceLoc&, const char* name, const char* op) override;
bool lineContinuationCheck(const TSourceLoc&, bool endOfComment) override;
......@@ -340,6 +362,10 @@ public:
void checkPrecisionQualifier(const TSourceLoc&, TPrecisionQualifier);
void memorySemanticsCheck(const TSourceLoc&, const TFunction&, const TIntermOperator& callNode);
TIntermTyped* vkRelaxedRemapFunctionCall(const TSourceLoc&, TFunction*, TIntermNode*);
// returns true if the variable was remapped to something else
bool vkRelaxedRemapUniformVariable(const TSourceLoc&, TString&, const TPublicType&, TArraySizes*, TIntermTyped*, TType&);
void assignError(const TSourceLoc&, const char* op, TString left, TString right);
void unaryOpError(const TSourceLoc&, const char* op, TString operand);
void binaryOpError(const TSourceLoc&, const char* op, TString left, TString right);
......@@ -417,6 +443,7 @@ public:
TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset);
void inheritMemoryQualifiers(const TQualifier& from, TQualifier& to);
void declareBlock(const TSourceLoc&, TTypeList& typeList, const TString* instanceName = 0, TArraySizes* arraySizes = 0);
void blockStorageRemap(const TSourceLoc&, const TString*, TQualifier&);
void blockStageIoCheck(const TSourceLoc&, const TQualifier&);
void blockQualifierCheck(const TSourceLoc&, const TQualifier&, bool instanceName);
void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation);
......@@ -461,6 +488,14 @@ protected:
void finish() override;
#endif
virtual const char* getGlobalUniformBlockName() const override;
virtual void finalizeGlobalUniformBlockLayout(TVariable&) override;
virtual void setUniformBlockDefaults(TType& block) const override;
virtual const char* getAtomicCounterBlockName() const override;
virtual void finalizeAtomicCounterBlockLayout(TVariable&) override;
virtual void setAtomicCounterBlockDefaults(TType& block) const override;
public:
//
// Generally, bison productions, the scanner, and the PP need read/write access to these; just give them direct access
......
......@@ -159,7 +159,7 @@ int MapVersionToIndex(int version)
return index;
}
const int SpvVersionCount = 3; // index range in MapSpvVersionToIndex
const int SpvVersionCount = 4; // index range in MapSpvVersionToIndex
int MapSpvVersionToIndex(const SpvVersion& spvVersion)
{
......@@ -167,8 +167,12 @@ int MapSpvVersionToIndex(const SpvVersion& spvVersion)
if (spvVersion.openGl > 0)
index = 1;
else if (spvVersion.vulkan > 0)
index = 2;
else if (spvVersion.vulkan > 0) {
if (!spvVersion.vulkanRelaxed)
index = 2;
else
index = 3;
}
assert(index < SpvVersionCount);
......@@ -723,6 +727,7 @@ void TranslateEnvironment(const TEnvironment* environment, EShMessages& messages
break;
case EShClientVulkan:
spvVersion.vulkanGlsl = environment->input.dialectVersion;
spvVersion.vulkanRelaxed = environment->input.VulkanRulesRelaxed;
break;
case EShClientOpenGL:
spvVersion.openGl = environment->input.dialectVersion;
......@@ -1868,6 +1873,15 @@ void TShader::setResourceSetBinding(const std::vector<std::string>& base) { in
void TShader::setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { intermediate->setTextureSamplerTransformMode(mode); }
#endif
void TShader::addBlockStorageOverride(const char* nameStr, TBlockStorageClass backing) { intermediate->addBlockStorageOverride(nameStr, backing); }
void TShader::setGlobalUniformBlockName(const char* name) { intermediate->setGlobalUniformBlockName(name); }
void TShader::setGlobalUniformSet(unsigned int set) { intermediate->setGlobalUniformSet(set); }
void TShader::setGlobalUniformBinding(unsigned int binding) { intermediate->setGlobalUniformBinding(binding); }
void TShader::setAtomicCounterBlockName(const char* name) { intermediate->setAtomicCounterBlockName(name); }
void TShader::setAtomicCounterBlockSet(unsigned int set) { intermediate->setAtomicCounterBlockSet(set); }
#ifdef ENABLE_HLSL
// See comment above TDefaultHlslIoMapper in iomapper.cpp:
void TShader::setHlslIoMapping(bool hlslIoMap) { intermediate->setHlslIoMapping(hlslIoMap); }
......@@ -1983,7 +1997,10 @@ bool TProgram::link(EShMessages messages)
error = true;
}
// TODO: Link: cross-stage error checking
if (!error) {
if (! crossStageCheck(messages))
error = true;
}
return ! error;
}
......@@ -2060,6 +2077,64 @@ bool TProgram::linkStage(EShLanguage stage, EShMessages messages)
return intermediate[stage]->getNumErrors() == 0;
}
//
// Check that there are no errors in linker objects accross stages
//
// Return true if no errors.
//
bool TProgram::crossStageCheck(EShMessages) {
// make temporary intermediates to hold the linkage symbols for each linking interface
// while we do the checks
// Independent interfaces are:
// all uniform variables and blocks
// all buffer blocks
// all in/out on a stage boundary
TVector<TIntermediate*> activeStages;
for (int s = 0; s < EShLangCount; ++s) {
if (intermediate[s])
activeStages.push_back(intermediate[s]);
}
// no extra linking if there is only one stage
if (! (activeStages.size() > 1))
return true;
// setup temporary tree to hold unfirom objects from different stages
TIntermediate* firstIntermediate = activeStages.front();
TIntermediate uniforms(EShLangCount,
firstIntermediate->getVersion(),
firstIntermediate->getProfile());
uniforms.setSpv(firstIntermediate->getSpv());
TIntermAggregate uniformObjects(EOpLinkerObjects);
TIntermAggregate root(EOpSequence);
root.getSequence().push_back(&uniformObjects);
uniforms.setTreeRoot(&root);
bool error = false;
// merge uniforms from all stages into a single intermediate
for (unsigned int i = 0; i < activeStages.size(); ++i) {
uniforms.mergeUniformObjects(*infoSink, *activeStages[i]);
}
error |= uniforms.getNumErrors() != 0;
// copy final definition of global block back into each stage
for (unsigned int i = 0; i < activeStages.size(); ++i) {
activeStages[i]->mergeGlobalUniformBlocks(*infoSink, uniforms);
}
// compare cross stage symbols for each stage boundary
for (unsigned int i = 1; i < activeStages.size(); ++i) {
activeStages[i - 1]->checkStageIO(*infoSink, *activeStages[i]);
error |= (activeStages[i - 1]->getNumErrors() != 0);
}
return !error;
}
const char* TProgram::getInfoLog()
{
return infoSink->info.c_str();
......
......@@ -1273,7 +1273,7 @@ void TParseVersions::spvRemoved(const TSourceLoc& loc, const char* op)
// Call for any operation removed because Vulkan SPIR-V is being generated.
void TParseVersions::vulkanRemoved(const TSourceLoc& loc, const char* op)
{
if (spvVersion.vulkan > 0)
if (spvVersion.vulkan > 0 && !spvVersion.vulkanRelaxed)
error(loc, "not allowed when using GLSL for Vulkan", op, "");
}
......
......@@ -87,11 +87,12 @@ inline const char* ProfileName(EProfile profile)
// The union of all requested rule sets will be applied.
//
struct SpvVersion {
SpvVersion() : spv(0), vulkanGlsl(0), vulkan(0), openGl(0) {}
SpvVersion() : spv(0), vulkanGlsl(0), vulkan(0), openGl(0), vulkanRelaxed(false) {}
unsigned int spv; // the version of SPIR-V to target, as defined by "word 1" of the SPIR-V binary header
int vulkanGlsl; // the version of GLSL semantics for Vulkan, from GL_KHR_vulkan_glsl, for "#define VULKAN XXX"
int vulkan; // the version of Vulkan, for which SPIR-V execution environment rules to use
int openGl; // the version of GLSL semantics for OpenGL, from GL_ARB_gl_spirv, for "#define GL_SPIRV XXX"
bool vulkanRelaxed; // relax changes to GLSL for Vulkan, allowing some GL-specific to be compiled to Vulkan SPIR-V target
};
//
......
......@@ -886,6 +886,7 @@ bool TOutputTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node
case EOpTime: out.debug << "time"; break;
case EOpAtomicAdd: out.debug << "AtomicAdd"; break;
case EOpAtomicSubtract: out.debug << "AtomicSubtract"; break;
case EOpAtomicMin: out.debug << "AtomicMin"; break;
case EOpAtomicMax: out.debug << "AtomicMax"; break;
case EOpAtomicAnd: out.debug << "AtomicAnd"; break;
......
......@@ -87,6 +87,35 @@ struct TVarEntryInfo {
return lPoints > rPoints;
}
};
struct TOrderByPriorityAndLive {
// ordering:
// 1) do live variables first
// 2) has both binding and set
// 3) has binding but no set
// 4) has no binding but set
// 5) has no binding and no set
inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) {
const TQualifier& lq = l.symbol->getQualifier();
const TQualifier& rq = r.symbol->getQualifier();
// simple rules:
// has binding gives 2 points
// has set gives 1 point
// who has the most points is more important.
int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0);
int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0);
if (l.live != r.live)
return l.live > r.live;
if (lPoints != rPoints)
return lPoints > rPoints;
return l.id < r.id;
}
};
};
// Base class for shared TIoMapResolver services, used by several derivations.
......@@ -107,8 +136,8 @@ public:
void endCollect(EShLanguage) override {}
void reserverResourceSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {}
void reserverStorageSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {}
int getBaseBinding(TResourceType res, unsigned int set) const;
const std::vector<std::string>& getResourceSetBinding() const;
int getBaseBinding(EShLanguage stage, TResourceType res, unsigned int set) const;
const std::vector<std::string>& getResourceSetBinding(EShLanguage stage) const;
virtual TResourceType getResourceType(const glslang::TType& type) = 0;
bool doAutoBindingMapping() const;
bool doAutoLocationMapping() const;
......@@ -122,9 +151,11 @@ public:
int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) override;
int resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) override;
int resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) override;
void addStage(EShLanguage stage) override {
if (stage < EShLangCount)
void addStage(EShLanguage stage, TIntermediate& stageIntermediate) override {
if (stage < EShLangCount) {
stageMask[stage] = true;
stageIntermediates[stage] = &stageIntermediate;
}
}
uint32_t computeTypeLocationSize(const TType& type, EShLanguage stage);
......@@ -139,6 +170,8 @@ protected:
int nextInputLocation;
int nextOutputLocation;
bool stageMask[EShLangCount + 1];
const TIntermediate* stageIntermediates[EShLangCount];
// Return descriptor set specific base if there is one, and the generic base otherwise.
int selectBaseBinding(int base, int descriptorSetBase) const {
return descriptorSetBase != -1 ? descriptorSetBase : base;
......
......@@ -293,7 +293,12 @@ public:
useStorageBuffer(false),
nanMinMaxClamp(false),
depthReplacing(false),
uniqueId(0)
uniqueId(0),
globalUniformBlockName(""),
atomicCounterBlockName(""),
globalUniformBlockSet(TQualifier::layoutSetEnd),
globalUniformBlockBinding(TQualifier::layoutBindingEnd),
atomicCounterBlockSet(TQualifier::layoutSetEnd)
#ifndef GLSLANG_WEB
,
implicitThisName("@this"), implicitCounterName("@count"),
......@@ -537,6 +542,19 @@ public:
void addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol&);
TIntermAggregate* findLinkerObjects() const;
void setGlobalUniformBlockName(const char* name) { globalUniformBlockName = std::string(name); }
const char* getGlobalUniformBlockName() const { return globalUniformBlockName.c_str(); }
void setGlobalUniformSet(unsigned int set) { globalUniformBlockSet = set; }
unsigned int getGlobalUniformSet() const { return globalUniformBlockSet; }
void setGlobalUniformBinding(unsigned int binding) { globalUniformBlockBinding = binding; }
unsigned int getGlobalUniformBinding() const { return globalUniformBlockBinding; }
void setAtomicCounterBlockName(const char* name) { atomicCounterBlockName = std::string(name); }
const char* getAtomicCounterBlockName() const { return atomicCounterBlockName.c_str(); }
void setAtomicCounterBlockSet(unsigned int set) { atomicCounterBlockSet = set; }
unsigned int getAtomicCounterBlockSet() const { return atomicCounterBlockSet; }
void setUseStorageBuffer() { useStorageBuffer = true; }
bool usingStorageBuffer() const { return useStorageBuffer; }
void setDepthReplacing() { depthReplacing = true; }
......@@ -848,6 +866,20 @@ public:
bool getBinaryDoubleOutput() { return binaryDoubleOutput; }
#endif // GLSLANG_WEB
void addBlockStorageOverride(const char* nameStr, TBlockStorageClass backing)
{
std::string name(nameStr);
blockBackingOverrides[name] = backing;
}
TBlockStorageClass getBlockStorageOverride(const char* nameStr) const
{
std::string name = nameStr;
auto pos = blockBackingOverrides.find(name);
if (pos == blockBackingOverrides.end())
return EbsNone;
else
return pos->second;
}
#ifdef ENABLE_HLSL
void setHlslFunctionality1() { hlslFunctionality1 = true; }
bool getHlslFunctionality1() const { return hlslFunctionality1; }
......@@ -883,6 +915,10 @@ public:
void merge(TInfoSink&, TIntermediate&);
void finalCheck(TInfoSink&, bool keepUncalled);
void mergeGlobalUniformBlocks(TInfoSink& infoSink, TIntermediate& unit);
void mergeUniformObjects(TInfoSink& infoSink, TIntermediate& unit);
void checkStageIO(TInfoSink&, TIntermediate&);
bool buildConvertOp(TBasicType dst, TBasicType src, TOperator& convertOp) const;
TIntermTyped* createConversion(TBasicType convertTo, TIntermTyped* node) const;
......@@ -906,6 +942,8 @@ public:
static int getOffset(const TType& type, int index);
static int getBlockSize(const TType& blockType);
static int computeBufferReferenceTypeSize(const TType&);
static bool isIoResizeArray(const TType& type, EShLanguage language);
bool promote(TIntermOperator*);
void setNanMinMaxClamp(bool setting) { nanMinMaxClamp = setting; }
bool getNanMinMaxClamp() const { return nanMinMaxClamp; }
......@@ -963,9 +1001,10 @@ protected:
void seedIdMap(TIdMaps& idMaps, long long& IdShift);
void remapIds(const TIdMaps& idMaps, long long idShift, TIntermediate&);
void mergeBodies(TInfoSink&, TIntermSequence& globals, const TIntermSequence& unitGlobals);
void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects);
void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects, EShLanguage);
void mergeBlockDefinitions(TInfoSink&, TIntermSymbol* block, TIntermSymbol* unitBlock, TIntermediate* unitRoot);
void mergeImplicitArraySizes(TType&, const TType&);
void mergeErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&, bool crossStage);
void mergeErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&, EShLanguage);
void checkCallGraphCycles(TInfoSink&);
void checkCallGraphBodies(TInfoSink&, bool keepUncalled);
void inOutLocationCheck(TInfoSink&);
......@@ -1015,6 +1054,13 @@ protected:
bool localSizeNotDefault[3];
int localSizeSpecId[3];
unsigned long long uniqueId;
std::string globalUniformBlockName;
std::string atomicCounterBlockName;
unsigned int globalUniformBlockSet;
unsigned int globalUniformBlockBinding;
unsigned int atomicCounterBlockSet;
#ifndef GLSLANG_WEB
public:
const char* const implicitThisName;
......@@ -1075,6 +1121,7 @@ protected:
int uniformLocationBase;
TNumericFeatures numericFeatures;
#endif
std::unordered_map<std::string, TBlockStorageClass> blockBackingOverrides;
std::unordered_set<int> usedConstantId; // specialization constant ids used
std::vector<TOffsetRange> usedAtomics; // sets of bindings used by atomic counters
......
......@@ -187,6 +187,7 @@ struct TInputLanguage {
EShLanguage stage; // redundant information with other input, this one overrides when not EShSourceNone
EShClient dialect;
int dialectVersion; // version of client's language definition, not the client (when not EShClientNone)
bool VulkanRulesRelaxed = false;
};
struct TClient {
......@@ -427,6 +428,14 @@ enum TResourceType {
EResCount
};
enum TBlockStorageClass
{
EbsUniform = 0,
EbsStorageBuffer,
EbsPushConstant,
EbsNone, // not a uniform or buffer variable
EbsCount,
};
// Make one TShader per shader that you will link into a program. Then
// - provide the shader through setStrings() or setStringsWithLengths()
......@@ -483,6 +492,14 @@ public:
GLSLANG_EXPORT void setNoStorageFormat(bool useUnknownFormat);
GLSLANG_EXPORT void setNanMinMaxClamp(bool nanMinMaxClamp);
GLSLANG_EXPORT void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode);
GLSLANG_EXPORT void addBlockStorageOverride(const char* nameStr, glslang::TBlockStorageClass backing);
GLSLANG_EXPORT void setGlobalUniformBlockName(const char* name);
GLSLANG_EXPORT void setAtomicCounterBlockName(const char* name);
GLSLANG_EXPORT void setGlobalUniformSet(unsigned int set);
GLSLANG_EXPORT void setGlobalUniformBinding(unsigned int binding);
GLSLANG_EXPORT void setAtomicCounterBlockSet(unsigned int set);
GLSLANG_EXPORT void setAtomicCounterBlockBinding(unsigned int binding);
// For setting up the environment (cleared to nothingness in the constructor).
// These must be called so that parsing is done for the right source language and
......@@ -539,6 +556,9 @@ public:
bool getEnvTargetHlslFunctionality1() const { return false; }
#endif
void setEnvInputVulkanRulesRelaxed() { environment.input.VulkanRulesRelaxed = true; }
bool getEnvInputVulkanRulesRelaxed() const { return environment.input.VulkanRulesRelaxed; }
// Interface to #include handlers.
//
// To support #include, a client of Glslang does the following:
......@@ -806,7 +826,7 @@ public:
// Called by TSlotCollector to resolve resource locations or bindings
virtual void reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) = 0;
// Called by mapIO.addStage to set shader stage mask to mark a stage be added to this pipeline
virtual void addStage(EShLanguage stage) = 0;
virtual void addStage(EShLanguage stage, TIntermediate& stageIntermediate) = 0;
};
#endif // !GLSLANG_WEB && !GLSLANG_ANGLE
......@@ -928,6 +948,7 @@ public:
protected:
GLSLANG_EXPORT bool linkStage(EShLanguage, EShMessages);
GLSLANG_EXPORT bool crossStageCheck(EShMessages);
TPoolAllocator* pool;
std::list<TShader*> stages[EShLangCount];
......
......@@ -53,7 +53,9 @@ if(BUILD_TESTING)
${CMAKE_CURRENT_SOURCE_DIR}/Link.FromFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Link.FromFile.Vk.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Pp.FromFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Spv.FromFile.cpp)
${CMAKE_CURRENT_SOURCE_DIR}/Spv.FromFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/VkRelaxed.FromFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/GlslMapIO.FromFile.cpp)
if(ENABLE_SPVREMAPPER)
set(TEST_SOURCES ${TEST_SOURCES}
......
......@@ -114,12 +114,12 @@ INSTANTIATE_TEST_SUITE_P(
::testing::ValuesIn(std::vector<std::vector<std::string>>({
{"link1.vk.frag", "link2.vk.frag"},
{"spv.unit1.frag", "spv.unit2.frag", "spv.unit3.frag"},
{"link.vk.matchingPC.0.0.frag", "link.vk.matchingPC.0.1.frag",
"link.vk.matchingPC.0.2.frag"},
{"link.vk.differentPC.0.0.frag", "link.vk.differentPC.0.1.frag",
"link.vk.differentPC.0.2.frag"},
{"link.vk.differentPC.1.0.frag", "link.vk.differentPC.1.1.frag",
"link.vk.differentPC.1.2.frag"},
{"link.vk.matchingPC.0.0.frag", "link.vk.matchingPC.0.1.frag",
"link.vk.matchingPC.0.2.frag"},
{"link.vk.differentPC.0.0.frag", "link.vk.differentPC.0.1.frag",
"link.vk.differentPC.0.2.frag"},
{"link.vk.differentPC.1.0.frag", "link.vk.differentPC.1.1.frag",
"link.vk.differentPC.1.2.frag"},
{"link.vk.pcNamingValid.0.0.vert", "link.vk.pcNamingValid.0.1.vert"},
{"link.vk.pcNamingInvalid.0.0.vert", "link.vk.pcNamingInvalid.0.1.vert"},
{"link.vk.multiBlocksValid.0.0.vert", "link.vk.multiBlocksValid.0.1.vert"},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment