Add separate shader object support.

Allow redeclaring gl_PerVertex for stage compatibility.
This commit is contained in:
Hans-Kristian Arntzen 2017-05-22 15:30:43 +02:00
parent c3e6fdc579
commit 36650c890c
4 changed files with 101 additions and 2 deletions

View File

@ -432,6 +432,7 @@ struct CLIArguments
bool force_temporary = false;
bool flatten_ubo = false;
bool fixup = false;
bool sso = false;
vector<PLSArg> pls_in;
vector<PLSArg> pls_out;
vector<Remap> remaps;
@ -458,6 +459,7 @@ static void print_help()
"[--cpp] [--cpp-interface-name <name>] "
"[--msl] [--msl-no-pack-ubos] "
"[--hlsl] [--shader-model] [--hlsl-enable-compat] "
"[--separate-shader-objects]"
"[--pls-in format input-name] [--pls-out format output-name] [--remap source_name target_name "
"components] [--extension ext] [--entry name] [--remove-unused-variables] "
"[--remap-variable-type <variable_name> <new_variable_type>]\n");
@ -585,6 +587,7 @@ int main(int argc, char *argv[])
cbs.add("--vulkan-semantics", [&args](CLIParser &) { args.vulkan_semantics = true; });
cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); });
cbs.add("--entry", [&args](CLIParser &parser) { args.entry = parser.next_string(); });
cbs.add("--separate-shader-objects", [&args](CLIParser &) { args.sso = true; });
cbs.add("--remap", [&args](CLIParser &parser) {
string src = parser.next_string();
string dst = parser.next_string();
@ -689,6 +692,7 @@ int main(int argc, char *argv[])
if (args.set_es)
opts.es = args.es;
opts.force_temporary = args.force_temporary;
opts.separate_shader_objects = args.sso;
opts.vulkan_semantics = args.vulkan_semantics;
opts.vertex.fixup_clipspace = args.fixup;
opts.cfg_analysis = args.cfg_analysis;

View File

@ -300,6 +300,9 @@ void CompilerGLSL::find_static_extensions()
if (!pls_inputs.empty() || !pls_outputs.empty())
require_extension("GL_EXT_shader_pixel_local_storage");
if (options.separate_shader_objects && !options.es && options.version < 410)
require_extension("GL_ARB_separate_shader_objects");
}
string CompilerGLSL::compile()
@ -1495,6 +1498,64 @@ void CompilerGLSL::fixup_image_load_store_access()
}
}
void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionModel model)
{
bool emitted_block = false;
for (auto &id : ids)
{
if (id.get_type() != TypeVariable)
continue;
auto &var = id.get<SPIRVariable>();
auto &type = get<SPIRType>(var.basetype);
bool block = has_decoration(type.self, DecorationBlock);
uint64_t builtins = 0;
if (var.storage == storage && block && is_builtin_variable(var))
{
for (auto &m : meta[type.self].members)
if (m.builtin)
builtins |= 1ull << m.builtin_type;
}
if (!builtins)
continue;
if (emitted_block)
SPIRV_CROSS_THROW("Cannot use more than one builtin I/O block.");
if (storage == StorageClassOutput)
statement("out gl_PerVertex");
else
statement("in gl_PerVertex");
begin_scope();
if (builtins & (1ull << BuiltInPosition))
statement("vec4 gl_Position;");
if (builtins & (1ull << BuiltInPointSize))
statement("float gl_PointSize;");
if (builtins & (1ull << BuiltInClipDistance))
statement("float gl_ClipDistance[];"); // TODO: Do we need a fixed array size here?
if (builtins & (1ull << BuiltInCullDistance))
statement("float gl_CullDistance[];"); // TODO: Do we need a fixed array size here?
bool builtin_array = !type.array.empty();
bool tessellation = model == ExecutionModelTessellationEvaluation || model == ExecutionModelTessellationControl;
if (builtin_array)
{
if (model == ExecutionModelTessellationControl && storage == StorageClassOutput)
end_scope_decl(join(to_name(var.self), "[", get_entry_point().output_vertices, "]"));
else
end_scope_decl(join(to_name(var.self), tessellation ? "[gl_MaxPatchVertices]" : "[]"));
}
else
end_scope_decl();
statement("");
emitted_block = true;
}
}
void CompilerGLSL::emit_resources()
{
auto &execution = get_entry_point();
@ -1510,6 +1571,27 @@ void CompilerGLSL::emit_resources()
if (!pls_inputs.empty() || !pls_outputs.empty())
emit_pls();
// Emit custom gl_PerVertex for SSO compatibility.
if (options.separate_shader_objects && !options.es)
{
switch (execution.model)
{
case ExecutionModelGeometry:
case ExecutionModelTessellationControl:
case ExecutionModelTessellationEvaluation:
emit_declared_builtin_block(StorageClassInput, execution.model);
emit_declared_builtin_block(StorageClassOutput, execution.model);
break;
case ExecutionModelVertex:
emit_declared_builtin_block(StorageClassOutput, execution.model);
break;
default:
break;
}
}
bool emitted = false;
// If emitted Vulkan GLSL,

View File

@ -67,6 +67,12 @@ public:
// Mostly useful for debugging SPIR-V files.
bool vulkan_semantics = false;
// If true, gl_PerVertex is explicitly redeclared in vertex, geometry and tessellation shaders.
// The members of gl_PerVertex is determined by which built-ins are declared by the shader.
// This option is ignored in ES versions, as redeclaration in ES is not required, and it depends on a different extension
// (EXT_shader_io_blocks) which makes things a bit more fuzzy.
bool separate_shader_objects = false;
enum Precision
{
DontCare,
@ -284,6 +290,7 @@ protected:
void emit_buffer_block_native(const SPIRVariable &var);
void emit_buffer_block_legacy(const SPIRVariable &var);
void emit_buffer_block_flattened(const SPIRVariable &type);
void emit_declared_builtin_block(spv::StorageClass storage, spv::ExecutionModel model);
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);

View File

@ -120,7 +120,7 @@ def validate_shader(shader, vulkan):
else:
subprocess.check_call(['glslangValidator', shader])
def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo):
def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso):
spirv_f, spirv_path = tempfile.mkstemp()
glsl_f, glsl_path = tempfile.mkstemp(suffix = os.path.basename(shader))
os.close(spirv_f)
@ -145,6 +145,8 @@ def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, fl
extra_args += ['--version', '100', '--es']
if flatten_ubo:
extra_args += ['--flatten-ubo']
if sso:
extra_args += ['--separate-shader-objects']
spirv_cross_path = './spirv-cross'
subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', glsl_path, spirv_path] + extra_args)
@ -238,6 +240,9 @@ def shader_is_legacy(shader):
def shader_is_flatten_ubo(shader):
return '.flatten.' in shader
def shader_is_sso(shader):
return '.sso.' in shader
def test_shader(stats, shader, update, keep):
joined_path = os.path.join(shader[0], shader[1])
vulkan = shader_is_vulkan(shader[1])
@ -247,9 +252,10 @@ def test_shader(stats, shader, update, keep):
invalid_spirv = shader_is_invalid_spirv(shader[1])
is_legacy = shader_is_legacy(shader[1])
flatten_ubo = shader_is_flatten_ubo(shader[1])
sso = shader_is_sso(shader[1])
print('Testing shader:', joined_path)
spirv, glsl, vulkan_glsl = cross_compile(joined_path, vulkan, is_spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo)
spirv, glsl, vulkan_glsl = cross_compile(joined_path, vulkan, is_spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso)
# Only test GLSL stats if we have a shader following GL semantics.
if stats and (not vulkan) and (not is_spirv) and (not desktop):