From 58475a8aa42284722a3763aa3bde49f2fa40366e Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Fri, 2 Feb 2018 10:38:22 -0800 Subject: Revamp documentation (#395) - Remove references to building by embedding source (not recommended at this point) - Push users more toward binary builds rather than building from source (but include a document that talks about how to build) - Remove most (all?) references to supporting GLSL input - Expand the language guide to talk about the new features - Add a document that talks about the parameter layout algorithm --- docs/api-users-guide.md | 41 +------- docs/building.md | 18 ++++ docs/command-line-slangc.md | 28 +----- docs/language-guide.md | 209 ++++++++++++++++++++++++++++++++++------ docs/layout.md | 228 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 433 insertions(+), 91 deletions(-) create mode 100644 docs/building.md create mode 100644 docs/layout.md (limited to 'docs') diff --git a/docs/api-users-guide.md b/docs/api-users-guide.md index 6f89852e9..a449ab4c8 100644 --- a/docs/api-users-guide.md +++ b/docs/api-users-guide.md @@ -8,30 +8,7 @@ Preliminaries ------------- Before using the Slang API, you'll need to link Slang into your application. -There are multiple ways you can do this: - -### Integrating the source into your build - -By far the simplest option if you just want to get up and running quickly, without fussing with dynamic libraries, is to integrate the Slang source files into your build. -The Slang project is packaged in a way that tries to make this easy: - -* Either clone the Slang repository, or download a source release -* Add the root folder of the repository/release to your include path -* In one `.cpp` file in your path, make sure to define `SLANG_INCLUDE_IMPLEMENTATION` before including `slang.h`: - -```c++ -#define SLANG_INCLUDE_IMPLEMENTATION -#include -``` - -This causes the `slang.h` header to `#include` all the source files that make up the Slang compiler implementation. - -Note that this option does *not* currently include support for generating SPIR-V output. -You can of course download or buld `slang-glslang` separately, if you want Slang's SPIR-V output, or you can just have Slang generate GLSL, and hten pass that on to whatever tool(s) you are using for GLSL today. - -### Binary releases - -Pre-built binary packages for the stand-alone Slang compiler and a DLL of the Slang library are available through GitHub [releases](https://github.com/shader-slang/slang/releases). +We recommend using a pre-built binary package, available through GitHub [releases](https://github.com/shader-slang/slang/releases). Just add the downloaded package to your include path, and make sure to add (or copy) the `slang.dll` and `slang-glslang.dll` libraries into the path of your executable. @@ -42,22 +19,6 @@ When using a binary release, you'll need to define the `SLANG_DYNAMIC` macro to #include ``` -### Building from source - -If you would like to build Slang from source, you will currently need Visual Studio 2015. -Clone [this](https://github.com/shader-slang/slang) repository, and then run: - - git submodule update --init - -Next, open `slang.sln` and build your desired platform/configuration. - -As with the binary distriution, you'll need to set the `SLANG_DYNAMIC` option when including the Slang header: - -```c++ -#define SLANG_DYNAMIC -#include -``` - Getting Started with the API ---------------------------- diff --git a/docs/building.md b/docs/building.md new file mode 100644 index 000000000..68e57ec2d --- /dev/null +++ b/docs/building.md @@ -0,0 +1,18 @@ +# Building Slang From Source + +## Get the Source Code + +Clone [this](https://github.com/shader-slang/slang) repository, and then run: + + git submodule update --init + +The submodule update step is required to pull in the copy of the `glslang` compiler that we currently use for generating SPIR-V. + +## Using Visual Studio + +Building from source is really only well supported for Windows users with Visual Studio 2015 or later. +If you are on Windows, then open `slang.sln` and build your desired platform/configuration. + +## Linux + +For Linux, we include a simple `Makefile`, but it is not designed to be used for active development (e.g., dependency tracking is not handled). diff --git a/docs/command-line-slangc.md b/docs/command-line-slangc.md index 1762874c0..8f27a3dbc 100644 --- a/docs/command-line-slangc.md +++ b/docs/command-line-slangc.md @@ -11,19 +11,9 @@ slangc [] [...] Simple Examples --------------- -### GLSL - -To compile a GLSL fragment shader and write SPIR-V assembly to the console: - - slangc my-shader.frag - -To output binary SPIR-V to disk, use: - - slangc my-shader.frag -o my-shader.spv - ### HLSL -When compiling an HLSL shader, you must also specify the "profile" to use. +When compiling an HLSL shader, you must specify the path to your shader code file as well as the "profile" to use. To see D3D bytecode assembly for a fragment shader entry point: slangc my-shader.hlsl -profile ps_5_0 @@ -49,18 +39,11 @@ To get SPIR-V assembly: slangc my-shader.slang -profile ps_5_0 -entry main -target spriv The code generation target is implicit when writing to a file with an appropriate extension. -To write DXBC and SPIR-V to files, use: +To write DXBC, SPIR-V, or GLSL to files, use: slangc my-shader.slang -profile ps_5_0 -entry main -o my-shader.dxbc slangc my-shader.slang -profile ps_5_0 -entry main -o my-shader.spv - -Cross-Compilation ------------------ - -When the input file is written in Slang (or the subset of HLSL that Slang currently understands) it is possible to cross-compile to GLSL (and on to SPIR-V), e.g.: - slangc my-shader.slang -profile ps_5_0 -entry main -o my-shader.glsl - slangc my-shader.slang -profile ps_5_0 -entry main -o my-shader.spv Multiple Entry Points --------------------- @@ -91,7 +74,7 @@ For completeness, here are the options that `slangc` currently accepts: * If no `` is specified, Slang will define the macro with an empty value * `-entry `: Specify the name of the entry-point function - * In single-file compiles for HLSL/GLSL, this defaults to `main` + * In single-file compiles for HLSL, this defaults to `main` * Multiple `-entry` options may appear on the command line. When they do, the input file path, `-profile` option, and `-o` option that apply for an entry point are each the first one found when scanning to the left from the `-entry` option. * `-I `: Add a path to be used in resolving `#include` and `__import` operations @@ -110,8 +93,6 @@ For completeness, here are the options that `slangc` currently accepts: * Profiles of the form `glsl_{vertex,tess_control,tess_eval,geometry,fragment,compute}_` are supported for all GLSL language versions where the corresponding stage is supported (e.g., there is a `glsl_fragment_110`, but the earliest compute profile is `glsl_compute_430`) * As a convenience profiles of the form `glsl_{vertex,tess_control,tess_eval,geometry,fragment,compute}` are provided that are intended to map to the latest version of GLSL known to the Slang compiler (curently `450`) -* `-no-checking`: Disable semantic checking as much as possible, or all files and entry points specified. Has no effect on Slang files, but will suppress many of Slang's error checks on HLSL/GLSL files. You may still get error messages when the code in those files is passed along to a downstream compiler like `fxc` or `glslang`. - * `-target `: Specifies the desired code-generation target. Values for `` are: * `glsl`: GLSL source code * `hlsl`: HLSL source code @@ -119,7 +100,6 @@ For completeness, here are the options that `slangc` currently accepts: * `spirv-assembly`: SPIR-V intermediate language assembly * `dxbc`: Direct3D shader bytecode binary * `dxbc-assembly`: Direct3D shader bytecode assembly - * `reflection-json`: A dump of shader parameter information in JSON format. This is only intended for using in debugging/testing at present. * `none`: Don't generate output code (but still perform front-end parsing/checking) * `--`: Stop parsing options, and treat the rest of the command line as input paths @@ -127,5 +107,5 @@ For completeness, here are the options that `slangc` currently accepts: Limitations ----------- -A major limitation of the `slangc` command today is that there is no provision for getting both compiled code *and* reflection data out in a single invocation. +A major limitation of the `slangc` command today is that there is no provision for getting reflection data out along with the compiled shade rcode. For now, the command-line tool is best seen as a debugging/testing tool, and all serious applications should drive Slang through the API. diff --git a/docs/language-guide.md b/docs/language-guide.md index a388e70f3..2cced92aa 100644 --- a/docs/language-guide.md +++ b/docs/language-guide.md @@ -19,19 +19,18 @@ Big-picture stuff that is supported: * The built-in templated resource types like `Texture2D` with their object-oriented syntax for sampling operations * Attributes like `[unroll]` are parsed, and passed along for HLSL/DXBC output, but dropped for other targets * `struct` types that contain textures/samplers as well as ordinary uniform data, both as function parameters and in constant buffers +* The built-in functions up through Shader Model 6.0 (as documented on MSDN) are supported New Features ------------ -Right now the Slang language only has one major feature that is different from existing HLSL (that will change over time). - ### Import Declarations -In order to support better software modularity, and also to deal with the issue of how to integrate shader libraries written in Slang into other langauges, Slang introduces an `__import` declaration construct. +In order to support better software modularity, and also to deal with the issue of how to integrate shader libraries written in Slang into other langauges, Slang introduces an `import` declaration construct. The basic idea is that if you write a file `foo.slang` like this: -``` +```hlsl // foo.slang float4 someFunc(float4 x) { return x; } @@ -39,61 +38,217 @@ float4 someFunc(float4 x) { return x; } you can then import this code into another file in Slang, HLSL, or GLSL: -``` -// bar.glsl +```hlsl +// bar.slang -__import foo; +import foo; -vec4 someOtherFunc(vec4 y) { return someFunc(y); } +float4 someOtherFunc(float4 y) { return someFunc(y); } ``` -The simplest way to think of it is that the `__import foo` declaration instructs the compiler to look for `foo.slang` (in the same search paths it uses for `#include` files), and give an error if it isn't found. +The simplest way to think of it is that the `import foo` declaration instructs the compiler to look for `foo.slang` (in the same search paths it uses for `#include` files), and give an error if it isn't found. If `foo.slang` is found, then the compiler will go ahead and parse and type-check that file, and make any declarations there visible to the original file (`bar.glsl` in this example). -When it comes time to generate output code, Slang will output any declarations from `__import`ed files that were actually used (it skips those that are never referenced), and it will cross-compile them as needed for the chosen target. +When it comes time to generate output code, Slang will output any declarations from `import`ed files that were actually used (it skips those that are never referenced), and it will cross-compile them as needed for the chosen target. -A few other details worth knowing about `__import` declarations: +A few other details worth knowing about `import` declarations: -* The name you use on the `__import` line gets translated into a file name with some very simple rules. An underscore (`_`) in the name turns into a dash (`-`) in the file name, and dot separators (`.`) turn into directory seprators (`/`). After these substitutions, `.slang` is added to the end of the name. +* The name you use on the `import` line gets translated into a file name with some very simple rules. An underscore (`_`) in the name turns into a dash (`-`) in the file name, and dot separators (`.`) turn into directory seprators (`/`). After these substitutions, `.slang` is added to the end of the name. -* If there are multiple `__import` declarations naming the same file, it will only be imported once. This is also true for nested imports. +* If there are multiple `import` declarations naming the same file, it will only be imported once. This is also true for nested imports. * Currently importing does not imply any kind of namespacing; all global declarations still occupy a single namespace, and collisions between different imported files (or between a file and the code it imports) are possible. This is a bug. -* If file `A.slang` imports `B.slang`, and then some other file does `__import A;`, then only the names from `A.slang` are brought into scope, not those from `B.slang`. This behavior can be controlled by having `A.slang` use `__exported __import B;` to also re-export the declarations it imports from `B`. +* If file `A.slang` imports `B.slang`, and then some other file does `import A;`, then only the names from `A.slang` are brought into scope, not those from `B.slang`. This behavior can be controlled by having `A.slang` use `__exported import B;` to also re-export the declarations it imports from `B`. + +* An import is *not* like a `#include`, and so the file that does the `import` can't see preprocessor macros defined in the imported file (and vice versa). Think of `import foo;` as closer to `using namspace foo;` in C++ (perhaps without the same baggage). + +### Explicit Parameter Blocks + +One of the most important new features of modern APIs like Direct3D 12 and Vulkan is an interface for providing shader parameters using efficient *parameter blocks* that can be stored in GPU memory (these are implemented as descritpor tables/sets in D3D12/Vulkan, and "attribute buffers" in Metal). +However, HLSL and GLSL don't support explicit syntax for parmaeter blocks, and so shader programmers are left to manually pack parameters into blocks either using `register`/`layout` modifiers, or with API-based remapping (in the D3D12 case). + +Slang supports a simple and explicit syntax for exploiting parameter blocks: + +```hlsl +struct ViewParams +{ + float3 cameraPos; + float4x4 viewProj; + TextureCube envMap; +}; + +ParameterBlock gViewParams; +``` + +In this example, the fields of `gViewParams` will be assigned to registers/bindings in a way that supports allocating them into a single parameter block. +For example, when generating GLSL for Vulkan, the Slang compiler will generate a single `uniform` block (for `cameraPos` and `viewProj`) and a global `textureCube` for `envMap`, both decorated with the same `layout(set = ...)`. + + +### Interfaces -* An import is *not* like a `#include`, and so the file that does the `__import` can't see preprocessor macros defined in the imported file (and vice versa). Think inf `__import foo;` as closer to `using namspace foo;` in C++ (perhaps without the same baggage). +Slang supports declaring `interface`s that user-defined `struct` types can implement. +For example, here is a simple interface for light sources: -On that last point, if you really do want something that is like `__import` but interacts with the preprocessor more like `#include` then you can try using `#import`: +```hlsl +// light.slang +struct LightSample { float3 intensity; float3 direction; }; + +interface ILight +{ + LightSample sample(float3 position); +} ``` -#import "foo.slang" -... + +We can now define a simple user type that "conforms to" (implements) the `ILight` interface: + +```hlsl +// point-light.slang + +import light; + +struct PointLight : ILight +{ + float3 position; + float3 intensity; + + LightSample sample(float3 hitPos) + { + float3 delta = hitPos - position; + float distance = length(delta); + + LightSample sample; + sample.direction = delta / distance; + sample.intensity = intensity * falloff(distance); + return sample; + } +} ``` -The `#import` directive is recognized during preprocessing, so macro definitions from the importing file will affect the imported code, and vice versa. -The rules about multiple imports still apply, though, so only the first `#import` encountered will determine the text that is parsed (be careful). -Please think of `#import` as a stopgap for when you want the cross-compilation benefits of `__import`, but need to deal with code that depends on macros in the here and now. +### Generics + +Slang supports *generic* declarations, using the commong angle-brack (`<>`) syntax from languages like C#, Java, etc. +For example, here is a generic function that works with any type of light: + +```hlsl +// diffuse.slang +import light; + +float4 computeDiffuse( float4 albedo, float3 P, float3 N, L light ) +{ + LightSample sample = light.sample(P); + float nDotL = max(0, dot(N, sample.direction)); + return albedo * nDotL; +} +``` + +The `computeDiffuse` function works with any type `L` that implements the `ILight` interface. +Unlike with C++ templates, the `computeDiffuse` function can be compiled and type-checked once (you won't suddenly get unexpected error messages when plugging in a new type). + +#### Global-Scope Generic Parameters + +Putting generic parameter directly on functions is helpful, but in many cases existing HLSL shaders declare their parameters at global scope. +For example, we might have a shader that uses a global declaration of material parameters: + +```hlsl +Material gMaterial; +``` + +In order to allow such a shader to be converted to use a generic parameter for the material type (to allow for specialization), Slang supports declaring type parameters at the global scope: + +```hlsl +type_param M : IMaterial; +M gMaterial; +``` + +Conceptually, you can think of this syntax as wrapping your entire shader program in a generic with parameter ``. +This isn't beautiful syntax, but it may help when incrementally porting an existing HLSL codebase to use Slang's features. + +### Associated Types + +Sometimes it is difficult to define an interface because each type that implements it might need to make its own choice about some intermediate type. +As a concrete example, suppose we want to define an interface `IMaterial` for material surface shaders, where each material might use its own BRDF. +We want to support evaluating the *pattern* of the surface separate from the reflectance function. + +```hlsl +// A reflectance function +interface IBRDF +{ + float3 eval(float3 wi, float3 wo); +} +struct DisneyBRDF : IBRDF { ... }; +struct KajiyaKay : IBRDF { ... }; + +// a surface pattern +interface IMaterial +{ + ??? evalPattern(float3 position, float2 uv); +} +``` + +What is the type `???` that `evalPattern` should return? We know that it needs to be a type that supports `IBRDF`, but *which* type? +One material might want to use `DisneyBRDF` while another wants to use `KajiyaKay`. + +The solution in Slang, as in modern languages like Swift and Rust, is to use *associated types* to express the depdence of the BRDF type on the material type: + +```hlsl +interface IMaterial +{ + associatedtype B : IBRDF; + B evalPattern(float3 position, float2 uv); +} + +struct MyCoolMaterial : IMaterial +{ + typedef DisneyBRDF B; + DisneyBRDF evalPattern(float3 position, float2 uv) + { ... } +} +``` + +Associated types are an advanced concept, and we only recommend using them when they are needed to define a usable interface. + Future Extensions ----------------- -Longer term we would like to make Slang a much more advanced language, and indeed the implementation already has some of the underpinnings for more powerful things. +### Implicit Generics Syntax + +The syntax for generics and interfaces in Slang is currently explicit, but verbose: + +```hlsl +float4 computeDiffuse( L light, ... ) +{ ... } +``` + +As a future change, we would like to allow using an interface like `ILight` as an ordinary parameter type: -The most important feature we plan to add is support for "constrained generics" as they appear in C#, Rust, and Swift. -For those of you with a C++ background you could think of this as "templates + concepts", but without many of the rought edges. +```hlsl +float4 computeDiffuse( ILight light, ... ) +{ ... } +``` + +This simpler syntax would act like "syntactic sugar" for the existing explicit generics syntax, so it would retain all of the important performance properties. + +### Returning a Value of Interface Type + +While the above dealt with using an interface as a parameter type, we would eventually like to support using an interface as the *return* type of a function: + +```hlsl +ILight getALightSource(Scene scene) { ... } +``` -Using constrained generics as an underlying mechanism, we then plan to introduce a feature similar in capability to Cg's "interfaces" feature, to allow shaders to express more flexible patterns of composition and dispatch. +Implementing this case efficiently is more challenging. In most cases, an associated type can be used instead when an interface return type would be desired. Not Supported ------------- -Some things are not supported, but probably will be given enough time/resources: +Some features of the current HLSL language are not supported, but probably will be given enough time/resources: * Local variables of texture/sampler type (or that contain these) * Matrix swizzles -* Object-oriented extensions for putting methods inside `struct` types * Explicit `packoffset` annotations on members of `cbuffer`s Some things from HLSL are *not* planned to be supported, unless there is significant outcry from users: diff --git a/docs/layout.md b/docs/layout.md new file mode 100644 index 000000000..aecb22d70 --- /dev/null +++ b/docs/layout.md @@ -0,0 +1,228 @@ +Parameter Layout Rules +====================== + +An important goal of the Slang project is that the rules for how shader parameters get assigned to `register`s/`binding`s is completely deterministic, so that users can rely on the compiler's behavior. +This document will attempt to explain the rules that Slang employs at a high level. +Eventually it might evolve into a formal specification of the expected behavior. + +Guarantees +---------- + +The whole point of having a deterministic layout approach is the guarantees that it gives to users, so we will start by explicitly stating the guarantees that users can rely upon: + +* A single top-level shader parameter will always occupy a contiguous range of bindings/registers for each resource type it consumes (e.g., a contiguous range of `t` registers, a contiguous range of bytes in a `cbuffer`, etc.). + +* The amount of resources a parameter consumes depends only on its type, and top-level context in which it appears (e.g., is it in a `cbuffer`? an entry-point varying parameter? etc.). + +* A shader parameter that is declared the same way in two different programs will get the same *amount* of resources (registers/bytes) allocated for it in both programs, but it might get a different starting offset/register. + +* Changing the bodies of functions in shader code cannot change the layout of shader parameters. In particular, just because a shader parameter is "dead" does not mean it gets eliminated. + +* If the user doesn't use explicit `register`/`layout` modifiers to bind parameters, then each module will get a contiguous range of bindings, and the overall program will always use a contiguous range starting from zero for each resource type. + +Overview of the Layout Algorithm +-------------------------------- + +Layout is applied to a Slang *compile request* which comprises one or more *translation units* of user code, and zero or more `import`ed modules. +The compile request also specifies zero or more *entry points* to be compiled, where each entry point identifies a function and a profile to use. + +Layout is always done with respect to a chosen *target*, and different targets might compute the resource usage for types differently, or apply different alignment. +Within a single target there may also be different layout rules (e.g., the difference between GLSL `std140` and `std430`). + +Layout proceeds in four main phases: + +1. Establish a global ordering on shader parameters +2. Compute the resource requirements of each shader parameter +3. Process shader parameters with fixed binding modifiers +4. Allocate bindings to parameter without fixed binding modifiers + +Ordering (and Collapsing) Shader Parameters +------------------------------------------- + +Shader parameters from the user's code always precede shader parameters from imported modules. +The order of parameters in the user's code is derived by "walking" through the code as follows: + +* Walk through each translation unit in the order they were added via API (or the order they were listed on the command line) + +* Walk through each source file of a translation unit in the order they were added/listed + +* Walk through global-scope shader paramter declarations (global variables, `cbuffer`s, etc.) in the order they are listed in the (preprocessed) file. + +* After all global parameters for a translation unit have been walked, walk through any entry points in the translation unit. + +* When walking through an entry point, walk through all of its function parameters (both uniforms and varyings) in order, and then walk the function result as a varying output parameter. + +When dealing with global-scope parameters in the user's code, it is possible for the "same" parameter to appear in multiple translation units. +Any two global shader parameters in user code with the same name are assumed to represent the same parameter, and will only be included in the global order at the first location where they are seen. +It is an error for the different declarations to have a mismatch in type, or conflicting explicit bindings. + +Parameters from `import`ed modules are enumerated after the user code, using the order in which modules were first `import`ed. +The order of parameters within each module is the same as when the module was compiled, which matches the ordering given above. + +Computing Resource Requirements +------------------------------- + +Each shader parameter computes its resource requirements based on its type, and how it is declared. + +* Global-scope parameters, entry point `uniform` parameters, and `cbuffer` decalrations all use the "default" layout rules + +* Entry point non-`uniform` parameters use "varying" layout rules, either input or output + +* A few other special case rules exist (e.g., for laying out the elements of a `StructuredBuffer`), but most users will not need to worry about these + +Note that the "default" rules are different for D3D and GL/Vulkan targets, because they have slightly different packing behavior. + +### Plain Old Data + +Under the default rules simple scalar types (`bool`, `int`, `float`, etc.) are laid out as "uniform" data (that is, bytes of ordinary memory). +In most cases, the size matches the expected data type size (although be aware that most targets treat `bool` as a synonym for `int`) and the alignment is the same as the size. + +### Vectors + +Vectors are laid out as N sequential scalars. +Under HLSL rules, a vector has the same alignment as its scalar type. +Under GLSL `std140` rules, a vector has an alignment that is its size rounded up to the next power of two (so a `float3` has `float4` alignment). + +### Opaque Types + +"Opaque" types include resource/sampler types like `Texture2D` and `SamplerState`. +These consume a single "slot" of the appropriate category for the chosen API. + +Note that when compiling for D3D, a `Texture2D` and a `SamplerState` will consume different resources (`t` and `s` registers, respectively), but when compiling for Vulkan, they both consume the same resource ("descriptor table slot"). + +Opaque types currently all have an alignment of one. + +### Structures + +A structure is laid out by initializing a counter for each resource type, and then processing fields sequential (in declaration order): + +* Compute resource usage for the field's type + +* Adjust counters based on the alignment of the field for each resource type where it has non-zero usage + +* Assign an offset to the field for each resource type where it has non-zero usage + +* Add the resource usage of the field to the counters + + +An important wrinkle is that when doing layout for HLSL, we must ensure that if a field with uniform data that is smaller than 16 bytes would straddle a 16-byte boundary, we advance to the next 16-byte aligned offset. + +The overall alignment of a `struct` is the maximum alignment of its fields or the default alignment (if it is larger). +The default alignment is 16 for both D3D and Vulkan targets. + +The final resource usage of a `struct` is rounded up to a multiple of the alignment for each resource type. Note that we allow a `struct` to consume zero bytes of uniform storage. + +It is important to note that a `struct` type can use resources of many different kinds, so in general we cannot talk about the "size" of a type, but only its size for a particular kind of resource (uniform bytes, texture registers, etc.). + +### Sized Arrays + +For uniform data, the size of the element type is rounded up to the target-specific minimum (e.g., 16 for D3D and Vulkan constant buffers) to arrive at the *stride* of the array. The total size of the array is then the stride times the element count. + +For opaque resource types, the D3D case simply takes the stride to be the number of registers consumed by each element, and multiplies this by the element count. + +For Vulkan, an array of resources uses only a single `binding`, so that the stride is always zero for these resource kinds, and the resource usage of an array is the same as its element type. + +### Unsized Arrays + +The uniform part of an unsized array has the same stride as for the sized case, but an effectively infinite size. + +For register/binding resource usage, a Vulkan unsized array is just like a sized one, while a D3D array will consume a full register *space* instead of individual registers. + +### Constant Buffers + +To determine the resource usage of a constant buffer (either a `cbuffer { ... }` declaration or a `ConstantBuffer`) we look at the resource usage of its element type. + +If the element uses any uniform data, the constant buffer will use at least one constant-buffer register (or whatever the target-specific resource is). +If the element uses any non-uniform data, that usage will be added to that of the constant buffer. + +### Parameter Blocks + +A parameter block is similar to a constant buffer. +If the element type uses any uniform data, we compute resource usage for a constant buffer. +We then add in any non-uniform resource usage for the element types. + +If the target requires use of register spaces (e.g., for Vulkan), then a parameter block uses a single register space; otherwise it exposes the resource usage of its element type directly. + +Processing Explicit Binding Modifiers +------------------------------------- + +If the user put an explicit binding modifier on a parameter, and that modifier applies to the current target, then we use it and "reserve" space in the overall binding range. + +Traditional HLSL `register` modifiers only apply for D3D targets. +Slang currently allows GLSL-style `layout(binding =...)` modifiers to be attached to shader parameters, and will use those modifiers for GL/Vulkan targets. + +If two parameters reserve overlapping ranges, we currently issue an error. +This may be downgraded to a warning for targets that support overlapping ranges. + +Allocating Bindings to Parameters +--------------------------------- + +Once ranges have been reserved for parameters with explicit bindings, the compiler goes through all parameters again, in the global order and assigns them bindings based on their resource requirements. + +For each resource type used by a parameter, it is allocated the first contiguous range of resources of that type that have not been reserved. + +Splitting of Arrays +------------------- + +In order to support `struct` types that mix uniform and non-uniform data, the Slang compiler always "splits" these types. +For example, given: + +```hlsl +struct LightInfo { float3 pos; Texture2D shadowMap; }; + +LightInfo gLight; +``` + +Slang will generate code like: + +```hlsl +float3 gLight_pos; +Texture2D gLight_shadowMap; +``` + +In a simple case like the above, this doesn't affect layout at all, but once arrays get involved, the layout can be more complicated. Consider this case: + +```hlsl +struct Pair { Texture2D a; Texture2D b; }; +Pair gPairs[8]; +``` + +The output from the splitting step is equivalent to: + +```hlsl +Texture2D gPairs_a[8]; +Texture2D gPairs_b[8]; +``` + +While this transformation is critical for having a type layout algorithm that applies across all APIs (and also it is pretty much required to work around various bugs in downstream compilers), it has the important down-side that the value `gPairs[0]` does not occupy a contiguous range of registers (although the top-level shader parameter `gPairs` *does*). + +The Slang reflection API will correctly report the information about this situation: + +* The "stride" of the `gPairs` array will be reported as one, because `gPairs[n+1].a` is always one register after `gPairs[n].a`. + +* The offset of the `gPairs.b` field will be reported as 8, because `gPairs[0].b` will be 8 registers after the starting register for `gPairs`. + +The Slang API tries to provide the best information it can in this case, but it is still important for users who mix arrays and complex `struct` types to know how the compiler will lay them out. + +Generics +-------- + +Generic type parameters complicate these layout rules. +For example, we cannot compute the exact resource requirements for a `vector` without knowing what the type `T` is. + +When generic parameters are used in a program, the layout rules are ammended as follows: + +* Identify those parameters with types that *depend on* a generic parameter + * A generic type parameter depends on itself + * An instantiation of a generic type `F` depends on a generic parameter if `A` does + * An aggregate (`struct`) type depends on a generic type if the type of any of its fields does + +* Split the global parameter order into two lists: one for parameters that do not depend on generic parameters, and one for parameters that do + +* When enumerating shader parameters, always enumerate the non-generic-dependent parameter first + +This choie ensures that plugging in different types for the generic type parameters of an entry point will not change the layout that was computed for any "ordinary" parameters that did not depend on the generics. + +Note that we do *not* currently apply this same logic to `struct` types (e.g., lay out all the non-generic-dependent fields before the generic-dependent ones). +At present, we think it is valuable for a specialization of a generic type to lay out like an "equivalent" non-generic type, but we may consider changing that decision. +To avoid problems, users are encourage to declare types so that all non-generic-dependent fields come before generic-dependent ones. -- cgit v1.2.3