Skip to content

Commit

Permalink
Merge 7ce9e7b into f0bc8f2
Browse files Browse the repository at this point in the history
  • Loading branch information
ibgreen committed Mar 1, 2024
2 parents f0bc8f2 + 7ce9e7b commit d648e78
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 123 deletions.
74 changes: 49 additions & 25 deletions docs/api-reference/shadertools/shader-assembler.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,43 @@ via the `ShaderAssembler` class.
- and injections
to generate the final vertex and fragment shader source that can be used to create a program.

## Types

## `AssembleShaderOptions`

For single shader compilation
- `source` - single shader source (always WGSL)

For shader pair compilation
- `vs` - vertex shader source
- `fs` - fragment shader source code

Common options
- `prologue`=`true` (Boolean) - Will inject platform prologue (see below)
- `defines`=`{}` (Object) - a map of key/value pairs representing custom `#define`s to be injected into the shader source
- `modules`=`[]` (Array) - list of shader modules (either objects defining the module, or names of previously registered modules)
- `inject`=`{}` (Object) - map of substituions,
- `hookFunctions`=`[]` Array of hook functions descriptions. Descriptions can simply be the hook function signature (with a prefix `vs` for vertex shader, or `fs` for fragment shader) or an object with the hook signature, and a header and footer that will always appear in the hook function.

Example of hook function

```typescript
[
'vs:MY_HOOK_FUNCTION1(inout vec4 color)',
{
hook: 'fs:MY_HOOK_FUNCTION2(inout vec4 color)',
header: 'if (color.a == 0.0) discard;\n',
footer: 'color.a *= 1.2;\n'
}
];
```

## Static Methods

### `getDefaultShaderAssembler()`

Most applications that register default modules and hooks will want to use a single `Shader`


## Methods

### `addDefaultModule(module: ShaderModule)`
Expand All @@ -35,41 +65,34 @@ Creates a shader hook function that shader modules can injection code into. Shad
- `opts.header` (optional): code always included at the beginning of a hook function
- `opts.footer` (optional): code always included at the end of a hook function

### `assembleShaders`
### `assembleShader(options: AssembleShaderOptions)`

`ahaderAssebler.assembleShaders()` composes base vertex and fragment shader source with shader modules,
hook functions and injections to generate the final vertex and
fragment shader source that can be used to create a program.
generate the shader source that can be used to create a shader and then a pipeline.

- composes a single shader source (compute or unified vertex/fragment WGSL shader) with source from shader modules,
- resolving hook functions and injections to

Takes the source code of a vertex shader and a fragment shader, and a list of modules, defines, etc. Outputs resolved source code for both shaders, after adding prologue, adding defines, importing and transpiling modules, and injecting any shader fragments).

- `vs` - vertex shader source
- `fs` - fragment shader source code
- `id` - `id` for the shader, will be used to inject shader names (using `#define SHADER_NAME`) if not already present in the source.
- `prologue`=`true` (Boolean) - Will inject platform prologue (see below)
- `defines`=`{}` (Object) - a map of key/value pairs representing custom `#define`s to be injected into the shader source
- `modules`=`[]` (Array) - list of shader modules (either objects defining the module, or names of previously registered modules)
- `inject`=`{}` (Object) - map of substituions,
- `hookFunctions`=`[]` Array of hook functions descriptions. Descriptions can simply be the hook function signature (with a prefix `vs` for vertex shader, or `fs` for fragment shader) or an object with the hook signature, and a header and footer that will always appear in the hook function. For example:
Returns:

```typescript
[
'vs:MY_HOOK_FUNCTION1(inout vec4 color)',
{
hook: 'fs:MY_HOOK_FUNCTION2(inout vec4 color)',
header: 'if (color.a == 0.0) discard;\n',
footer: 'color.a *= 1.2;\n'
}
];
```
- `vs` - the resolved vertex shader
- `fs` - the resolved fragment shader
- `getUniforms` - a combined `getUniforms` function covering all modules.

### `assembleShaderPair(options: AssembleShaderOptions)`

Generate the final vertex and fragment shader source that can be compiled to create two shaders and then link them into a pipeline.

- composes base vertex and fragment shader source with source from shader modules
- resolves hook functions and injections

Takes the source code of a vertex shader and a fragment shader, and a list of modules, defines, etc. Outputs resolved source code for both shaders, after adding prologue, adding defines, importing and transpiling modules, and injecting any shader fragments).

Returns:

- `vs` - the resolved vertex shader
- `fs` - the resolved fragment shader
- `getUniforms` - a combined `getUniforms` function covering all modules.
- `moduleMap` - a map with all resolved modules, keyed by name

## Shader Module Assembly

Expand Down Expand Up @@ -116,6 +139,7 @@ void MY_HOOK_FUNCTION(inout vec4 color) {
The hook function now changes the color from white to red.
## Constants and Values
### Predefined Injection Hooks
Expand Down
18 changes: 10 additions & 8 deletions docs/whats-new.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,25 @@ To accelerate WebGPU development, luma.gl v9 drops support for legacy functional
**`@luma.gl/shadertools`**

- All shader modules now use uniform buffers.
- NEW: `ShaderAssembler` class that provides a clean entry point to the shader module system.
- New `ShaderAssembler` class that provides a clean entry point to the shader module system.
- New `CompilerMessage` type and `formatCompilerLog` function for portable shader log handling.
- Shader assembly now supports WGSL and single shader source (compute or single vertex+fragment WGSL shaders)

**`@luma.gl/webgl`**

WebGL is not dead yet! Browsers (Chrome in particular) are still adding extensions to WebGL 2, and luma.gl
is adding support for many of the new features through the [`DeviceFeatures`](/docs/api-reference/core/device-features) API.

New `Device.features` that improve WebGL application performance:
New `Device.features` that improve application performance in WebGL:
- `compilation-status-async-webgl`: Asynchronous shader compilation and linking is used automatically when available and speeds up applications that create many `RenderPipelines`.

New `Device.features` that expose new WebGL GPU parameters:
New `Device.features` that enable additional color format support in WebGL:
- `rgb9e5ufloat-renderable-webgl`: `rgb9e5ufloat` is renderable.
- `snorm8-renderable-webgl`: `r,rg,rgba8snorm` are renderable.
- `norm16-renderable-webgl`: `r,rg,rgba16norm` are renderable.
- `snorm16-renderable-webgl`: `r,rg,rgba16snorm` are renderable.

New `Device.features` that expose new GPU parameters in WebGL:
- `depth-clip-control`: `parameters.unclippedDepth` - depth clipping can now be disabled if the feature is available.
- `provoking-vertex-webgl`: `parameters.provokingVertex` - controls which primitive vertex is used for flat shading.
- `polygon-mode-webgl`: `parameters.polygonMode` - enables wire frame rendering of polygons. Check the feature.
Expand All @@ -92,8 +99,3 @@ New `Device.features` that enable new GLSL syntax
- `shader-noperspective-interpolation-webgl`: GLSL vertex outputs and fragment inputs may be declared with a `noperspective` interpolation qualifier.
- `shader-conservative-depth-webgl`: GLSL `gl_FragDepth` qualifiers `depth_any` `depth_greater` `depth_less` `depth_unchanged` can enable early depth test optimizations.

New `Device.features` that enable additional WebGL color format support:
- `rgb9e5ufloat-renderable-webgl`: `rgb9e5ufloat` are renderable.
- `snorm8-renderable-webgl`: `r,rg,rgba8snorm` are renderable.
- `norm16-renderable-webgl`: `r,rg,rgba16norm` are renderable.
- `snorm16-renderable-webgl`: `r,rg,rgba16snorm` are renderable.
2 changes: 1 addition & 1 deletion modules/engine/src/model/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ export class Model {
const modules =
(this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || [];

const {vs, fs, getUniforms} = this.props.shaderAssembler.assembleShaders({
const {vs, fs, getUniforms} = this.props.shaderAssembler.assembleShaderPair({
platformInfo,
...this.props,
modules
Expand Down
2 changes: 1 addition & 1 deletion modules/shadertools/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export {generateShaderForModule} from './lib/shader-generator/generate-shader';
export {capitalize} from './lib/shader-generator/utils/capitalize';

// TEST EXPORTS - Do not use in production applications
export {assembleShaders} from './lib/shader-assembly/assemble-shaders';
export {assembleShaderPairGLSL} from './lib/shader-assembly/assemble-shaders';
export {ShaderModuleInstance as _ShaderModuleInstance} from './lib/shader-module/shader-module-instance';
export {combineInjects} from './lib/shader-assembly/shader-injections';
export {resolveModules as _resolveModules} from './lib/shader-assembly/resolve-modules';
Expand Down
38 changes: 33 additions & 5 deletions modules/shadertools/src/lib/shader-assembler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@

import type {ShaderModule} from './shader-module/shader-module';
import {ShaderModuleInstance} from './shader-module/shader-module-instance';
import {GetUniformsFunc, assembleShaders} from './shader-assembly/assemble-shaders';
import {selectShaders, AssembleShaderProps} from './shader-assembly/select-shaders';
import {
GetUniformsFunc,
assembleShaderWGSL,
assembleShaderPairWGSL,
assembleShaderPairGLSL
} from './shader-assembly/assemble-shaders';

/**
* A stateful version of `assembleShaders` that can be used to assemble shaders.
Expand Down Expand Up @@ -68,16 +73,15 @@ export class ShaderAssembler {
* @param props
* @returns
*/
assembleShaders(props: AssembleShaderProps): {
vs: string;
fs: string;
assembleShader(props: AssembleShaderProps): {
source: string;
getUniforms: GetUniformsFunc;
modules: ShaderModuleInstance[];
} {
const modules = this._getModuleList(props.modules); // Combine with default modules
const hookFunctions = this._hookFunctions; // TODO - combine with default hook functions
const options = selectShaders(props);
const assembled = assembleShaders({
const assembled = assembleShaderWGSL({
platformInfo: props.platformInfo,
...options,
modules,
Expand All @@ -86,6 +90,30 @@ export class ShaderAssembler {
return {...assembled, modules};
}

/**
* Assemble a pair of shaders into a single shader program
* @param platformInfo
* @param props
* @returns
*/
assembleShaderPair(props: AssembleShaderProps): {
vs: string;
fs: string;
getUniforms: GetUniformsFunc;
modules: ShaderModuleInstance[];
} {
const options = selectShaders(props);
const modules = this._getModuleList(props.modules); // Combine with default modules
const hookFunctions = this._hookFunctions; // TODO - combine with default hook functions
const {platformInfo} = props;
const isWGSL = props.platformInfo.shaderLanguage === 'wgsl';
const assembled = isWGSL
? assembleShaderPairWGSL({platformInfo, ...options, modules, hookFunctions})
: assembleShaderPairGLSL({platformInfo, ...options, modules, hookFunctions});

return {...assembled, modules};
}

/**
* Dedupe and combine with default modules
*/
Expand Down
112 changes: 69 additions & 43 deletions modules/shadertools/src/lib/shader-assembly/assemble-shaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ export type AssembleShaderOptions = {
platformInfo: PlatformInfo;
/** Inject shader id #defines */
id?: string;
/** Single WGSL shader */
source?: string;
/** Vertex shader */
vs: string;
vs?: string;
/** Fragment shader */
fs: string;
/** Shader type @deprecated do we still need this */
// type?: any;
fs?: string;
/** Modules to be injected */
modules?: (ShaderModule | ShaderModuleInstance)[];
/** Defines to be injected */
Expand Down Expand Up @@ -81,51 +81,77 @@ type AssembleStageOptions = {
export type GetUniformsFunc = (opts: Record<string, any>) => Record<string, any>;

/**
* Inject a list of shader modules into shader sources
* Inject a list of shader modules into a single shader source for WGSL
*/
export function assembleShaderWGSL(options: AssembleShaderOptions): {
source: string;
getUniforms: GetUniformsFunc;
} {
const modules = resolveModules(options.modules || []);
return {
source: assembleWGSLShader(options.platformInfo, {
...options,
source: options.source,
stage: 'vertex',
modules
}),
getUniforms: assembleGetUniforms(modules)
};
}

/**
* Injects dependent shader module sources into pair of main vertex/fragment shader sources for WGSL
*/
export function assembleShaders(options: AssembleShaderOptions): {
export function assembleShaderPairWGSL(options: AssembleShaderOptions): {
vs: string;
fs: string;
getUniforms: GetUniformsFunc;
} {
const {vs, fs} = options;
const modules = resolveModules(options.modules || []);

switch (options.platformInfo.shaderLanguage) {
case 'glsl':
return {
vs: assembleGLSLShader(options.platformInfo, {
...options,
source: vs,
stage: 'vertex',
modules
}),
fs: assembleGLSLShader(options.platformInfo, {
...options,
source: fs,
stage: 'fragment',
modules
}),
getUniforms: assembleGetUniforms(modules)
};
return {
vs: assembleWGSLShader(options.platformInfo, {
...options,
source: options.vs,
stage: 'vertex',
modules
}),
fs: assembleWGSLShader(options.platformInfo, {
...options,
source: options.fs,
stage: 'fragment',
modules
}),
getUniforms: assembleGetUniforms(modules)
};
}

case 'wgsl':
return {
vs: assembleWGSLShader(options.platformInfo, {
...options,
source: vs,
stage: 'vertex',
modules
}),
fs: assembleWGSLShader(options.platformInfo, {
...options,
source: fs,
stage: 'fragment',
modules
}),
getUniforms: assembleGetUniforms(modules)
};
}
/**
* Injects dependent shader module sources into pair of main vertex/fragment shader sources for GLSL
*/
export function assembleShaderPairGLSL(options: AssembleShaderOptions): {
vs: string;
fs: string;
getUniforms: GetUniformsFunc;
} {
const {vs, fs} = options;
const modules = resolveModules(options.modules || []);

return {
vs: assembleGLSLShader(options.platformInfo, {
...options,
source: vs,
stage: 'vertex',
modules
}),
fs: assembleGLSLShader(options.platformInfo, {
...options,
source: fs,
stage: 'fragment',
modules
}),
getUniforms: assembleGetUniforms(modules)
};
}

/**
Expand All @@ -135,7 +161,7 @@ export function assembleShaders(options: AssembleShaderOptions): {
* @param options
* @returns
*/
function assembleWGSLShader(platformInfo: PlatformInfo, options: AssembleStageOptions) {
export function assembleWGSLShader(platformInfo: PlatformInfo, options: AssembleStageOptions) {
const {
// id,
source,
Expand Down Expand Up @@ -412,7 +438,7 @@ ${getApplicationDefines(allDefines)}
* @param modules
* @returns
*/
function assembleGetUniforms(modules: ShaderModuleInstance[]) {
export function assembleGetUniforms(modules: ShaderModuleInstance[]) {
return function getUniforms(opts: Record<string, any>): Record<string, any> {
const uniforms = {};
for (const module of modules) {
Expand Down
2 changes: 2 additions & 0 deletions modules/shadertools/src/lib/shader-assembly/select-shaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import type {AssembleShaderOptions} from './assemble-shaders';
*/
export type AssembleShaderProps = Omit<AssembleShaderOptions, 'vs' | 'fs'> & {
platformInfo: PlatformInfo;
/** Single shader source. Always WGSL */
source?: string | null;
/** Vertex shader source. Can be GLSL or WGSL or both */
vs?: {glsl?: string; wgsl?: string} | string | null;
/** Fragment shader source. Can be GLSL or WGSL or both */
Expand Down
Loading

0 comments on commit d648e78

Please sign in to comment.