dannymcgee / vscode-wgsl

Rich language support for WebGPU Shading Language
Apache License 2.0
14 stars 1 forks source link

`naga-oil` directive evaluation #38

Closed dannymcgee closed 4 months ago

dannymcgee commented 4 months ago

This update significantly expands support for naga-oil modules.

The language server now accepts a preprocessor key, with this interface:

export interface Preprocessor {
  globalShaderDefs: Record<string, string>;
  shaderDefs: Record<string, Record<string, string>>;
}

An example configuration looks like this:

{
  "wgsl.preprocessor.globalShaderDefs": {
    "AVAILABLE_STORAGE_BUFFER_BINDINGS": "8",
    "PER_OBJECT_BUFFER_BATCH_SIZE": "64",
    "MAX_CASCADES_PER_LIGHT": "8",
    "MAX_DIRECTIONAL_LIGHTS": "4",
    "MORPH_TARGETS": "",
    "SKINNED": "",
    "VERTEX_POSITIONS": "",
    "SLICE_COUNT": "3",
    "SAMPLES_PER_SLICE_SIDE": "3"
  },
  "wgsl.preprocessor.shaderDefs": {
    "bevy_core_pipeline::tonemapping": {
      "TONEMAP_METHOD_AGX": ""
    }
  }
}

When WGSL documents are processed, conditional directives like the following are parsed and evaluated using the provided configuration:

#ifdef SKINNED
    // ...
#else
    // ...
#endif
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
    // ...
#else
    // ...
#endif

Conditional branches that evaluate to false are marked inactive (with DiagnosticTag.Unnecessary) and removed from the input that's passed to the WGSL parser, along with all lines containing conditional directives.

Additionally, shader-def interpolations like the following are replaced with their configured values, with a helpful diagnostic if the server couldn't find a definition for the identifier:

#ifdef PER_OBJECT_BUFFER_BATCH_SIZE
// The WGSL parser will read this type as `array<Mesh, 64u>`
@group(1) @binding(0) var<uniform> mesh: array<Mesh, #{PER_OBJECT_BUFFER_BATCH_SIZE}u>;
#else
@group(1) @binding(0) var<storage> mesh: array<Mesh>;
#endif // PER_OBJECT_BUFFER_BATCH_SIZE
@compute
@workgroup_size(8, 8, 1)
fn gtao(@builtin(global_invocation_id) global_id: vec3<u32>) {
    // The parser will read this value as `f32(3)`
    let slice_count = f32(#SLICE_COUNT);
    // ...
}

Hovering over the #{PER_OBJECT_BUFFER_BATCH_SIZE} range in the editor displays a tooltip like the following, reflecting the value provided in the LSP configuration:

#define PER_OBJECT_BUFFER_BATCH_SIZE 64

Additionally, in-source #define statements like the example above are handled by the pre-parser in the same way.

Remaining Work

This update does not make any changes to the handling of module imports — while the server still provides features like hover tooltips, go-to-definition and reference lookups for imported/exported symbols, these elements are not yet expanded to valid WGSL source code before passing to the Naga validator, so Naga will still emit a parse error and exit early in any modules that include a #define_import_path or #import statement.