<div dir="ltr"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>This
 is something that I have considered doing on multiple occasions and 
have given a significant amount of thought.  Unfortunately, it has never
 made it particularly high on my priority list so no code has been 
written.  However, there have been a number of people who have brought 
this up lately so I thought I would do a brain-dump on the topic in the 
hopes of saving someone some effort.<br><br></div>Why?<br><br></div>That's as good a place to start as any.  I think there a re a number of reasons why this may be a useful thing to do:<br><br></div> 1)
 Provide another GLSL -> SPIR-V compiler.  Right now, the only thing 
in that space is GLSLang and, while it seems to be servicing the need 
ok, it also means that drivers tend not to implement SPIR-V support so 
much as GLSLang support.  SPIR-V provides a decent amount of freedom in 
how you do things and GLSLang tends to only do them one way.  This means
 that the other 10 ways are untested in most drivers.  Having another 
SPIR-V producer out there would help the ecosystem.<br><br></div> 2) 
Optimizations.  One thing that I've heard developers asking for is some 
basic platform-agnostic optimizations specifically to reduce SPIR-V code
 size.  Our compiler stack already has quite a few optimizations that 
developers may want such as copy propagation, dead code elimination, 
constant folding, loop unrolling, CSE, etc.  In a GLSL -> SPIR-V 
translator, we would probably want these to be optional, but it wouldn't
 be hard to provide them as flags.<br><br></div> 3) Bootstrapping 
GL_ARB_spirv.  The biggest barrier here is going to be testing and 
convincing ourselves that we've done everything right.  However, if we 
did GLSL -> SPIR-V -> NIR -> back-end, then we would be able to
 exercise both the GLSL -> SPIR-V path and the SPIR-V -> NIR path 
with the full piglit suite.  Some people have suggested modifying 
shader_runner.  However,  I think that if we simply did the SPIR-V 
translation inside the driver, it would be much easier to test things 
like non-SSO because we could go into SPIR-V after linking.<br><br></div> 4) Better testing spirv_to_nir.  See above.<br><br></div> 5)
 Better spec compliance.  I don't know the glslang code base well enough
 to speak particularly strongly about it's correctness.  What I do know,
 however, is that the GLSL compiler in mesa is used in production-grade 
drivers that pass piglit, the Open GL 4.5 CTS, the Open GL ES 3.2 CTS, 
and the Android CTS.  That's not something you can say about GLSLang.  I
 think we should make that available to game devs wishing to target 
Vulkan.<br><br></div>Where?<br><br></div>This is a question that I have 
internally debated for some time.  When we first started working on 
Vulkan, we boot-strapped it by adding some hacks to the GLSL compiler to
 provide the descriptor set bindings and just used GLSL.  When it came 
time for SPIR-V support, I chose to go directly into NIR.  At the time, I
 thought that SPIR-V was going to end up being a fairly low-level IR and
 that NIR would be a better match.  However, as things turned out, 
SPIR-V (at least for graphics) is more of a binary form of GLSL than 
anything else.  In retrospect, there are a number of things (dealing 
with built-ins comes to mind) which would have been easier had we gone 
SPIR-V -> GLSL.  However, I still stand by my original decision for 
two reasons:<br><br></div> 1) As we try to add some of these "advanced 
compute" features that people keep talking about, I think NIR will be a 
better and better fit.  Trying to retrofit some of those things into 
GLSL may be more trouble than it's worth.<br><br></div> 2) Going 
directly into NIR meant that we didn't have to pull in mtypes.h.  NIR is
 a fairly stand-alone chunk of the mesa tree and we have pulled our i965
 back-end compiler out so it only depends on NIR.  This makes the Vulkan
 driver much smaller and keeps it separate from the GL state tracker.<br><br></div>When
 considering a GLSL -> SPIR-V path there is some question about 
whether it should be GLSL -> NIR -> SPIR-V or GLSL -> SPIR-V 
directly.  There are pros and cons to both:<br><br></div> 1) SPIR-V 
would actually make a reasonable serialization format for NIR.  It's 
maybe not as compact as we could do if we made our own, but it's not 
bad.<br><br></div> 2) NIR is, in some ways simpler than GLSL IR so it may be a bit easier.  Unfortunately, that's not always a help...<br><br></div> 3)
 Because NIR is simpler, it requires a lot of lowering.  There are 
several things such as block variables (for UBOs and SSBOs) that NIR 
doesn't know how to handle.  It assumes that it just gets 
load_ubo(index, offset).  NIR also doesn't handle GLSL built-ins.  For 
SPIR-V -> NIR, those things are lowered a way on-the-fly.  This means
 that we would have to add some things to NIR and the SPIR-V code you 
get out would necessarily be lower-level than what glslang produces.<br><br></div> 4) As I pointed out above, GLSL is a bit better fit than NIR in a lot of ways.<br><br></div> 5) If we did NIR -> SPIR-V, we could also compile ARB programs to SPIR-V.  Maybe that's a downside?<br><br></div><div>All
 in all, my recommendation would be to do GLSL -> SPIR-V directly.  I
 think there's less of an impedance mismatch there and I think it would 
be easier to handle some of the higher-level things such as built-ins 
and block variables.<br></div><div><br></div>How?<br><br></div>This is 
something that I've also given quite a bit of thought. :-)  You could 
even say that I've written a decent chunk of it in my head.  Too bad I 
haven't typed it into a computer yet. :-(<br><br></div><div>The first 
thing I would do would be to write some sort of SPIR-V builder and put 
it in src/compiler/spirv.  It have multiple dword streams going at a 
time (SPIR-V has multiple sections) to handle things such as types, 
constants, etc. as well as function bodies.  Then you would have 
functions which would generate SPIR-V opcodes similar to nir_builder.h. 
 For Types, you would have a single spv_type function that turned a 
glsl_type pointer into a SPIR-V ID.  You would want to have this backed 
by a hash map so that they aren't re-emitted all the time.  For the rest
 of the opcodes, SPIRV-Headers project on the Khronos github project has
 a JSON representation of SPIR-V so it shouldn't be hard to write a 
little python code that generates most of the "build an opcode" 
functions similar to what we do for nir_builder_opcodes.h.<br><br></div><div>Finally,
 you would write the GLSL -> SPIR-V (or NIR -> SPIR-V) pass on top
 of that.  If you did GLSL -> SPIR-V, you could probably look at 
glsl_to_nir for inspiration.<br><br></div><div>Ok, there's my 
brain-dump.  It's on the list now so anyone who wants to pick up the 
project has it.  I'm happy to chat further via e-mail or IRC about 
different approaches but I think I've given a fairly complete picture of
 my thoughts at the moment.  Happy Hacking!<br><br></div>--Jason</div></div>