floooh / sokol-tools

Command line tools for use with sokol headers
MIT License
236 stars 61 forks source link

add a lib-shdc? #34

Open kevinw opened 4 years ago

kevinw commented 4 years ago

Being able to recompile shaders at run-time is a thing I would like in my sokol projects.

sokol-shdc has done a wonderful job of wrapping the glslang and SPIRV-cross libs. Would it be feasible to write a C API for a sokol-shdc.dll that, given some shader source, fills the appropriate sokol shader description structs?

floooh commented 4 years ago

Yeah I think that should work and wouldn't be too complicated (both as DLL or static link library, or with a bit of tinkering even as a WASM module). It won't be small though (the DLL will probably be between 5..6 MBytes, or in case of a static lib, add that much to the executable).

What's needed is an "API header/source pair" which defines a C library API and maybe move some code from main.cc into a common source used both by the sokol-shdc exe and the library.

Ideally the sokol-shdc executable would use the same library API internally too (I'd prefer this because then the library API is automatically tested by the sokol-shdc executable).

I had something similar in mind but for a different use case: eventually I want to get rid of the precompiled binaries in https://github.com/floooh/sokol-tools-bin and want to have a solution where the bulk of the shader compiler is compiled to WASM and then executed through some WASI-launcher, I think this would also benefit from having the core functionality wrapped in a separate library.

I'll see if I can start tinkering on this 'soon-ish' (want to do some smaller things first).

rcases commented 3 years ago

Also define callback functions for basic file operations (open, read, write, seek, tell ,close). If not defined, use the standard ones. More or less as SDL_RWops

kkukshtel commented 1 year ago

Picking this back up — would also really like this. I'm binding Sokol headers to an external language (C#) for an engine, and being able to have runtime compilation of shaders (and also abstract away the process to end-users) would definitely be ideal.

I think something as simple as "pass in a glsl file and get back the shader in the target lang + uniforms in some info struct"would be great, don't need any of the header generation stuff.

Right now my plan was to ship shdc and invoke it as an external process and capture the output. A DLL would definitely be more ideal.

RandyGaul commented 3 months ago

Would it be possible to get some guidance on implementing a library for shdc? It seems a bit low priority for @floooh so perhaps I could take a stab at it. I’ve also wanted to compile shaders at runtime so they can be easily hotloaded, and to simplify build steps by not requiring build time shader compilation.

floooh commented 3 months ago

There's a couple of fairly simple steps, and one I don't know how to solve :)

...and now the tricky part: D3D and Metal byte code generation (which might be out of scope):

Both might not be an option (but maybe also not required?) for sokol-shdc as a library, so maybe this can simply be ignored (since at one point, shader source code needs to be compiled anyway, and this can just as well happen in sokol_gfx.h).

If anybody wants to tackle this, I would prefer it as a number of smaller PRs instead of a single big one, for instance:

  1. implement the virtual IO interface and replace any direct IO usage with this
  2. maybe another PR for the error reporting
  3. move most of the main function into a new common entry point function, and change main() to call that function
  4. implement the actual split into a library and executable
kkukshtel commented 3 months ago

Correct me if I'm wrong but bytecode generation would be the thing that would make the library most useful. Especially in terms of hotloading shaders, there would need to be some compile step as part of library functionality to actually consume the shaders, otherwise the library would only be about producing .c versions of the shaders, correct?

For me, the bytecode generation bit would definitely be the thing worth doing. Though I'm also not good enough (for now!) to actually build this func and would mostly be a consumer of whatever anyone else does until I could contribute to it.

rcases commented 3 months ago

I've been creating my own library for a while now.

The way I've done it revolves around making minimal changes to the shdc codebase to make it easier to maintain. These changes are splitting the load_and_preprocess method into load and preprocess and adding parse_source.

The library can use either a filename or a pointer to the source code. Unfortunately I haven't yet implemented a way to load includes from a virtual interface.

If you're interested in seeing the strategy I've used, you can find it here:

https://gist.github.com/rcases/8f65e7d54b2556fea60bc138af6cdb0d

floooh commented 3 months ago

otherwise the library would only be about producing .c versions of the shaders, correct?

No, there would be no code generation step, you would get GLSL, HLSL, MSL and WGSL source code as strings which you can feed into sokol-gfx in the sg_make_shader() call. The compilation of HLSL and MSL to byte code happens inside sokol_gfx.h then (and this compilation step needs to happen either way, because SPIRVCross cannot generate HLSL or Metal bytecode directly - so letting sokol_gfx.h do it isn't any less efficient than doing it yourself for a hot reload situation).

How to deal with reflection is indeed an interesting question though. One 'naive' way would be to have a new function which directly returns a populated sg_shader_desc struct, but that would make sokol-shdc dependent on sokol_gfx.h, which really isn't a good idea.

A cleaner way would be to have a new 'user-friendly' reflection struct which makes it easy to build a reflection_to_shader_desc() helper function on the user side.

kkukshtel commented 3 months ago

Ah thanks for the clarification!

but that would make sokol-shdc dependent on sokol_gfx.h, which really isn't a good idea.

One possible option here could be to provide a header similar to sokol_glue.h that acts as a go-between when both gfx and the (not yet existent) sokol_shdc.h are present that can return a populated sg_shader_desc struct?