<div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Jan 8, 2020 at 7:55 PM Luke Kenneth Casson Leighton <<a href="mailto:lkcl@lkcl.net">lkcl@lkcl.net</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br><br>On Thursday, January 9, 2020, Jason Ekstrand <<a href="mailto:jason@jlekstrand.net" target="_blank">jason@jlekstrand.net</a>> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><u></u>

<div>
<div dir="auto">
<div dir="auto">Drive-by comment:</div></div></div></blockquote><div><br></div><div>really appreciate the feedback.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div dir="auto"><div dir="auto"> I don't think you actually want to base any decisions an a vec4 architecture. Nearly every company in the graphics industry thought that was a good idea and designed vec4 processors. Over the course of the last 15 years or so they have all, one by one, realized that it was a bad idea and stopped doing it. Instead, they all parallelize the other way and their SIMD instructions and on scalar values across 8, 16, 32, or 64 invocations (vertices, pixels, etc.) Of the shader program.</div></div></div></blockquote><div><br></div><div>for simplicity (not outlining nearly 18 months of Vector Architecture ISA development) i missed out that we have designed a vector-plus-subvector architecture, where the subvectors may be of any length between 1 and 4, and there is an additional vector loop around that which may be from length 1 (scalar) to 64 </div><div><br></div><div>individual predicate mask bits may be applied to vector however they may only be applied (one bit) per subvector.</div><div><br></div><div>discussions have been ongoing for around 2 years now on the LLVM dev lists to support this type of concept.</div><div><br></div><div>on the libre soc lists we have also had detailed discissions on how to do swizzles at the subvector level.</div><div><br></div><div>AMDGPU, NVIDIA, MALI, they all support these capabilities.</div><div><br></div><div>to be clear: we have *not* designed an architecture or an ISA which critically and exclusively depends on vec4 and vec4 alone.</div><div><br></div><div>now, whether it is a bad idea or not to have vec2, vec3 snd vec4 capability, the way that i see it is that Vulkan supports them, as does the SPIRV compiler in e.g. AMDVLK, and we would be asking for trouble (performance penalties, compiler complexity due to having to add predicated autovectorisation) if we did not support them.</div></blockquote><div><br></div><div>Now that I'm at a keyboard and not a phone, I can provide a more thorough explanation.  Hopefully that will be helpful.  First of all, let's start with what SPIR-V supporting vec2/3/4 means.  It is true that a vec3 is a native SPIR-V type; it is also a native type in GLSL and HLSL.  Those languages also support matrices, write-masking of results, and swizzling on sources.  This stuff is all very useful when writing graphics shaders because 70% of what you do in graphics is some sort of vector math.  Having constructs built directly into the language is really nice.  When it comes to SPIR-V specifically, you should think of it much more like a high-level language than like an IR; it's intentionally designed to lose as little high-level information as possible.  Most of the early shader architectures also had vec2/3/4 as native data types and had swizzling and write-masking as core concepts; partly because it seemed like a good idea and partly because that's the way Microsoft's D3D9 bytecode format worked.  The reason why NIR supports vec4 is because there is a lot of hardware out there (including Intel from 5-6 years ago) which needs vec4 and some of that hardware is still very actively supported in Mesa.<br></div><div><br></div><div>On all modern architectures I'm familiar with (this includes Intel, NVIDIA, AMD, ARM, Imagination, and Qualcomm), everything is scalarized so a vec3 + vec3 add operation is turned into three scalar add operations.  In our NIR-based compilers, the scalarization usually happens almost immediately once you're in NIR.  We then run up to 64 invocations (vertex, pixel, etc.) at a time with wide hardware instructions.  When control-flow diverges (some invocations go one way and some another), both paths are executed and predication is used to disable the SIMD lanes for the invocations that took the other path.  On Intel and several other architectures, this happens fairly automatically.  On AMD, their management of predicates is much more manual.</div><div><br></div><div>What about vec4?  As I've said a couple of times, basically everyone in the industry (at least Intel, NVIDIA, AMD, ARM, Imagination, and Qualcomm) has done it at some point in the past.  Let's take Intel as a concrete example.  It's a good example because a) I'm familiar with it, b) it's all publicly documented and c) they did scalar and vec4 at the same time in the same ISA so it's really easy to look at the trade-offs.  On Intel, everything runs 8-wide (I'm simplifying a bit but it's good enough for this discussion).  Older Intel hardware could run in one of two modes depending on shader stage: SIMD8 or SIMD4x2.  In SIMD8 mode, each of the 8 lanes corresponds to a different shader invocation and each instruction acts on 8 scalars, one from each invocation.  In SIMD4x2 mode, it runs 2 invocations with 4 lanes per invocation.  The ISA has swizzles and write-masks so those 4 lanes can operate on an entire vec4 at a time.  There were even fancy cross-lane opcodes for things like dot products.  For a lot of simple operations, the SIMD4x2 mode was really slick.  If, for instance, you want to multiply a vec4 by a mat4x4, it's just 4 dot instructions or a MUL and 3 MAD depending on which way you're doing the multiply.</div><div><br></div><div>So why did everyone leave vec4?  It all comes down to ALU utilization.  Because of the way that predication is used to deal with divergent control-flow, you can end up executing more instructions than any single invocation needs.  As a worst-case example, suppose your shader starts with a big switch statement where each case is some non-trivial piece of code.  Further suppose that you have some shader thread where each invocation takes a different case in the switch.  Thanks to the way divergent control-flow has to be handled, you would end up effectively executing each invocation separately and nothing would be parallelized.  Depending on how well your compiler handles divergence and re-convergence and depending on the workload, the worst case is that you end up spending Nx the cycles where N is the number of invocations you run at once.  For Intel, that means as much as 8x in SIMD8 mode and as much as 2x in SIMD4x2 mode.  It looks like SIMD4x2 is winning, right?  Well, vec4 has its own utilization problems.  Even though the source language natively works in vec4, in practice shaders often end up doing a lot of scalar or vec2 calculations.  Every time you do a scalar calculation, you have 2 lanes which are doing work and 6 which are idle (using Intel SIMD4x2 as the example here). Yes, you can write a vectorizer to try and pack stuff together better but it turns out that's much harder than you'd think.  It's also harder to optimize in vec4 in general so those shaders are likely to use less efficient math.  I don't have hard numbers here but, if I had to give you a gut feeling from experience, I'd say that, for complex shaders (doing more than a vec4 x mat4x4), it's hard to get an average utilization better than 50-70%.   So why did everyone leave vec4?  Because, even though it's a bit counter-intuitive, divergence makes for less of a HW utilization problem than vec4.</div><div><br></div><div>Hopefully that makes the trade-off make more sense.  One other important factor is that, even if vec4 could, in theory, be more efficient, it's way easier to write a compiler if you scalarize everything.<br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>however, there are two nice things:</div><div><br></div><div>1. we are at an early phase, therefore we *can* evaluate valuable "headsup" warnings such as the one you give, jason (so thank you)</div></blockquote><div><br></div><div>Hooray!<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>2. as a flexible Vector Processor, soft-programmable, then over time if the industry moves to dropping vec4, so can we.</div></blockquote><div><br></div><div>That's very nice.  My primary reason for sending the first e-mail was that SwiftShader vs. Mesa is a pretty big decision that's hard to reverse after someone has poured several months into working on a driver and the argument you gave in favor of Mesa was that it supports vec4.  If you assume vec4 isn't an issue (which I'm arguing you should), maybe you would make a different decision.  Personally, I'm still a fan of Mesa. :-)  I also won't hold it against anyone if they like SwiftShader; it's a neat project too.<br></div><div><br></div><div>--Jason<br></div></div></div></div></div></div>