KhronosGroup / SPIRV-Cross

SPIRV-Cross is a practical tool and library for performing reflection on SPIR-V and disassembling SPIR-V back to high level languages.
Apache License 2.0
2.05k stars 562 forks source link

C Interface #611

Closed amerkoleci closed 5 years ago

amerkoleci commented 6 years ago

Not really an issue, would be nice to have C interface for SPIRV-Cross so we can call from external, for example C#.

Maybe I can send PR tho.

HansKristian-Work commented 6 years ago

A C-interface would be useful, but I'd prefer if this was contributed by someone else (e.g. you). @rossy made something like this, not sure how complete it is.

amerkoleci commented 6 years ago

I have something WIP here (https://github.com/amerkoleci/SPIRVCrossSharp), will do PR

rossy commented 6 years ago

Yeah, I made a (very limited) C interface for SPIRV-Cross so I could use it in mpv: https://github.com/rossy/crossc

The API is documented here: https://github.com/rossy/crossc/blob/master/crossc.h

Currently it can only compile to HLSL and it only has interfaces for the options I needed in mpv (shader_model, flip_vert_y,) though it also has features that I think are important for a cross-platform C library, like shared library support (with SONAMEs,) and I tried to build it in a way that encourages ABI stability by using accessor functions to set properties, rather than setting them in public structures.

I was meaning to propose it to upstream and hopefully get it (or something like it) integrated as the official C interface for SPIRV-Cross, but I guess I got lazy, since I haven't done that yet.

grovesNL commented 6 years ago

I do something similar in order to generate internal Rust bindings to map them to a Rust API. The biggest issues I had were handling exception messages and classes.

Maybe this wrapper has some useful ideas, although it tends to be slightly conservative and copies most return values from SPIRV-Cross (I don't try to assume the lifetime of return values): https://github.com/grovesNL/spirv_cross/blob/master/spirv_cross/src/wrapper.hpp https://github.com/grovesNL/spirv_cross/blob/master/spirv_cross/src/wrapper.cpp

HansKristian-Work commented 6 years ago

It sounds like many people have looked into similar things, so we should make sure the C wrapper is comprehensive enough to cover the cases for everyone who has been involved in this.

As for exceptions, a C wrapper can easily catch all exception and translate that. Generally, exceptions in SPIRV-Cross are "fatal" exceptions. Usually, exceptions can be directly translated to an abort().

The most important things for me is:

ratchetfreak commented 6 years ago

and you should be careful of allocations, making sure that each side of the api boundary will free what was allocated by them and not the other.

HansKristian-Work commented 6 years ago

Ye, that's another consideration. Where allocations and frees happen.

rossy commented 6 years ago

@HansKristian-ARM Those requirements sound good to me. Also thanks for keeping mpv's use case in mind.

As for exceptions, a C wrapper can easily catch all exception and translate that. Generally, exceptions in SPIRV-Cross are "fatal" exceptions. Usually, exceptions can be directly translated to an abort().

Hopefully the abort() won't be done by the C wrapper itself. A fatal exception in SPIRV-Cross might not be fatal for the calling application. For example, mpv lets the user interactively enable and disable shaders, so if a shader can't be translated, the user can just disable it again. Also, mpv logs the full shader source on error to help with debugging, which it wouldn't be able to do if the process aborted immediately.

Currently, mpv calls crossc_strerror in crossc to get the exception message (e.what()) thrown by SPIRV-Cross, which is also helpful for debugging.

HansKristian-Work commented 6 years ago

Yes, the wrapper shouldn't actually call abort(), just meant that the errors tend to be fatal in nature, so translating the exception handlers into some generic error code + error string is fine.

HansKristian-Work commented 5 years ago

I'm mentally marking this is as the biggest ticket feature I will try to get done.

grovesNL commented 5 years ago

The error code + error string would be great to avoid e.what(). I have a build of my SPIRV-Cross C wrapper that I compiled to WebAssembly with Emscripten, but exceptions aren't supported in that environment, so I use -fno-exceptions and -DSPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS=1 at the moment.

dneto0 commented 5 years ago

Hm. I had not seen this issue before.

For embedding in Chromium, my team wanted a path into SPIRV-Cross which (1) always runs SPIR-V validatio first and (2) never uses exceptions (aborts instead). We ended up writing a library to do this, as a component in Shaderc, and are now making "libshaderc_spvc".

And in Shaderc we always make a C API as the baseline thing, so we have https://github.com/google/shaderc/blob/master/libshaderc_spvc/include/shaderc/spvc.h It might not be complete, but we intend to keep it stable. Then we build a headers-only C++ wrapper around that.

Note: your uses might want to deliberately bypass SPIR-V validation, so you might not want to use codepath.

dneto0 commented 5 years ago

(whoops. Accidentally closed the issue!) Also, thanks to @HansKristian-Work for adding the abort-instead-of-exceptions mode and making an interface we could use to bypass the exception-heavy initial binary parsing.

HansKristian-Work commented 5 years ago

@dneto0 I see. From what I can tell, that interface is more tailored to the needs of simple cases of cross compilation. I guess the main user of this interface right now is WebGPU?

On top of that, I intend to add the full reflection support, querying type information and all other options and helper functions for special use cases. I don't think this interface is going to supplant whatever is in shaderc though. I don't intent on exposing the ParsedIR integration interface to C for example.

HansKristian-Work commented 5 years ago

One thing that might be possible to add is a wrapper to export a C++ generated ParsedIR to a C version of that. That might enable shaderc to reuse the C wrapper for the advanced use cases.