Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions reference/shaders-hlsl/asm/lib/export-calls-export.asm.lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
uint add_one(uint x)
{
return x + 1u;
}

uint add_three(uint z)
{
return z + 3u;
}

uint add_two(uint y)
{
uint _16 = y;
uint _17 = add_one(_16);
uint _18 = add_one(_17);
return add_three(_18);
}

11 changes: 11 additions & 0 deletions reference/shaders/asm/lib/global-array.asm.lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifdef SPIRV_CROSS_LIBRARY_HEADER
#version 450
#endif

const uint _15[4] = uint[](10u, 20u, 30u, 40u);

uint lookup(uint i)
{
return _15[i];
}

21 changes: 21 additions & 0 deletions reference/shaders/asm/lib/multi-export.asm.lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifdef SPIRV_CROSS_LIBRARY_HEADER
#version 450
#endif

uint add_one(uint x)
{
return x + 1u;
}

uint helper_add(uint a, uint b)
{
return a + b;
}

uint add_two(uint y)
{
uint _22 = y;
uint _23 = 2u;
return helper_add(_22, _23);
}

59 changes: 59 additions & 0 deletions shaders-hlsl/asm/lib/export-calls-export.asm.lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
; SPIR-V
; Version: 1.5
; Generator: Khronos SPIR-V Tools Assembler; 0
; Bound: 50
; Schema: 0
OpCapability Linkage
OpCapability Shader
OpMemoryModel Logical GLSL450
OpSource HLSL 630
OpName %add_one "add_one"
OpName %x "x"
OpName %add_two "add_two"
OpName %y "y"
OpName %add_three "add_three"
OpName %z "z"
OpDecorate %add_one LinkageAttributes "add_one" Export
OpDecorate %add_two LinkageAttributes "add_two" Export
OpDecorate %add_three LinkageAttributes "add_three" Export
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%uint_3 = OpConstant %uint 3
%_ptr_Function_uint = OpTypePointer Function %uint
%fn1 = OpTypeFunction %uint %_ptr_Function_uint

%add_one = OpFunction %uint None %fn1
%x = OpFunctionParameter %_ptr_Function_uint
%o_bb = OpLabel
%o_xv = OpLoad %uint %x
%o_r = OpIAdd %uint %o_xv %uint_1
OpReturnValue %o_r
OpFunctionEnd

; add_two calls add_one twice (already-emitted callee), and forward-references
; add_three (callee declared *after* add_two in the module). Both exercise
; the func.active short-circuit but in different directions.

%add_two = OpFunction %uint None %fn1
%y = OpFunctionParameter %_ptr_Function_uint
%t_bb = OpLabel
%t_arg1 = OpVariable %_ptr_Function_uint Function
%t_arg2 = OpVariable %_ptr_Function_uint Function
%t_arg3 = OpVariable %_ptr_Function_uint Function
%t_yv = OpLoad %uint %y
OpStore %t_arg1 %t_yv
%t_a = OpFunctionCall %uint %add_one %t_arg1
OpStore %t_arg2 %t_a
%t_b = OpFunctionCall %uint %add_one %t_arg2
OpStore %t_arg3 %t_b
%t_r = OpFunctionCall %uint %add_three %t_arg3
OpReturnValue %t_r
OpFunctionEnd

%add_three = OpFunction %uint None %fn1
%z = OpFunctionParameter %_ptr_Function_uint
%r_bb = OpLabel
%r_zv = OpLoad %uint %z
%r_r = OpIAdd %uint %r_zv %uint_3
OpReturnValue %r_r
OpFunctionEnd
35 changes: 35 additions & 0 deletions shaders/asm/lib/global-array.asm.lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
; SPIR-V
; Version: 1.5
; Generator: Khronos SPIR-V Tools Assembler; 0
; Bound: 30
; Schema: 0
OpCapability Linkage
OpCapability Shader
OpMemoryModel Logical GLSL450
OpSource HLSL 630
OpName %lookup "lookup"
OpName %i "i"
OpName %table "table"
OpDecorate %lookup LinkageAttributes "lookup" Export
%uint = OpTypeInt 32 0
%uint_10 = OpConstant %uint 10
%uint_20 = OpConstant %uint 20
%uint_30 = OpConstant %uint 30
%uint_40 = OpConstant %uint 40
%uint_4 = OpConstant %uint 4
%_arr_uint_uint_4 = OpTypeArray %uint %uint_4
%_ptr_Private__arr_uint_uint_4 = OpTypePointer Private %_arr_uint_uint_4
%_ptr_Private_uint = OpTypePointer Private %uint
%_ptr_Function_uint = OpTypePointer Function %uint
%fn1 = OpTypeFunction %uint %_ptr_Function_uint
%table_init = OpConstantComposite %_arr_uint_uint_4 %uint_10 %uint_20 %uint_30 %uint_40
%table = OpVariable %_ptr_Private__arr_uint_uint_4 Private %table_init

%lookup = OpFunction %uint None %fn1
%i = OpFunctionParameter %_ptr_Function_uint
%l_bb = OpLabel
%l_iv = OpLoad %uint %i
%l_ac = OpAccessChain %_ptr_Private_uint %table %l_iv
%l_v = OpLoad %uint %l_ac
OpReturnValue %l_v
OpFunctionEnd
54 changes: 54 additions & 0 deletions shaders/asm/lib/multi-export.asm.lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
; SPIR-V
; Version: 1.5
; Generator: Khronos SPIR-V Tools Assembler; 0
; Bound: 40
; Schema: 0
OpCapability Linkage
OpCapability Shader
OpMemoryModel Logical GLSL450
OpSource HLSL 630
OpName %add_one "add_one"
OpName %x "x"
OpName %add_two "add_two"
OpName %y "y"
OpName %helper_add "helper_add"
OpName %a "a"
OpName %b "b"
OpDecorate %add_one LinkageAttributes "add_one" Export
OpDecorate %add_two LinkageAttributes "add_two" Export
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%uint_2 = OpConstant %uint 2
%_ptr_Function_uint = OpTypePointer Function %uint
%fn1 = OpTypeFunction %uint %_ptr_Function_uint
%fn2 = OpTypeFunction %uint %_ptr_Function_uint %_ptr_Function_uint

%helper_add = OpFunction %uint None %fn2
%a = OpFunctionParameter %_ptr_Function_uint
%b = OpFunctionParameter %_ptr_Function_uint
%h_bb = OpLabel
%h_av = OpLoad %uint %a
%h_bv = OpLoad %uint %b
%h_sum = OpIAdd %uint %h_av %h_bv
OpReturnValue %h_sum
OpFunctionEnd

%add_one = OpFunction %uint None %fn1
%x = OpFunctionParameter %_ptr_Function_uint
%o_bb = OpLabel
%o_xv = OpLoad %uint %x
%o_r = OpIAdd %uint %o_xv %uint_1
OpReturnValue %o_r
OpFunctionEnd

%add_two = OpFunction %uint None %fn1
%y = OpFunctionParameter %_ptr_Function_uint
%t_bb = OpLabel
%t_arg1 = OpVariable %_ptr_Function_uint Function
%t_arg2 = OpVariable %_ptr_Function_uint Function
%t_yv = OpLoad %uint %y
OpStore %t_arg1 %t_yv
OpStore %t_arg2 %uint_2
%t_r = OpFunctionCall %uint %helper_add %t_arg1 %t_arg2
OpReturnValue %t_r
OpFunctionEnd
44 changes: 38 additions & 6 deletions spirv_glsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -820,14 +820,23 @@ string CompilerGLSL::compile()
if ((options.es || options.vulkan_semantics) && required_polyfills_relaxed != 0)
emit_polyfills(required_polyfills_relaxed, true);

emit_function(get<SPIRFunction>(ir.default_entry_point), Bitset());
if (ir.is_library_module)
{
// Emit each exported function as a normal free function.
// emit_function recursively emits callees, so internal helpers
// are picked up too.
for (auto export_id : ir.library_exported_functions)
emit_function(get<SPIRFunction>(export_id), Bitset());
}
else
emit_function(get<SPIRFunction>(ir.default_entry_point), Bitset());

pass_count++;
} while (is_forcing_recompilation());

// Implement the interlocked wrapper function at the end.
// The body was implemented in lieu of main().
if (interlocked_is_complex)
if (interlocked_is_complex && !ir.is_library_module)
{
if (options.use_entry_point_name)
statement("void ", get_entry_point().name, "()");
Expand All @@ -841,8 +850,9 @@ string CompilerGLSL::compile()
end_scope();
}

// Entry point in GLSL is always main().
if (!options.use_entry_point_name)
// Entry point in GLSL is always main(). Skip the rename for library
// modules; their exports keep their declared names.
if (!options.use_entry_point_name && !ir.is_library_module)
get_entry_point().name = "main";

return buffer.str();
Expand Down Expand Up @@ -915,6 +925,16 @@ void CompilerGLSL::request_subgroup_feature(ShaderSubgroupSupportHelper::Feature
void CompilerGLSL::emit_header()
{
auto &execution = get_entry_point();

// Library modules have no entry point. The emitted GLSL is meant to be #include'd or appended
// rather than compiled standalone, so the version and extension directives that follow are
// wrapped in `#ifdef SPIRV_CROSS_LIBRARY_HEADER ... #endif`. By default they are skipped (the
// consuming translation unit provides its own preamble); a caller that wants to compile the
// library standalone defines SPIRV_CROSS_LIBRARY_HEADER to opt in. The stage-specific layout
// block at the end of this function is skipped entirely in library mode.
if (ir.is_library_module)
statement("#ifdef SPIRV_CROSS_LIBRARY_HEADER");

statement("#version ", options.version, options.es && options.version > 100 ? " es" : "");

if (!options.es && options.version < 420)
Expand Down Expand Up @@ -1124,6 +1144,13 @@ void CompilerGLSL::emit_header()
for (auto &header : header_lines)
statement(header);

if (ir.is_library_module)
{
statement("#endif");
statement("");
return;
}

SmallVector<string> inputs;
SmallVector<string> outputs;

Expand Down Expand Up @@ -17733,7 +17760,12 @@ void CompilerGLSL::add_function_overload(const SPIRFunction &func)

void CompilerGLSL::emit_function_prototype(SPIRFunction &func, const Bitset &return_flags)
{
if (func.self != ir.default_entry_point)
// In library mode default_entry_point points at the first exported
// function; treat every export as a normal function rather than as the
// shader's entry point.
const bool is_entry_point = !ir.is_library_module && func.self == ir.default_entry_point;

if (!is_entry_point)
add_function_overload(func);

// Avoid shadow declarations.
Expand All @@ -17747,7 +17779,7 @@ void CompilerGLSL::emit_function_prototype(SPIRFunction &func, const Bitset &ret
decl += type_to_array_glsl(type, 0);
decl += " ";

if (func.self == ir.default_entry_point)
if (is_entry_point)
{
// If we need complex fallback in GLSL, we just wrap main() in a function
// and interlock the entire shader ...
Expand Down
47 changes: 40 additions & 7 deletions test_shaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,8 +561,7 @@ def cross_compile_hlsl(shader, spirv, opt, force_no_external_validation, iterati

sm = shader_to_sm(shader)

# Library SPIR-V modules (e.g. dxc -T lib_6_*) have no OpEntryPoint;
# skip the --entry flag for those so spirv-cross does not try to
# Library SPIR-V modules have no OpEntryPoint. Skip the --entry flag for those so spirv-cross does not try to
# select an entry point that does not exist.
is_library = shader_is_library(shader)
hlsl_args = [spirv_cross_path]
Expand Down Expand Up @@ -630,13 +629,37 @@ def validate_shader(shader, vulkan, paths):
else:
subprocess.check_call([paths.glslang, shader])

def validate_library_glsl(library_path, paths):
# Library GLSL output has no #version directive and no main(), since it is meant to be #include'd by GLSL
# source. Validate it by writing a minimal wrapper translation unit alongside it that does the include via
# the GL_GOOGLE_include_directive and runs glslang on the wrapper. Note, `-V` is required because glslang
# only processes GL_GOOGLE_include_directive under Vulkan semantics.
library_dir = os.path.dirname(library_path)
library_name = os.path.basename(library_path)
fd, wrapper_path = tempfile.mkstemp(suffix = '.frag', dir = library_dir)
try:
with os.fdopen(fd, 'w') as f:
f.write('#version 450\n')
f.write('#extension GL_GOOGLE_include_directive : require\n')
f.write('#include "' + library_name + '"\n')
f.write('void main() {}\n')
subprocess.check_call([paths.glslang, '-V', wrapper_path])
finally:
remove_file(wrapper_path)

def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, force_es, flatten_ubo, sso, flatten_dim, opt, push_ubo, iterations, paths):
spirv_path = create_temporary()
glsl_path = create_temporary(os.path.basename(shader))

spirv_16 = '.spv16.' in shader
spirv_14 = '.spv14.' in shader
if spirv_16:
is_library = shader_is_library(shader)
if is_library:
# Library modules use the Linkage capability, which is rejected by
# Vulkan target envs. Use a universal/spv target instead.
spirv_env = 'spv1.5'
glslang_env = 'spirv1.5'
elif spirv_16:
spirv_env = 'spv1.6'
glslang_env = 'vulkan1.3'
elif spirv_14:
Expand Down Expand Up @@ -706,18 +729,28 @@ def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, fo

spirv_cross_path = paths.spirv_cross

# Library SPIR-V modules have no OpEntryPoint. skip the --entry flag for those so spirv-cross does not try to
# select an entry point that does not exist.
entry_arg = [] if is_library else ['--entry', 'main']

# A shader might not be possible to make valid GLSL from, skip validation for this case.
if (not ('nocompat' in glsl_path)) or (not vulkan):
subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', glsl_path, spirv_path] + extra_args)
subprocess.check_call([spirv_cross_path] + entry_arg + ['--output', glsl_path, spirv_path] + extra_args)
if not 'nocompat' in glsl_path:
validate_shader(glsl_path, False, paths)
if is_library:
validate_library_glsl(glsl_path, paths)
else:
validate_shader(glsl_path, False, paths)
else:
remove_file(glsl_path)
glsl_path = None

if (vulkan or spirv) and (not is_legacy):
subprocess.check_call([spirv_cross_path, '--entry', 'main', '-V', '--output', vulkan_glsl_path, spirv_path] + extra_args)
validate_shader(vulkan_glsl_path, True, paths)
subprocess.check_call([spirv_cross_path] + entry_arg + ['-V', '--output', vulkan_glsl_path, spirv_path] + extra_args)
if is_library:
validate_library_glsl(vulkan_glsl_path, paths)
else:
validate_shader(vulkan_glsl_path, True, paths)
# SPIR-V shaders might just want to validate Vulkan GLSL output, we don't always care about the output.
if not vulkan:
remove_file(vulkan_glsl_path)
Expand Down
Loading