Multiple -entry options in single slangc invocation does not work
Hello!
I'm trying to use the slangc commandline utility to output multiple .spv outputs, one per entry point. However, this doesn't seem to work...
According to the help,
-entry
: Specify the name of an entry-point function. Multiple -entry options may be used in a single invocation. If no -entry options are given, compiler will use [shader(...)] attributes to detect entry points.
I have a simple slang file here:
StructuredBuffer<float> buffer0;
StructuredBuffer<float> buffer1;
RWStructuredBuffer<float> result;
[shader("compute")]
[numthreads(1,1,1)]
void hello(uint3 threadId : SV_DispatchThreadID)
{
uint index = threadId.x;
result[index] = buffer0[index] + buffer1[index] + 1337.f;
}
[shader("compute")]
[numthreads(1,1,1)]
void world(uint3 threadId : SV_DispatchThreadID)
{
uint index = threadId.x;
result[index] = buffer0[index] + buffer1[index] + 42.f;
}
This command works: slangc.exe hello-world.slang -entry hello -o test2.spv
And this one works too: slangc.exe hello-world.slang -entry world -o test2.spv
But multiple -entry result in errors: slangc.exe hello-world.slang -entry hello -o test1.spv -entry world -o test2.spv:
(0): error 70: the output path 'test1.spv' is not associated with any entry point; a '-o' option for a compiled kernel must follow the '-entry' option for its corresponding entry point (0): error 70: the output path 'test2.spv' is not associated with any entry point; a '-o' option for a compiled kernel must follow the '-entry' option for its corresponding entry point
Perhaps I'm using the compiler incorrectly here, but I would have expected the above to work given the help text generated by the compiler.
Hi Nate,
I believe this currently isn't supported - although it's something we've talked about adding support for, and we have a fair amount of the infrastructure needed for it.
The underlying issue is having a 'container' for multiple kernel binaries. The intention there being using our zip file support and probably some manifest describing the contents, most likely in JSON.
Could I ask for more information about your need/desire for the capability?
I should probably add I believe we support multiple entry points in a DXIL lib though (because that is possible in a single DXIL output binary).
Hey Jonathan!
if this is unsupported then the -h output for slangc should probably be changed. At the moment it states that multiple entry points are allowed on the same compiler line, which sounds like isn’t true for SPIRV.
At the moment, I am trying to replicate my OptiX workflow as close as I can with Slang. In OptiX, we often compile our GPU code into PTX through nvcc, and then embed that PTX as a binary array into a .c file.
For example here: https://github.com/owl-project/owl/blob/3fec33ee6f09133fe18493f63607051e8a539368/samples/interactive/int01-simpleTriangles/CMakeLists.txt#L17 That embed_ptx command takes the shader source and outputs a single PTX with all entry points. Sounds like that's similar to DX's multiple entry points in a DXIL lib
In a ray tracing workload, there are many entry points that I’ll work with. I might have 5-10 closest hit programs, anyhit programs, intersection programs, and several miss programs as well as a ray gen program. It’s nice to have all of these be compiled into one PTX output in one step, as all these entry points often share the same helper functions and definitions.
Right now with slang, I have to re-compile my shaders for every entry point, which is pretty ugly in comparison, and means developers must edit the CMakeLists.txt file for each entry point they introduce to the shader code that needs a compiler evocation.
Seems inefficient to compile the same code over and over again, and I’d like to somehow avoid developers needing to introduce new targets to CMake whenever they make a new entry point.
Here's what my slang workflow is like: https://github.com/natevm/vkrt/blob/b2f77df1ebaceddb5c9ae51706e8598d8c9ea581/CMakeLists.txt#L59
I might somehow be able to extract all entry points in my build system somehow though to make editing the cmake for each new entrypoint an automated thing.
It sure looks like the short answer is that slangc isn't matching the behavior it implies in its error messages.
A compilation that names multiple entry points and multiple output files with -o should work. The user's meaning is clear. I suspect that a bug snuck into the "do what I mean" part of the options-parsing logic.
@natevm If you'd be willing to debug the issue into the options-parsing logic in Slang, we can probably get the case of a single slangc invocation with multiple -os to work.
That wouldn't solve the larger issue where a target like PTX can support multiple entry points in the same output binary/file, while our SPIR-V back-end does not. I want to follow up with another message on that topic.
There's a bunch of notes I want to add here, in the vicinity of the topic being raised, even if they do not directly relate to the error message seen.
Support for Outputs Files With Multiple Entry Points
The Slang API and slangc tool differentiate between two main modes of compilation: single-entry-point and multiple-entry-point or "whole program" mode. The difference is very visible at the API level, where the API user can either request a binary blob for an entire program (IComponentType), which will include all entry points, or request a blob for a single entry point (indicating the entry point by index).
Some of our compilation back-ends naturally/natively support N entry points being compiled into a single binary. For those back-ends, "single-entry point mode" is simply implemented as the N == 1 case.
Some of our compilation back-ends naturally/natively support only one entry point being compiled per binary output. Those back-ends fundamentally cannot directly support multiple entry points in one binary output.
Right now our SPIR-V compilation path is one of the back-ends that cannot support multiple entry points in the same binary. There are a few paths forward that could provide a solution to the problem.
Adding multiple-entry-point support to our SPIR-V output
The actual SPIR-V encoding supports multiple entry points in a .spv file. The limitation imposed by Slang is actually a byproduct of the way we generate SPIR-V: we emit GLSL source based on Slang IR, and then compile that GLSL with glslang (+ spirv-opt) to get SPIR-V. Because the GLSL language only supports a single entry point per compile, we end up having a matching restriction.
There are two main ways that we could address this limitation in our SPIR-V output path:
-
We could bypass GLSL and
glslangand start emitting SPIR-V directly from Slang IR, at which point we can go ahead and implement multiple-entry-point support from the get-go. -
We could take the separately-generated SPIR-V blobs for different entry points and use a SPIR-V linker to merge them into one.
Option (1) is something we consider POR in the long run, but there is a significant amount of engineering before that option is ready. Option (2) would be something of a kludge, but could, at least in theory, provide a workable near-term solution for people. Depending on whether we already have access to a SPIR-V linker through the spirv-tools repository, it might be possible for a contributor to add (2) as a quick-fix.
Using a "container" file to wrap multiple outputs
As @jsmall-nvidia mentions, we also want to support the option of storing multiple per-entry-point and/or per-target binaries in a "container" file (likely to be a .zip-based format). That approach would allow us to support multiple-entry-point mode for any back-end (or even multiple back-ends in one go), but would require the runtime application to incorporate logic to read the container file and extract binaries from it. We expect to provide implementations of such services as part of the Slang codebase.
Support for "container" files is something we consider POR, but don't currently have anybody working on. It might be something a motivated contributor could add without undue effort, at least for an initial version.
Implicit vs. explicit entry point mode
When invoking slangc or using the Slang API, there is actually a different mode distinction made from the single-entry-point or multiple-entry-point choice being described above:
- If there are any explicit
-entryoptions (or the API equivalent), then we are in "explicit entry point mode" - If there are no explicit
-entryoptions, then we are in "implicit entry point mode"
The distinction then has consequences for the rest of compilation:
- In "explicit" mode, the only entry points that get validated and compiled are the ones the user specified
- In "implicit" mode, any suitable function with a
[shader(...)]attribute will be validated and compiled
IMO, "explicit entry point mode" is actually more of a legacy feature than a good way to do shader compilation.
For targets that support multiple entry points, it should be possible to just drop the -entry options completely:
slangc stuff.slang -o stuff.ptx
For targets that only support single-entry-point compilation, the "implicit" mode isn't really usable through slangc. Note that it is usable via the Slang API, though.
Thanks for the response!
A bit more background, we generally lean towards using slangc, as for this project we prefer not to ship raw shader code with the executable. It's also convenient to avoid compilation during runtime startup. For more complicated setups, (eg, on the fly shader compilation) I would be tempted to use the runtime slang API to inline different functions and things, but for this project where the shaders are large but static, we're leaning towards pre-compiled shaders instead.
I understand this is a limitation with the SPV backend though. IIUC, spv does support multiple entry points, and although HLSL supports this, glsl does not, so it seems to be a relatively underdeveloped feature.
Agreed, "implicit" mode would definitely be preferred. That would remove a bookkeeping step. Explicit entry points seem like a legacy thing that should fade away as GPU languages become more one-to-one with CPU languages.
At the moment, I'm generating one .spv file per-entry point, and then embedding all of those.spv files into one single .c file with multiple uchar* arrays. It's tempting to have slangc's implicit mode generate multiple .spv files, one per discovered entry point, and named automatically depending on the name of the entry point... Maybe as an "experimental" flag until "Option 1" could be implemented.
I can probably take a look at the multiple -o behavior, at least for explicit entry point mode.
Closing as fixed.