<feed xmlns='http://www.w3.org/2005/Atom'>
<title>slang.git/tools/render-test/cuda/cuda-compute-util.cpp, branch master</title>
<subtitle>Making it easier to work with shaders</subtitle>
<id>https://git.yummers.dev/slang.git/atom?h=master</id>
<link rel='self' href='https://git.yummers.dev/slang.git/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.yummers.dev/slang.git/'/>
<updated>2021-03-17T19:55:30+00:00</updated>
<entry>
<title>Remove old code paths from render-test (#1760)</title>
<updated>2021-03-17T19:55:30+00:00</updated>
<author>
<name>Tim Foley</name>
<email>tfoleyNV@users.noreply.github.com</email>
</author>
<published>2021-03-17T19:55:30+00:00</published>
<link rel='alternate' type='text/html' href='https://git.yummers.dev/slang.git/commit/?id=6e5d85efb9fa5f647f7f0c7ef784a9fd09b29023'/>
<id>urn:sha1:6e5d85efb9fa5f647f7f0c7ef784a9fd09b29023</id>
<content type='text'>
* Remove old code paths from render-test

Historically, the `render-test` tool was using three different code paths:

* One based on `gfx` and manual (non-reflection-based) parameter setting, used for OpenGL, D3D11, D3D12, and Vulkan
* One for CPU that used reflection-based parameter setting but shared no code with the first
* One for CUDA that used reflection-based parameter setting and shared some, but not all, code with the CPU path

Recently we've updated `render-test` to include a fourth option:

* Using `gfx` and the "shader object" system it exposes for a unified reflection-based parameter-setting system taht works across OpenGL, D3D11, D3D12, Vulkan, CUDA, and CPU

This change removes the first three options and leaves only the single unified path. A sa result, a bunch of code in `render-test` is no longer needed, and the codebase no longer relies on things like the `IDescriptorSet`-related APIs in `gfx`.

Several existing tests had to be disabled to make this change possible. Those tests will need to be audited and either re-enabled once we fix issues in the shader object system, or permanently removed if they don't test stuff we intend to support in the long run (e.g., global-scope type parameters, which aren't a clear necessity).

* fixup: CUDA detection logic</content>
</entry>
<entry>
<title>Enable `gfx::CUDADevice` on linux. (#1756)</title>
<updated>2021-03-15T19:59:58+00:00</updated>
<author>
<name>Yong He</name>
<email>yonghe@outlook.com</email>
</author>
<published>2021-03-15T19:59:58+00:00</published>
<link rel='alternate' type='text/html' href='https://git.yummers.dev/slang.git/commit/?id=10b39e0cb545f98f1a417da8e8e52258134a3e87'/>
<id>urn:sha1:10b39e0cb545f98f1a417da8e8e52258134a3e87</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Add shader object parameter binding to renderer_test. (#1622)</title>
<updated>2020-12-03T16:23:05+00:00</updated>
<author>
<name>Yong He</name>
<email>yonghe@outlook.com</email>
</author>
<published>2020-12-03T16:23:05+00:00</published>
<link rel='alternate' type='text/html' href='https://git.yummers.dev/slang.git/commit/?id=44c0a56974b664e50b2cb8cb6f10740b19c4e02f'/>
<id>urn:sha1:44c0a56974b664e50b2cb8cb6f10740b19c4e02f</id>
<content type='text'>
* Add shader object parameter binding to renderer_test.

* remove multiple-definitions.hlsl

* Fix cuda implementation.

Co-authored-by: Tim Foley &lt;tfoleyNV@users.noreply.github.com&gt;</content>
</entry>
<entry>
<title>Use integer RTTI/witness handles in existential tuples. (#1598)</title>
<updated>2020-11-10T22:55:36+00:00</updated>
<author>
<name>Yong He</name>
<email>yonghe@outlook.com</email>
</author>
<published>2020-11-10T22:55:36+00:00</published>
<link rel='alternate' type='text/html' href='https://git.yummers.dev/slang.git/commit/?id=7bcc2b15c8be4aebc6b9b8f05af6db7a451b228b'/>
<id>urn:sha1:7bcc2b15c8be4aebc6b9b8f05af6db7a451b228b</id>
<content type='text'>
* Use integer RTTI/witness handles in existential tuples.

* Fix clang error.

* Fix IR serialization to use 16bits for opcode.

* Undo accidental comment change.

* Use variable length encoding for opcode.

* Fix compile error.

* Fixing issues

* Fix code review issues.</content>
</entry>
<entry>
<title>Support CUDA bindless texture in dynamic dispatch code. (#1575)</title>
<updated>2020-10-09T18:29:11+00:00</updated>
<author>
<name>Yong He</name>
<email>yonghe@outlook.com</email>
</author>
<published>2020-10-09T18:29:11+00:00</published>
<link rel='alternate' type='text/html' href='https://git.yummers.dev/slang.git/commit/?id=fab1c9f4c745ba84983c2448646376799d461e96'/>
<id>urn:sha1:fab1c9f4c745ba84983c2448646376799d461e96</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Front-load cuda module loading to fill in RTTI pointers. (#1548)</title>
<updated>2020-09-17T22:07:08+00:00</updated>
<author>
<name>Yong He</name>
<email>yonghe@outlook.com</email>
</author>
<published>2020-09-17T22:07:08+00:00</published>
<link rel='alternate' type='text/html' href='https://git.yummers.dev/slang.git/commit/?id=017acb3b58df51ec8bf3bb7eb9f66e58b6713145'/>
<id>urn:sha1:017acb3b58df51ec8bf3bb7eb9f66e58b6713145</id>
<content type='text'>
Co-authored-by: Tim Foley &lt;tfoleyNV@users.noreply.github.com&gt;</content>
</entry>
<entry>
<title>Change parameter passing convention for CUDA (#1463)</title>
<updated>2020-07-28T22:14:31+00:00</updated>
<author>
<name>Tim Foley</name>
<email>tfoleyNV@users.noreply.github.com</email>
</author>
<published>2020-07-28T22:14:31+00:00</published>
<link rel='alternate' type='text/html' href='https://git.yummers.dev/slang.git/commit/?id=cd106730ea52511a672c9c2c5c8697eaca3b57c8'/>
<id>urn:sha1:cd106730ea52511a672c9c2c5c8697eaca3b57c8</id>
<content type='text'>
The Big Picture
===============

Given input Slang code like:

```hlsl
Texture2D gA;

[shader("compute")]
void kernelFunc(uniform Texture2D b, uint3 tid : SV_DispatchThreadID)
{ ... }
```

the existing CUDA code generation strategy would always generate a kernel with a signature like:

```c++
struct GlobalParams { Texture2D gA; }
struct EntryPointParams { Texture2D b; }

extern "C" __global__
void kernelFunc(EntryPointParams* entryPointParams, GlobalParams* globalParams)
{ ... }
```

This choice was consistent with the conventions of the CPU kernel target, and shares the advantage that it is easy for the user to data-drive the logic for filling in parameters and then invoking a kernel.

However, the approach outlined above has two serious problems when used for CUDA kernels:

* First, it defies the programmer's expectation about what an "equivalent" CUDA kernel signature would be, which makes it awkward for a developer to invoke this kernel from CUDA C++ host code (especially in the context of an app that might also run hand-written CUDA kernels).

* Second, the performance of this approach suffers because every access to a global or entry point parameter turns into a load from global memory. In contrast, a typical hand-written CUDA kernel passes its parameters via an implementation-specific path that (for current CUDA platforms) seems to be equivalent to `__constant__` memory in performance.

This change alters the convention so that the Slang compiler takes the code from the top of this message and translates it into something like:

```c++
struct GlobalParams { Texture2D gA; }

__constant__ GlobalParams SLANG_globalParams;

extern "C" __global__
void kernelFunc( Texture2D b )
{ ... }
```

This translation alleviates both problems with the current translation:

* The signature of the generated CUDA kernel function is as close to that of the original as is possible (we had to eliminate the `SV_*`-semantic varying inputs), and should directly match what the programmer would expect in common cases.

* Entry-point parameters are passed via CUDA kernel parameters, and should thus match in performance. Global parameters are passed via a variable in `__constant__` memory, and thus should also perform as well as possible/expected.

Detailed Changes
================

* Disable the `collectEntryPointUniformParams` pass for CUDA, so that entry-point `uniform` parameters are *not* bundles into a single `struct` and/or `ConstantBuffer`.

* When targeting CUDA, disable the logic for generating an entry-point parameter for passing in the global shader parameter(s)

* Allow `CLikeSourceEmitter` subclasses to override the name generated for entry-point symbols, and use this to add the required prefix for each OptiX kernel type when translating a ray-tracing kernel.

* Add logic to emit "parameter groups" in a specialized way for CUDA (this is the same approach that allows us to generate `cbufffer { ... }` declarations for fxc). A global-scope parameter group will turn into a global `__constant__` variable called `SLANG_globalParams` (that name becomes part of the ABI for Slang-compiled shaders).

* Update the logic in `render-test` for loading and invoking CUDA kernels to handle the new policy.

The last bullet there merits expansion, since it is indicative of the work a client using Slang would have to go through to use our generated kernels with the new policy:

* When loading a CUDA module with one or more kernels, we also use `cuModuleGetGlobal` to query the address of the `SLANG_globalParams` symbol in that CUDA module. That pointer needs to be used when setting global parameter values to be used by kernels in that CUDA odule.

* Because our existing `BindPoint` logic for CUDA always sets up parameter data in GPU memory, we end up having to copy the entry-point parameter data from GPU memory to host memory. This step would ideally be skipped in a codebase that understands the correct policy, but it is a bit unfortunate that it is no longer trivially correct for an application to store all parameter data in GPU memory.

* Before invoking the kernel, we need to use a `cudaMemcpyAsync` to copy from the prepared GPU memory for global parameters over to the `SLANG_globalParams` symbol associated with the kernel to be invoked. Because this operations is issued on the same CUDA stream as the kernel call, it is guaranteed to not overlap with GPU kernel execution.

* When invoking the kernel, we take advantage of the seldom-used `CU_LAUNCH_PARAM_BUFFER_POINTER` facility to specify a contiguous memory region with all the entry-point parameters in it instead of passing each entry-point parameter separately. Given Slang reflection it is also possible to query the offset of each entry-point parameter in the buffer, so we could invoke the kernel in the traditional fashion as well. The choice here is up to the application.

Caveats
=======

* This is a breaking change, and any subsequent release will need to reflect that fact. Any customers who rely on Slang's current CUDA codegen strategy are likely to be surprised by this change, and I don't see an easy way to give them a more gentle transition.

* This change does *not* remove the logic that introduces a `KernelContext` type for code that requires it. That means that things like `static` global variables can continue to work on CUDA for now, but we know that those are not going to be something we can support in the long-term with separate compilation.

* While the policy implemented in this change is a reasonable default, it is still not going to perfectly match expecations for some developers. In particular, some developers who are familiar with both D3D and CUDA will likely wonder why a global `cbuffer` in Slang translates to a global-memory pointer in the output CUDA instead of one global `__constant__` variable per `cbuffer`. A more detailed alternate translation would generate a distinct global `__constant__` variable for each top-level constant buffer or parameter block. We may need to refine the translation even more based on feedback from users who care about how we handle global-scope parameters.

* Recent changes in Slang have broken the logic that handles the OptiX "shader record" as an alternative mechanism for passing entry-point parameters. In order to get any level of OptiX support up and running we will have to change the IR passes that run on CUDA kernels to actually run the "collection" of `uniform` parameters for ray tracing stages, and then to replace references to the resulting parameter with a call to the function to access the shader record.

* The use of `SLANG_globalParams` here works well enough in the case of whole-program compilation; every `CUmodule` ends up with (zero or) one parameter with this name, and an application can just hard-code it. As a mechanism it wouldn't work in the presence of separately-compiled modules that might introduce their own global parameters (including cases like constant lookup tables that really want to be at the global scope). An alternative approach would have Slang generate output PTX for each module, where a module has an optional global symbol for its own global-scope parameters (with a mangled name that is based on the module name), and then a linked CUDA binary has all of those distinct symbols. Such an approach would be compatible with module-at-a-time reflection and parameter binding, but would lead to another breaking change down the line for code that switches to `SLANG_globalParams`.</content>
</entry>
<entry>
<title>Synthesize "active mask" for CUDA (#1352)</title>
<updated>2020-05-26T22:11:22+00:00</updated>
<author>
<name>Tim Foley</name>
<email>tfoleyNV@users.noreply.github.com</email>
</author>
<published>2020-05-26T22:11:22+00:00</published>
<link rel='alternate' type='text/html' href='https://git.yummers.dev/slang.git/commit/?id=e5d0f3360f44a4cdd2390e7817db17bb3cc0dd04'/>
<id>urn:sha1:e5d0f3360f44a4cdd2390e7817db17bb3cc0dd04</id>
<content type='text'>
* Synthesize "active mask" for CUDA

The Big Picture
===============

The most important change here is to `hlsl.meta.slang`, where the declaration of `WaveGetActiveMask()` is changed so that instead of mapping to `__activemask()` on CUDA (which is semantically incorrect) it maps to a dedicated IR instruction.

The other `WaveActive*()` intrinsics that make use of the implicit "active mask" concept had already been changed in #1336 so that they explicitly translate to call the equivalent `WaveMask*()` intrinsic with the result of `WaveGetActiveMask()`. As a result, all of the `WaveActive*()` functions are now no different from a user-defined function that uses `WaveGetActiveMask()`.

The bulk of the work in this change goes into an IR pass to replace the new instruction for getting the active mask gets replaced with appropriately computed values before we generate output CUDA code. That work is in `slang-ir-synthesize-active-mask.{h,cpp}`.

Utilities
=========

There are a few pieces of code that were helpful in writing the main pass but that can be explained separately:

* IR instructions were added corresponding to the Slang `WaveMaskBallot()` and `WaveMaskMatch()` functions, which map to the CUDA `__ballot_sync()` and `__match_any_sync()` operations, respectively. These are only implemented for the CUDA target because they are only being generated as part of our CUDA-only pass.

* The `IRDominatorTree` type was updated to make it a bit more robust in the presence of unreachable blocks in the CFG. It is possible that the same ends could be achieved more efficiently by folding the corner cases into the main logic, but I went ahead and made things very explicit for now.

* I added an `IREdge` utility type to better encapsulate the way that certain code operating on the predecessors/successors of an `IRBlock` were using an `IRUse*` to represent a control-flow edge. The `IREdge` type makes the logic of those operations more explicit. A future change should proably change it so that `IRBlock::getPredecessors()` and `getSuccessors()` are instead `getIncomingEdges()` and `getOutgoingEdges()` and work as iterators over `IREdge` values, given the way that the predecessor and successor lists today can contain duplicates.

* Using the above `IREdge` type, the logic for detecting and break critical edges was broken down into something that is a bit more clear (I hope), and that also factors out the breaking of an edge (by inserting a block along it) into a reusable subroutine.

The Main Pass
=============

The implementation of the new pass is in `slang-ir-synthesize-active-mask.cpp`, and that file attempts to include enough comments to make the logic clear. A brief summary for the benefit of the commit history:

* The first order of business is to identify functions that need to have the active mask value piped into them, and to add an additional parameter to them so that the active mask is passed down explicitly. Call sites are adjusted to pass down the active mask which can then result in new functions being identified as needing the active mask.

* The next challenge is for a function that uses the active mask, to compute the active mask value to use in each basic block. The entry block can easily use the active mask value that was passed in, while other blocks need more work.

* When doing a conditional branch, we can compute the new mask for the block we branch to as a function of the existing mask and the branch condition. E.g., the value `WaveMaskBallot(existingMask, condition)` can be used as the mask for the "then" block of an `if` statement.

* When control flow paths need to "reconverge" at a point after a structured control-flow statement, we need to insert logic to synchronize and re-build the mask that will execute after the statement, while also excluding any lanes/threads that exited the statement in other ways (e.g., an early `return` from the function).

The explanation here is fairly hand-wavy, but the actual pass uses much more crisp definitions, so the code itself should be inspected if you care about the details.

Tests
=====

The tests for the new feature are all under `tests/hlsl-intrinsic/active-mask/`. Most of them stress a single control-flow construct (`if`, `switch`, or loop) and write out the value of `WaveGetActiveMask()` at various points in the code.

In practice, our definition of the active mask doesn't always agree with what D3D/Vulkan implementations seem to produce in practice, and as a result a certain amount of effort has gone into adding tweaks to the tests that force them to produce the expected output on existing graphics APIs. These tweaks usually amount to introducing conditional branches that aren't actually conditional in practice (the branch condition is always `true` or always `false` at runtime), in order to trick some simplistic analysis approaches that downstream compilers seem to employ.

One test case currently fails on our CUDA target (`switch-trivial-fallthrough.slang`) and has been disabled. This is an expected failure, because making it produce the expected value requires a bit of detailed/careful coding that would add a lot of additional complexity to this change. It seemed better to leave that as future work.

Future Work
===========

* As discussed under "Tests" above, the handling of simple `switch` statements in the current pass is incomplete.

* There's an entire can of worms to be dealt with around the handling of fall-through for `switch`.

* The current work also doesn't handle `discard` statements, which is unimportant right now (CUDA doesn't have fragment shaders), but might matter if we decide to synthesize masks for other targets. Similar work would probably be needed if we ever have `throw` or other non-local control flow that crosses function boundaries.

* An important optimization opportunity is being left on the floor in this change. When block that comes "after" a structured control-flow region (which is encoded explicitly in Slang IR and SPIR-V) post-dominates the entry block of the region, then we know that the active mask when exiting the region must be the same as the mask when entering the region, and there is no need to insert explicit code to cause "re-convergence." This should be addressed in a follow-on change once we add code to Slang for computing a post-dominator tree from a function CFG.

* Related to the above, the decision-making around whether a basic block "needs" the active mask is perhaps too conservative, since it decides that any block that precedes one needing the active mask also needs it. This isn't true in cases where the active mask for a merge block can be inferred by post-dominance (as described above), so that the blocks that branch to it don't need to compute an active mask at all.

* If/when we extend the CPU target to support these operations (along with SIMD code generation, I assume), we will also need to synthesize an active mask on that platform, but the approach taken here (which pretty much relies on support for CUDA "cooperative groups") wouldn't seem to apply in the SIMD case.

* Similarly, the approach taken to computing the active mask here requires a new enough CUDA SM architecture version to support explicit cooperative groups. If we want to run on older CUDA-supporting architectures, we will need a new and potentially very different strategy.

* Because the new pass here changes the signature of functions that require the active mask (and not those that don't), it creates possible problems for generating code that uses dynamic dispatch (via function pointers). In principle, we need to know at a call site whether or not the callee uses the active mask. There are multiple possible solutions to this problem, and they'd need to be worked through before we can make the implicit active mask and dynamic dispatch be mutually compatible.

* Related to changing function signatures: no effort is made in this pass to clean up the IR type of the functions it modifies, so there could technically be mismatches between the IR type of a function and its actual signature. If/when this causes problems for downstream passes we probably need to do some cleanup.

* fixup: backslash-escaped lines

I did some "ASCII art" sorts of diagrams to explain cases in the CFG, and some of those diagrams used backslash (`\`) characters as the last character on the line, causing them to count as escaped newlines for C/C++.

The gcc compiler apparently balked at those lines, since they made some of the single-line comments into multi-line comments.

I solved the problem by adding a terminating column of `|` characters at the end of each line that was part of an ASCII art diagram.

* fixup: typos

Co-authored-by: jsmall-nvidia &lt;jsmall@nvidia.com&gt;</content>
</entry>
<entry>
<title>Small Improvements around Wave Intrinsics (#1328)</title>
<updated>2020-04-21T18:09:36+00:00</updated>
<author>
<name>jsmall-nvidia</name>
<email>jsmall@nvidia.com</email>
</author>
<published>2020-04-21T18:09:36+00:00</published>
<link rel='alternate' type='text/html' href='https://git.yummers.dev/slang.git/commit/?id=77d59713ac665785b7ebee4ad2b5dcbb73cf5af5'/>
<id>urn:sha1:77d59713ac665785b7ebee4ad2b5dcbb73cf5af5</id>
<content type='text'>
* Fix issues in wave-mask/wave.slang tests.
WaveGetActiveMask -&gt; WaveGetConvergedMask.
Update target-compatibility.md

* First pass at wave-intrinsics.md documentation.
Write up around WaveMaskSharedSync.

* Added more of the Wave intrinsics as WaveMask intrinsics.
Improvements to documentation around wave-intrinsics.

* Add the Wave intrinsics for SM6.5 for WaveMask
Expand WaveMask intrinsics
Improve WaveMask documentation

* Added WaveMaskIsFirstLane.

* Added WaveGetConvergedMask for glsl and hlsl.
Added wave-get-converged-mask.slang test.

* WaveGetActiveMask/Multi and WageGetConvergedMask/Multi

* Improve Wave intrinsics docs.
Adde WaveGetActveMulti WaveGetConvergedMulti, WaveGetActiveMask (for vk/hlsl).

* Enable GLSL WaveMultiPrefixBitAnd.

* Re-add definitions of f16tof32 and f32to16 from #1326

* Remove multiple definition of f32tof16
Disable optix call to Ray trace test, if OPTIX not available.

* Improve wave intrinsics documetnation - remove the __generic as part of definitions, small improvements.

* Change comment to try and trigger build.</content>
</entry>
<entry>
<title>Add support for global shader parameters to OptiX path (#1323)</title>
<updated>2020-04-17T15:53:41+00:00</updated>
<author>
<name>Tim Foley</name>
<email>tfoleyNV@users.noreply.github.com</email>
</author>
<published>2020-04-17T15:53:41+00:00</published>
<link rel='alternate' type='text/html' href='https://git.yummers.dev/slang.git/commit/?id=acb1c39b4e29358cf496c07dc325e52f39be71f4'/>
<id>urn:sha1:acb1c39b4e29358cf496c07dc325e52f39be71f4</id>
<content type='text'>
There are two main pieces here.

First, we specialize the code generaiton for CUDA kernels to account for the way that shader parameters are passed differently for ordinary compute kernels vs. ray-tracing kernels. Both global and entry-point shader parameters in Slang are translated to kernel function parameters for CUDA compute kernels, while for OptiX ray tracing kernels we need to use a global `__constant__` variable for the global parameters, and the SBT data (accessed via an OptiX API function) for entry-point shader parameters.

This choice bakes in a few pieces of policy when it comes to how Slang ray-tracing shaders translate to OptiX:

* It fixes the name used for the global `__constant__` variable for global shader parameters to be `SLANG_globalParams`. Since that name has to be specified when creating a pipeline with the OptiX API, the choice of name effectively becomes an ABI contract for Slang's code generation.

* It fixes the choice that global parameters in Slang map to per-launch parameters in OptiX, and entry-point parameters in Slang map to SBT-backed parameters in OptiX. This is a reasonable policy, and it is also one that we are likely to need to codify for Vulkan as well, but it is always a bit unfortunate to bake policy choices like this into the compiler (especially when shaders compiled for D3D can often decouple the form of their HLSL/Slang code from how things are bound in the API).

The second piece is a lot of refactoring of the logic in `render-test/cuda/cuda-compute-util.cpp`, so that the logic for setting up (and reading back) the buffers of parameter data can be shared between the compute and ray-tracing paths. The result may not be a true global optimum for how the code is organized, but it at least serves the goal of not duplicating the parameter-binding logic between compute and ray-tracing.</content>
</entry>
</feed>
