https://github.com/shader-slang/slang
Revision 71e35b6822b9e2846e129a888774d45a5e0827da authored by Tim Foley on 20 May 2019, 17:40:38 UTC, committed by GitHub on 20 May 2019, 17:40:38 UTC
* A few changes required for application adoption of interface-type parameters

There are a few small changes here that are all related in that they arose from trying to integrate support for specialization via global interface-type shader parameters into a real application.

Allow querying the "pending" layout via reflection API
------------------------------------------------------

The naming here isn't ideal, and could probably use a round of "bikeshedding" to arrive at something better, but the basic idea is that when you have a type like:

```
struct MyStuff
{
    int a;
    IFoo foo;
    int b;
}
```

the fields `a` and `b` get allocated space directly in the "primary" layout for `MyStuff` (at offsets 0 and 4, with `sizeof(MyStuff) == 8`), but the `foo` field can't be allocated space until we know what concrete type will get plugged in there.

If we have a concrete type in mind:

```
struct Bar : IFoo { int bar; }
```

then we can know how much space the `foo` field will take up, but we still can't allocate it space directly in `MyStuff`, because we already decided that `sizeof(MyStuff) == 8`.

Now imagine we place some `MyStuff` values into constant buffers:

```
cbuffer X {
    MyStuff x;
}

cbuffer Y {
    MyStuff y;
    float4 z;
}
```

In each case we know that we want to place the `MyStuff::foo` field at the end of the containing constant buffer so that it doesn't disrupt the layout of the existing fields. But that means that the offset of `MyStuff::foo` relative to the start of the `MyStuff` isn't fixed, because of unrelated fields like `z` that need to get in between.

In our layout code, we handle this by having a notion of a "pending" layout. Once we know how `MyStuff::foo` will be specialized, we can compute both a "primary" and a "pending" layout for `MyStuff`, which basically treats it as if it were two distinct types:

```
struct MyStuff_Primary
{
    int a;
    int b;
}

struct MyStuff_Pending
{
    Bar foo;
}
```

Layout for an aggregate type like the `X` or `Y` constant buffer then proceeds by computing an aggregate primary layout and an aggregate pending layout, and then finally a constant buffer or parameter block "flushes" all or part of the pending data by appending it to the primary data to get the final layout.

What all this means is that a type like `MyStuff` will have two different layouts (a default one for the primary data and a "pending" one for any specialized interface-type fields), and a variable like `Y::y` will also have two variable layouts that specify offsets (one set of offsets for its primary part, and one set of offsets for its pending part).

In order to handle interface-type fields with these layout rules, an application needs a way to query the "pending" part of a type or variable layout, which luckily gives it back just another type/variable layout. The API change here is minimal, although actually exploiting the new API correctly in application code could prove challenging.

Allow creating of explicitly specialized types
----------------------------------------------

This feature isn't actually implemented all the way through the compiler (I just needed enough to make the API calls go through), but I've added support for specializing a type that has interface-type fields through the reflection API. This maps to an `ExistentialSpecializedType` in the AST, and I'm lowering it to the IR as a `BindExistentialsType`, although that isn't 100% correct for the future.

This feature will require a future PR to actually flesh out the implementation work, but I'll wait until that is the sticking point on the application side before I do that.

Introduce a tiny `Hasher` abstraction
-------------------------------------

While implementing all the boilerplate for a new `Type` subclass (we really need to reduce that work...), I got fed up with how we do hash-code computation and introduced a small utility `Hasher` type that is intended to wrap up the idiom of combining hashes. For now this isn't a major change, but in the future I'd like to expand on the design a bit to clean up some of the warts around how we handle hashing:

* The `Hasher` implementation can and should switch from maintaining a single `HashCode` as its state to something that contains a more complete state (larger than the hash code) and just hashes new bytes into that state as it goes. This should make it possible to implement a `Hasher` for more serious hash functions, whether MD5, CityHash, or whatever we decide is good default.

* Things that are hashable shouldn't have a `getHashCode()` method, but instead should have something like a `hashInto(Hasher&)` method. This change would have the dual benefits that (1) a composite type can easily hash all the fields that contribute to its identity into the hasher with minimal fuss/boilerplate, and (2) the hashes for composite types will be of higher quality because they can exploit all the bits of the hasher's state to combine the fields, instead of restricting each sub-field to just the bits in a hash code.

We should be able to incrementally improve the quality of our design there over future changes, but for now it probably isn't a critical priority.

Fixes for legalization of existential types
-------------------------------------------

There were some missing cases in the handling of type legalization, such that a global interface-type shader parameter that got specialized to a type that contains *only* resource-type fields would cause a crash in the legalization step.

I added a test for this case, and then made `ir-legalize-types.cpp` account for this case (the code to handle it ias a bit of a kludge, and shows that the `declareVars()` routine there is getting to a level of complexity that is worrying.

* fixup: review feedback
1 parent 3b9994b
History
Tip revision: 71e35b6822b9e2846e129a888774d45a5e0827da authored by Tim Foley on 20 May 2019, 17:40:38 UTC
Changes required for application adoption of interface-type parameters (#963)
Tip revision: 71e35b6
File Mode Size
docs
examples
external
source
tests
tools
.editorconfig -rw-r--r-- 937 bytes
.gitattributes -rw-r--r-- 95 bytes
.gitignore -rw-r--r-- 407 bytes
.gitmodules -rw-r--r-- 406 bytes
.travis.yml -rw-r--r-- 1.7 KB
CODE_OF_CONDUCT.md -rw-r--r-- 3.1 KB
LICENSE -rw-r--r-- 1.1 KB
README.md -rw-r--r-- 6.7 KB
appveyor.yml -rw-r--r-- 3.7 KB
premake5.lua -rw-r--r-- 26.6 KB
slang-com-helper.h -rw-r--r-- 4.8 KB
slang-com-ptr.h -rw-r--r-- 4.8 KB
slang.h -rw-r--r-- 92.7 KB
slang.sln -rw-r--r-- 9.8 KB
test.bat -rw-r--r-- 1.4 KB
travis_build.sh -rw-r--r-- 304 bytes
travis_test.sh -rw-r--r-- 435 bytes

README.md

back to top