CLI tool
KhronosGroup/SPIRV-Cross avatar
KhronosGroup/SPIRV-Cross

SPIRV-Cross: Converting SPIR-V Back to GLSL, MSL and HLSL

SPIRV-Cross is a practical tool and library for performing reflection on SPIR-V and disassembling SPIR-V back to high level languages.

2,519 stars718 forksGLSLApache-2.0

At a glance

What is it?
SPIRV-Cross is a Khronos Group tool and library that parses SPIR-V and emits readable GLSL, Metal Shading Language or HLSL, with a reflection API for building Vulkan pipeline layouts. It is a compiler in reverse, and the boundaries of that job matter as much as the output.
Who is it for?
Adopt SPIRV-Cross if you ship Vulkan or SPIR-V and need GLSL, MSL or HLSL output plus reflection for pipeline layout creation. Do not adopt it as a general SPIR-V decompiler for reverse engineering, and do not rely on the C++ API as a stable ABI boundary: link it statically or use the C wrapper.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly GLSL, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What SPIRV-Cross Is For, and Who Actually Needs It

SPIR-V is the intermediate representation Vulkan consumes. It is deliberately unfriendly to read and carries no information about which high level language produced it. SPIRV-Cross exists to walk that representation back out into a form a human or another compiler can use: GLSL, Metal Shading Language, HLSL, or a JSON reflection dump. The README states the goal plainly, that it tries to emit output that "looks like it was written by a human and not awkward IR/assembly-like code."

The audience is narrow and specific. Engine and driver-adjacent developers who already have SPIR-V in hand and need to target a second graphics API. A Vulkan renderer that must also run on Metal needs MSL; one that must run on Direct3D needs HLSL. The second audience is tooling: anyone who needs to inspect what a shader actually declares. The reflection API answers questions like which descriptor set and binding a sampled image occupies, which is exactly the data a Vulkan pipeline layout is built from.

What it is not is a shader compiler front end. It does not read GLSL, HLSL or MSL as input. It reads SPIR-V and writes those languages. If you are starting from GLSL source, you need a compiler such as glslang to produce the SPIR-V first, and the repository ships checkout_glslang_spirv_tools.sh and build_glslang_spirv_tools.sh, which suggests that is the expected companion workflow.

Reflection and Decoration Rewriting: The Mechanism Behind the API

The core object is a compiler instance constructed from a SPIR-V binary. In the C++ example in the README, a std::vector<uint32_t> is moved into a spirv_cross::CompilerGLSL. From that point the SPIR-V is parsed and two things become possible: querying resources, and rewriting decorations before emitting source.

The reflection side returns a ShaderResources struct. Iterating resources.sampled_images and calling get_decoration with spv::DecorationDescriptorSet and spv::DecorationBinding yields the set and binding numbers. That is the pipeline layout data.

The decoration rewriting is the more interesting half. The README example calls unset_decoration to strip the descriptor set and then set_decoration to remap the binding to set * 16 + binding. This is not cosmetic. GLSL has no descriptor sets, so a Vulkan set and binding pair has to be flattened into something a GL driver understands. Doing that remapping inside SPIRV-Cross means the same SPIR-V can feed different binding conventions without recompiling the shader.

Options are set through a CompilerGLSL::Options struct. The README sets options.version = 310 and options.es = true before calling set_common_options, then compile() returns the source as a std::string. So the output dialect is a compile-time decision made through that struct, not a guess.

Installing SPIRV-Cross and Running a First Conversion

CMake is the recommended build system. The README notes it is the only build system tested in continuous integration, and the only one with install commands. A non-ancient GCC (4.8+) or Clang (3.x+) is required because the codebase uses C++11 extensively. The make fallback only builds the CLI tool.

To build with CMake, configure and build from the repository root:

bash
cmake -B build -DSPIRV_CROSS_CLI=ON
cmake --build build

The SPIRV_CROSS_STATIC, SPIRV_CROSS_SHARED and SPIRV_CROSS_CLI options control which modules are built and installed. Setting SPIRV_CROSS_CLI=ON gives you the spirv-cross command line tool. The README does not document the CLI's argument list, so check the tool's own help output after building rather than assuming flags.

If you would rather not build from source, the README documents a vcpkg port:

bash
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install spirv-cross

That port is maintained by Microsoft team members and community contributors, and the README points at the vcpkg repository if the version lags.

For a library integration, the C++ path starts by including spirv_glsl.hpp, constructing a CompilerGLSL from a SPIR-V word vector, and calling compile(). The README's full example, reproduced in part here, shows the shape:

c++
#include "spirv_glsl.hpp"

spirv_cross::CompilerGLSL glsl(std::move(spirv_binary));
spirv_cross::ShaderResources resources = glsl.get_shader_resources();
spirv_cross::CompilerGLSL::Options options;
options.version = 310;
options.es = true;
glsl.set_common_options(options);
std::string source = glsl.compile();

What you should see is a GLSL ES 3.10 source string. If the shader uses a feature the backend does not handle, expect an error rather than a silently wrong translation.

The ABI Boundary: Why the C++ API Is Not the Stable One

This is the design decision most likely to bite an integrator. The README states directly that the C++ API is not guaranteed to be ABI-stable and recommends linking against it statically. It goes further: the C89-compatible wrapper in spirv_cross_c.h is described as fully stable in both API and ABI, and it is the only interface supported when SPIRV-Cross is built as a shared library.

That means a shared library build exposes the C API, not the C++ one. If you were planning to ship libspirv-cross.so and link C++ against it, the documented position is that you should not. Static linking of the C++ API, or the C wrapper, are the two supported shapes.

The C wrapper also changes memory management. Allocations live inside an spvc_context. You create it with spvc_context_create, parse SPIR-V with spvc_context_parse_spirv, and hand the parsed IR to a compiler with spvc_context_create_compiler using SPVC_BACKEND_GLSL and SPVC_CAPTURE_MODE_TAKE_OWNERSHIP. Most functions return an spvc_result where SPVC_SUCCESS is the only success code. The README advises destroying the context reasonably soon, or calling spvc_context_release_allocations() if you intend to reuse it. That is a manual lifetime model, and getting it wrong leaks the whole parse tree.

Where SPIRV-Cross Is the Wrong Tool

The README is candid that individual features are "mostly complete" but that obscure GLSL features may not be supported, and it characterises the remaining gaps as trivial improvements. Treat that as a project self-assessment, not a guarantee for your shader. The practical consequence is that an unusual construct can fail the conversion, and there is no documented fallback path in the README when it does.

The clearer boundary is direction and purpose. SPIRV-Cross is not a decompiler in the reverse-engineering sense. It reconstructs shader source, but the SPIR-V has already lost the original variable names, comments and structure. The output is functional and, by the project's stated aim, readable, but it is not the source that was compiled. If your goal is recovering someone else's original shader, this is the wrong instrument.

WGSL is also outside the documented backend list. The README names GLSL, MSL, HLSL, a JSON reflection format, and a deprecated C++ backend. If your target is WGSL, nothing in the README says SPIRV-Cross produces it, and you should verify that before designing around it.

Finally, the C++ backend is marked DEPRECATED in the feature list. Do not start new work against it.

SPIRV-Cross Against SPIRV-Reflect

SPIRV-Reflect is the natural comparison because it occupies adjacent territory: reflection over SPIR-V. The difference is scope. SPIRV-Reflect is a reflection library, and the README of SPIRV-Cross frames its own reflection API as a means to an end, describing it as simplifying the creation of Vulkan pipeline layouts. SPIRV-Cross also carries full translation backends for GLSL, MSL and HLSL, plus decoration modification through unset_decoration and set_decoration.

So the split is straightforward. If all you need is to enumerate descriptor sets, bindings and push constants to build a pipeline layout, a reflection-only library is a smaller dependency. If you need the same information and then need to emit shader source for another API, SPIRV-Cross does both in one pass over the same parsed IR. Choosing SPIRV-Reflect for a cross-API port means adding a second dependency later.

Note that the README does not benchmark either tool or compare output quality. The comparison above is about feature scope as documented, not about which produces better code.

Licence, Build Cost and Upgrade Exposure

SPIRV-Cross is Apache-2.0, and the repository carries a LICENSES/ directory alongside the top-level LICENSE file, which is the REUSE-style layout Khronos uses. Apache-2.0 includes an explicit patent grant, which matters for a component that sits between your engine and a graphics driver. That is a description of the licence text, not legal advice; if patent terms or attribution obligations affect your product, have counsel read the actual file.

The README header carries a CC-BY-4.0 notice for the documentation itself, separate from the code licence. If you redistribute the README text, that is the licence that applies to it.

Upgrade cost is where the ABI note turns into real work. The C++ API can change over time, so a static link means rebuilding and retesting when you move to a newer revision. The C wrapper is the stable surface, so a shared library deployment should be built against spirv_cross_c.h and spvc_result handling, not the C++ classes. The last push to the repository was on 2026-09-22, so the codebase is moving; the most recent tagged release listed is MoltenVK-1.1.5 from 2021-08-30, which means tags do not track the pace of commits. Pin a commit hash rather than a tag if you need reproducible builds.

Editorial conclusion

Adopt SPIRV-Cross if you ship Vulkan or SPIR-V and need GLSL, MSL or HLSL output plus reflection for pipeline layout creation. Do not adopt it as a general SPIR-V decompiler for reverse engineering, and do not rely on the C++ API as a stable ABI boundary: link it statically or use the C wrapper. Before committing, confirm your shader stages are covered by the documented vertex, fragment, tessellation, geometry and compute set, and check whether your target needs WGSL, which is not among the documented backends.

Frequently asked questions

What is SPIRV-Cross?

It is a tool and library from the Khronos Group for parsing SPIR-V and converting it to other shader languages. The README lists GLSL, Metal Shading Language, HLSL and a JSON reflection format as outputs.

How do I use SPIRV-Cross?

Build it with CMake, which the README says is the only build system tested in continuous integration, and enable SPIRV_CROSS_CLI to get the command line tool. For library use, construct a compiler such as CompilerGLSL from a SPIR-V word vector, optionally set options, and call compile().

Is SPIRV-Cross platform independent?

The README states it has been tested on Linux, iOS/OSX, Windows and Android. CMake is the recommended build system on all of them, and on Android the README says it is only useful as a library, linked through the CMake build.

Official sources

  1. Issues
  2. KhronosGroup/SPIRV-Cross on GitHub
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/khronosgroup-spirv-cross.svg)](https://hysenlabs.com/projects/khronosgroup-spirv-cross)