Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/sphinx-build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
cd docs
# Run Sphinx build to catch issues
# -D build_toctree=True enables toctree processing to verify all docs are included
python -m sphinx -b html . _build/html
python -m sphinx -W -b html . _build/html

- name: Check for broken links to external sites
run: |
Expand Down
15 changes: 9 additions & 6 deletions docs/autodiff-tips-custom-diffs.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Autodiff Tips and Tricks: Custom Derivatives
title: Autodiff Tips and Tricks - Custom Derivatives
layout: page
description: Autodiff Tips and Tricks: Custom Derivatives
description: Autodiff Tips and Tricks - Custom Derivatives
permalink: "/docs/autodiff-tips-custom-diffs"
intro_image_absolute: true
intro_image_hide_on_mobile: false
Expand All @@ -19,7 +19,8 @@ Opaque functions are those where the internal operations are not visible or acce

For these functions, since the autodiff system cannot "see" inside to compute the derivative, providing a custom derivative allows you to still incorporate them into your differentiable computations.

```slang
<!-- There is no "slang" lexer, so "hlsl" will be used here instead -->
```hlsl
[BackwardDerivative(externalFunction_bwd)]
float externalFunction(float x)
{
Expand All @@ -46,7 +47,8 @@ This example shows one option to handle an opaque function that calls an externa

Functions whose output depends on values retrieved from memory based on an input (e.g., accessing an RWStructuredBuffer on the GPU, or reading from a raw pointer CPU-side) introduce side-effects that automatic differentiation struggles to handle. This can include things like race conditions or ambiguous derivative write-back locations. Additionally, the lookup index itself is non-continuous. Therefore, custom derivatives are often necessary to accurately represent "change" at these points, potentially involving subgradients or specific approximations.

```slang
<!-- There is no "slang" lexer, so "hlsl" will be used here instead -->
```hlsl
RWStructuredBuffer<float> myBuffer;
RWStructuredBuffer<Atomic<float>> gradientBuffer; // Global buffer for gradients

Expand Down Expand Up @@ -74,7 +76,8 @@ Numerical stability refers to how well a computation preserves accuracy when fac

By defining a custom derivative, you can implement more robust numerical methods that mitigate these issues, ensuring that your derivatives are well-behaved and do not lead to computational errors or poor training performance. This might involve re-parameterizations or specialized derivative formulas.

```slang
<!-- There is no "slang" lexer, so "hlsl" will be used here instead -->
```hlsl
[BackwardDerivative(safeDivide_bwd)]
float safeDivide(float numerator, float denominator)
{
Expand Down Expand Up @@ -104,7 +107,7 @@ One of the key strengths of Slang's autodiff system is its flexibility. You are

This means you can address just the parts of your function stack that truly need custom derivatives (e.g., the opaque or numerically unstable sections) while still leveraging Slang's powerful autodiff for the rest of your computations. This hybrid approach offers the best of both worlds: the convenience and efficiency of automatic differentiation where it's most effective, and the precision and control of custom derivatives where they are absolutely necessary.

For examples of this in practice, take a look at some of the [experiments]() in our SlangPy samples repository. In particular, you can see a user-defined custom derivative function invoking bwd\_diff() to make use of automatic differentiation for the functions it calls out to in the [differentiable splatting experiment](https://github.com/shader-slang/slangpy-samples/blob/main/experiments/diff-splatting/diffsplatting2d.slang#L512).
For examples of this in practice, take a look at some of the [experiments](https://github.com/shader-slang/slangpy-samples/blob/main/experiments) in our SlangPy samples repository. In particular, you can see a user-defined custom derivative function invoking bwd\_diff() to make use of automatic differentiation for the functions it calls out to in the [differentiable splatting experiment](https://github.com/shader-slang/slangpy-samples/blob/main/experiments/diff-splatting/diffsplatting2d.slang#L512).

# Approximating Derivatives for Inherently Undifferentiable Functions

Expand Down
46 changes: 23 additions & 23 deletions docs/compilation-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The Slang compilation API is provided as a dynamic library. Linking to it, you h
* [Post-Compilation Reflection](#post-compilation-reflection)
* [Complete Example](#complete-example)

### Basic Compilation
## Basic Compilation

This is the overall flow needed to compile even the simplest applications.

Expand All @@ -38,24 +38,24 @@ This is the overall flow needed to compile even the simplest applications.
6. [Link Program](#link)
7. [Get Target Kernel Code](#get-target-kernel-code)

### Step-by-step
#### Includes
## Step-by-step
### Includes
The main header file is `slang.h`, though you also need `slang-com-ptr.h` to have the definition of Slang::ComPtr used throughout the API. `slang-com-helper.h` is nice to have, since it provides helpers for checking API return values and otherwise using COM.
```cpp
#include "slang.h"
#include "slang-com-ptr.h"
#include "slang-com-helper.h"
```

#### Create Global Session
### Create Global Session
The global API call to `createGlobalSession` is always going to be the first runtime step, since it establishes a connection to the Slang API implementation.

```cpp
Slang::ComPtr<slang::IGlobalSession> globalSession;
createGlobalSession(globalSession.writeRef());
```

#### Create Session
### Create Session
To read more about what sessions are all about, see [About Sessions](#about-sessions).
Creating a session sets the configuration for what you are going to do with the API.

Expand All @@ -69,7 +69,7 @@ The `SessionDesc` object holds all the configuration for the Session.
slang::SessionDesc sessionDesc = {};
```

##### List of enabled compilation targets
#### List of enabled compilation targets

Here, only one target is enabled, `spirv_1_5`. You can enable more targets, for example, if you need to be able to compile the same source to DXIL as well as SPIRV.
```cpp
Expand All @@ -81,7 +81,7 @@ Here, only one target is enabled, `spirv_1_5`. You can enable more targets, for
sessionDesc.targetCount = 1;
```

##### Preprocessor defines
#### Preprocessor defines

Slang supports using the preprocessor.
```cpp
Expand All @@ -94,7 +94,7 @@ Slang supports using the preprocessor.
sessionDesc.preprocessorMacroCount = preprocessorMacroDesc.size();
```

##### Compiler options
#### Compiler options

Here is where you can specify Session-wide options. Check the [User Guide](https://docs.shader-slang.org/en/latest/external/slang/docs/user-guide/08-compiling.html#compiler-options) for info on available options.

Expand All @@ -110,7 +110,7 @@ Here is where you can specify Session-wide options. Check the [User Guide](https
sessionDesc.compilerOptionEntryCount = options.size();
```

##### Create the session
#### Create the session

With a fully populated `SessionDesc`, the session can be created.

Expand All @@ -119,7 +119,7 @@ With a fully populated `SessionDesc`, the session can be created.
globalSession->createSession(sessionDesc, session.writeRef());
```

#### Load Modules
### Load Modules

Modules are the granularity of shader source code that can be compiled in Slang. When using the compilation API, there are two main functions to consider.

Expand All @@ -142,11 +142,11 @@ Modules are the granularity of shader source code that can be compiled in Slang.
}
```

##### Life Time of Modules
#### Life Time of Modules

Modules are owned by the slang Session. Once loaded, they are valid as long as the Session is valid.

#### Query Entry Points
### Query Entry Points

Slang shaders may contain many entry points, and it's necessary to be able to identify them programatically in the Compilation API in order to select which entry points to compile.

Expand All @@ -170,7 +170,7 @@ A common way to query an entry-point is by using the `IModule::findEntryPointByN
It is also possible to query entry-points by index, and work backwards to check the name of the entry-points that are returned at different indices.
Check the [User Guide](https://docs.shader-slang.org/en/latest/external/slang/docs/user-guide/09-reflection.html#program-reflection) for info.

#### Compose Modules and Entry Points
### Compose Modules and Entry Points

Up to this point, modules have been loaded, and entry points have been identified, but to move forward with defining a GPU program, the relevant subset need to be selected for _composition_ into a unified program.

Expand All @@ -194,7 +194,7 @@ Up to this point, modules have been loaded, and entry points have been identifie
}
```

#### Link
### Link

Ensure that there are no missing dependencies in the composed program by using `link()`.

Expand All @@ -210,7 +210,7 @@ Ensure that there are no missing dependencies in the composed program by using `
}
```

#### Get Target Kernel Code
### Get Target Kernel Code

Finally, it's time to compile the linked Slang program to the target format.

Expand Down Expand Up @@ -266,7 +266,7 @@ Both methods cache results within the session and will return a pre-compiled blo
About Sessions
--------------

#### What's a session?
### What's a session?

A session is a scope for caching and reuse. As you use the Slang API, the session caches everything that is loaded in it.

Expand All @@ -278,7 +278,7 @@ It's strongly recommended to use as few session objects as possible in applicati

Using long-lived sessions with Slang API is a big advantage over compiling with the standalone `slangc` compiler executable, since each invocation of `slangc` creates a new session object by necessity.

#### When do I need a new Session?
### When do I need a new Session?

A session does have some global state in it which currently makes it unable to cache and reuse artifacts, namely, the `#define` configurations. Unique combinations of preprocessor `#defines` used in your shaders will require unique session objects.

Expand All @@ -296,16 +296,16 @@ API methods for module precompilation are described in the [User Guide](https://
Specialization
--------------

#### Link-time Constants
### Link-time Constants

This form of specialization involves placing relevant constant definitions in a separate Module that can be selectively included. For example, if you have two variants of a shader that differ in constants that they use, you can create two different Modules for the constants, one for each variant. When composing one variant or the other, just select the right constants module in createCompositeComponentType(). This is described also in the [User Guide](https://docs.shader-slang.org/en/latest/external/slang/docs/user-guide/10-link-time-specialization.html#link-time-constants)

#### Link-time Types
### Link-time Types

Similar to Link-time Constants. This form of specialization simply puts different versions of user types in separate modules so that the needed implementation can be selected when creating the CompositeComponentType.
[User Guide](https://docs.shader-slang.org/en/latest/external/slang/docs/user-guide/10-link-time-specialization.html#link-time-types)

#### Generics Specialization
### Generics Specialization

Say you have a shader that has a feature in it that can be in one of two states, "High Quality" and "Low Quality". One way to support both modes of operation is to use generics. Put the logic for the two modes into two structs, both conforming to an interface (e.g. `IQuality`) that can be consistently used by callers.

Expand Down Expand Up @@ -569,7 +569,7 @@ void computeMain(uint3 threadId_0 : SV_DispatchThreadID)

Notice in particular the switch case added in the function `float U_S11specialized8IQuality8getValuep0pf_0(uint2 _S1)`.

#### Supporting both specialization and dynamic dispatch
### Supporting both specialization and dynamic dispatch

In the prior example, `HighQuality` and `LowQuality` are both supported in a single uber-shader, compiled to support dynamic dispatch on the call to `getValue()`. To achieve this, two `ITypeConformance` objects were added to the composite component.

Expand Down Expand Up @@ -813,7 +813,7 @@ int main()
}
```

#### Compile it (g++ directions)
### Compile it (g++ directions)
* Assumes Slang is installed in the current directory at `slang`.
* Assumes program is saved to "shortest.cpp".
* Assumes a release build of Slang.
Expand All @@ -824,7 +824,7 @@ If any of the above assumptions are wrong in your case, adjust the paths below t
g++ -I./slang/include --std=c++14 shortest.cpp -L./slang/build/Release/lib/ -l:libslang.so
```

#### Run it
### Run it

```bat
LD_LIBRARY_PATH=slang/build/Release/lib ./a.out
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def setup(app):
myst_title_to_header = True

# Suppress specific warnings
suppress_warnings = ["myst.header", "myst.xref_missing", "myst.xref_ambiguous"]
suppress_warnings = ["myst.header", "myst.xref_ambiguous"]

linkcheck_anchors = False
linkcheck_ignore = [
Expand Down
Loading