Add support for Vulkan GLSL as output target.

Mostly useful for debugging SPIR-V where it is useful to see the
"original" Vulkan GLSL constructs which created the SPIR-V.
This commit is contained in:
Hans-Kristian Arntzen 2016-05-05 10:16:22 +02:00
parent 5af1a51727
commit dbee4e4346
15 changed files with 247 additions and 12 deletions

View File

@ -268,11 +268,12 @@ struct CLIArguments
uint32_t iterations = 1;
bool cpp = false;
bool metal = false;
bool vulkan_semantics = false;
};
static void print_help()
{
fprintf(stderr, "Usage: spirv-cross [--output <output path>] [SPIR-V file] [--es] [--no-es] [--version <GLSL version>] [--dump-resources] [--help] [--force-temporary] [--cpp] [--metal] [--flatten-ubo] [--fixup-clipspace] [--iterations iter] [--pls-in format input-name] [--pls-out format output-name]\n");
fprintf(stderr, "Usage: spirv-cross [--output <output path>] [SPIR-V file] [--es] [--no-es] [--version <GLSL version>] [--dump-resources] [--help] [--force-temporary] [--cpp] [--metal] [--vulkan-semantics] [--flatten-ubo] [--fixup-clipspace] [--iterations iter] [--pls-in format input-name] [--pls-out format output-name]\n");
}
static vector<PlsRemap> remap_pls(const vector<PLSArg> &pls_variables, const vector<Resource> &resources, const vector<Resource> *secondary_resources)
@ -347,6 +348,7 @@ int main(int argc, char *argv[])
cbs.add("--iterations", [&args](CLIParser &parser) { args.iterations = parser.next_uint(); });
cbs.add("--cpp", [&args](CLIParser &) { args.cpp = true; });
cbs.add("--metal", [&args](CLIParser &) { args.metal = true; });
cbs.add("--vulkan-semantics",[&args](CLIParser &) { args.vulkan_semantics = true; });
cbs.add("--pls-in", [&args](CLIParser &parser) {
auto fmt = pls_format(parser.next_string());
@ -401,6 +403,7 @@ int main(int argc, char *argv[])
if (args.set_es)
opts.es = args.es;
opts.force_temporary = args.force_temporary;
opts.vulkan_semantics = args.vulkan_semantics;
opts.vertex.fixup_clipspace = args.fixup;
compiler->set_options(opts);

View File

@ -0,0 +1,14 @@
#version 310 es
precision mediump float;
precision highp int;
layout(input_attachment_index = 0, binding = 0) uniform mediump subpassInput uSubpass0;
layout(input_attachment_index = 1, binding = 1) uniform mediump subpassInput uSubpass1;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = (subpassLoad(uSubpass0) + subpassLoad(uSubpass1));
}

View File

@ -0,0 +1,20 @@
#version 310 es
precision mediump float;
precision highp int;
struct PushConstants
{
vec4 value0;
vec4 value1;
};
uniform PushConstants push;
layout(location = 0) out vec4 FragColor;
layout(location = 0) in vec4 vColor;
void main()
{
FragColor = ((vColor + push.value0) + push.value1);
}

View File

@ -0,0 +1,18 @@
#version 310 es
precision mediump float;
precision highp int;
layout(push_constant, std430) uniform PushConstants
{
vec4 value0;
vec4 value1;
} push;
layout(location = 0) out vec4 FragColor;
layout(location = 0) in vec4 vColor;
void main()
{
FragColor = ((vColor + push.value0) + push.value1);
}

View File

@ -0,0 +1,38 @@
#version 310 es
precision mediump float;
precision highp int;
layout(binding = 1) uniform mediump texture2D uTexture;
layout(binding = 0) uniform mediump sampler uSampler;
layout(binding = 4) uniform mediump texture2DArray uTextureArray;
layout(binding = 3) uniform mediump textureCube uTextureCube;
layout(binding = 2) uniform mediump texture3D uTexture3D;
layout(location = 0) in vec2 vTex;
layout(location = 1) in vec3 vTex3;
layout(location = 0) out vec4 FragColor;
vec4 sample_func(mediump sampler samp, vec2 uv)
{
return texture(sampler2D(uTexture, samp), uv);
}
vec4 sample_func_dual(mediump sampler samp, mediump texture2D tex, vec2 uv)
{
return texture(sampler2D(tex, samp), uv);
}
void main()
{
vec2 off = (vec2(1.0) / vec2(textureSize(uTexture, 0)));
vec2 off2 = (vec2(1.0) / vec2(textureSize(sampler2D(uTexture, uSampler), 1)));
highp vec2 param = ((vTex + off) + off2);
vec4 c0 = sample_func(uSampler, param);
highp vec2 param_1 = ((vTex + off) + off2);
vec4 c1 = sample_func_dual(uSampler, uTexture, param_1);
vec4 c2 = texture(sampler2DArray(uTextureArray, uSampler), vTex3);
vec4 c3 = texture(samplerCube(uTextureCube, uSampler), vTex3);
vec4 c4 = texture(sampler3D(uTexture3D, uSampler), vTex3);
FragColor = ((((c0 + c1) + c2) + c3) + c4);
}

View File

@ -0,0 +1,38 @@
#version 310 es
precision mediump float;
precision highp int;
layout(binding = 1) uniform mediump texture2D uTexture;
layout(binding = 0) uniform mediump sampler uSampler;
layout(binding = 4) uniform mediump texture2DArray uTextureArray;
layout(binding = 3) uniform mediump textureCube uTextureCube;
layout(binding = 2) uniform mediump texture3D uTexture3D;
layout(location = 0) in vec2 vTex;
layout(location = 1) in vec3 vTex3;
layout(location = 0) out vec4 FragColor;
vec4 sample_func(mediump sampler samp, vec2 uv)
{
return texture(sampler2D(uTexture, samp), uv);
}
vec4 sample_func_dual(mediump sampler samp, mediump texture2D tex, vec2 uv)
{
return texture(sampler2D(tex, samp), uv);
}
void main()
{
vec2 off = (vec2(1.0) / vec2(textureSize(uTexture, 0)));
vec2 off2 = (vec2(1.0) / vec2(textureSize(sampler2D(uTexture, uSampler), 1)));
highp vec2 param = ((vTex + off) + off2);
vec4 c0 = sample_func(uSampler, param);
highp vec2 param_1 = ((vTex + off) + off2);
vec4 c1 = sample_func_dual(uSampler, uTexture, param_1);
vec4 c2 = texture(sampler2DArray(uTextureArray, uSampler), vTex3);
vec4 c3 = texture(samplerCube(uTextureCube, uSampler), vTex3);
vec4 c4 = texture(sampler3D(uTexture3D, uSampler), vTex3);
FragColor = ((((c0 + c1) + c2) + c3) + c4);
}

View File

@ -0,0 +1,9 @@
#version 310 es
uniform int SPIRV_Cross_BaseInstance;
void main()
{
gl_Position = (vec4(1.0, 2.0, 3.0, 4.0) * float((gl_VertexID + (gl_InstanceID + SPIRV_Cross_BaseInstance))));
}

View File

@ -0,0 +1,7 @@
#version 310 es
void main()
{
gl_Position = (vec4(1.0, 2.0, 3.0, 4.0) * float((gl_VertexIndex + gl_InstanceIndex)));
}

View File

@ -0,0 +1,16 @@
#version 310 es
precision mediump float;
layout(push_constant, std430) uniform PushConstants
{
vec4 value0;
vec4 value1;
} push;
layout(location = 0) in vec4 vColor;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = vColor + push.value0 + push.value1;
}

View File

@ -0,0 +1,6 @@
#version 310 es
void main()
{
gl_Position = float(gl_VertexIndex + gl_InstanceIndex) * vec4(1.0, 2.0, 3.0, 4.0);
}

View File

@ -673,10 +673,20 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var)
auto flags = dec.decoration_flags;
auto typeflags = meta[type.self].decoration.decoration_flags;
if (options.vulkan_semantics && var.storage == StorageClassPushConstant)
attr.push_back("push_constant");
if (flags & (1ull << DecorationRowMajor))
attr.push_back("row_major");
if (flags & (1ull << DecorationColMajor))
attr.push_back("column_major");
if (options.vulkan_semantics)
{
if (flags & (1ull << DecorationInputAttachmentIndex))
attr.push_back(join("input_attachment_index = ", dec.input_attachment));
}
if (flags & (1ull << DecorationLocation))
attr.push_back(join("location = ", dec.location));
if ((flags & (1ull << DecorationDescriptorSet)) && dec.set != 0) // set = 0 is the default.
@ -692,8 +702,9 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var)
// If SPIR-V does not comply with either layout, we cannot really work around it.
if (var.storage == StorageClassUniform && (typeflags & (1ull << DecorationBlock)))
attr.push_back("std140");
if (var.storage == StorageClassUniform && (typeflags & (1ull << DecorationBufferBlock)))
else if (var.storage == StorageClassUniform && (typeflags & (1ull << DecorationBufferBlock)))
attr.push_back(ssbo_is_std430_packing(type) ? "std430" : "std140");
else if (options.vulkan_semantics && var.storage == StorageClassPushConstant)
attr.push_back(ssbo_is_std430_packing(type) ? "std430" : "std140");
// For images, the type itself adds a layout qualifer.
@ -714,6 +725,19 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var)
}
void CompilerGLSL::emit_push_constant_block(const SPIRVariable &var)
{
if (options.vulkan_semantics)
emit_push_constant_block_vulkan(var);
else
emit_push_constant_block_glsl(var);
}
void CompilerGLSL::emit_push_constant_block_vulkan(const SPIRVariable &var)
{
emit_buffer_block(var);
}
void CompilerGLSL::emit_push_constant_block_glsl(const SPIRVariable &var)
{
// OpenGL has no concept of push constant blocks, implement it as a uniform struct.
auto &type = get<SPIRType>(var.basetype);
@ -988,7 +1012,7 @@ void CompilerGLSL::emit_resources()
{
// For gl_InstanceIndex emulation on GLES, the API user needs to
// supply this uniform.
if (meta[var.self].decoration.builtin_type == BuiltInInstanceIndex)
if (meta[var.self].decoration.builtin_type == BuiltInInstanceIndex && !options.vulkan_semantics)
{
statement("uniform int SPIRV_Cross_BaseInstance;");
emitted = true;
@ -1942,13 +1966,25 @@ string CompilerGLSL::builtin_to_glsl(BuiltIn builtin)
case BuiltInPointSize:
return "gl_PointSize";
case BuiltInVertexId:
if (options.vulkan_semantics)
throw CompilerError(
"Cannot implement gl_VertexID in Vulkan GLSL. This shader was created with GL semantics.");
return "gl_VertexID";
case BuiltInInstanceId:
if (options.vulkan_semantics)
throw CompilerError(
"Cannot implement gl_InstanceID in Vulkan GLSL. This shader was created with GL semantics.");
return "gl_InstanceID";
case BuiltInVertexIndex:
return "gl_VertexID"; // gl_VertexID already has the base offset applied.
if (options.vulkan_semantics)
return "gl_VertexIndex";
else
return "gl_VertexID"; // gl_VertexID already has the base offset applied.
case BuiltInInstanceIndex:
return "(gl_InstanceID + SPIRV_Cross_BaseInstance)"; // ... but not gl_InstanceID.
if (options.vulkan_semantics)
return "gl_InstanceIndex";
else
return "(gl_InstanceID + SPIRV_Cross_BaseInstance)"; // ... but not gl_InstanceID.
case BuiltInPrimitiveId:
return "gl_PrimitiveID";
case BuiltInInvocationId:
@ -3191,9 +3227,16 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
}
else if (type.image.dim == DimSubpassData)
{
// Implement subpass loads via texture barrier style sampling.
// Fairly ugly, but should essentially work as a fallback for desktop.
imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), 0)");
if (options.vulkan_semantics)
{
// With Vulkan semantics, use the proper Vulkan GLSL construct.
imgexpr = join("subpassLoad(", to_expression(ops[2]), ")");
}
else
{
// Implement subpass loads via texture barrier style sampling.
imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), 0)");
}
pure = true;
}
else
@ -3504,6 +3547,9 @@ string CompilerGLSL::image_type_glsl(const SPIRType &type)
break;
}
if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && options.vulkan_semantics)
return res + "subpassInput";
// If we're emulating subpassInput with samplers, force sampler2D
// so we don't have to specify format.
if (type.basetype == SPIRType::Image && type.image.dim != DimSubpassData)

View File

@ -60,6 +60,10 @@ public:
bool es = false;
bool force_temporary = false;
// If true, Vulkan GLSL features are used instead of GL-compatible features.
// Mostly useful for debugging SPIR-V files.
bool vulkan_semantics = false;
enum Precision
{
DontCare,
@ -208,6 +212,8 @@ protected:
void emit_resources();
void emit_buffer_block(const SPIRVariable &type);
void emit_push_constant_block(const SPIRVariable &var);
void emit_push_constant_block_vulkan(const SPIRVariable &var);
void emit_push_constant_block_glsl(const SPIRVariable &var);
void emit_interface_block(const SPIRVariable &type);
void emit_block_chain(SPIRBlock &block);
std::string emit_continue_block(uint32_t continue_block);

View File

@ -72,10 +72,22 @@ def cross_compile(shader, vulkan):
os.close(spirv_f)
os.close(glsl_f)
if vulkan:
vulkan_glsl_f, vulkan_glsl_path = tempfile.mkstemp(suffix = os.path.basename(shader))
os.close(vulkan_glsl_f)
subprocess.check_call(['glslangValidator', '-V' if vulkan else '-G', '-o', spirv_path, shader])
subprocess.check_call(['./spirv-cross', '--output', glsl_path, spirv_path])
validate_shader(glsl_path, vulkan)
return (spirv_path, glsl_path)
if not ('nocompat' in glsl_path):
validate_shader(glsl_path, False)
if vulkan:
subprocess.check_call(['./spirv-cross', '--vulkan-semantics', '--output', vulkan_glsl_path, spirv_path])
validate_shader(vulkan_glsl_path, vulkan)
return (spirv_path, glsl_path, vulkan_glsl_path if vulkan else None)
def md5_for_file(path):
md5 = hashlib.md5()
@ -115,12 +127,14 @@ def regression_check(shader, glsl, update, keep):
def test_shader(stats, shader, update, keep, vulkan):
print('Testing shader:', shader)
spirv, glsl = cross_compile(shader, vulkan)
spirv, glsl, vulkan_glsl = cross_compile(shader, vulkan)
if stats:
cross_stats = get_shader_stats(glsl)
regression_check(shader, glsl, update, keep)
if vulkan_glsl:
regression_check(shader + '.vk', vulkan_glsl, update, keep)
os.remove(spirv)
if stats: