Language1 min read

Vertex Attributes

Declaring pipeline attributes and selecting which ones a pass consumes.

Reading Time
1 min
Word Count
132
Sections
6
Try It Live

Pressure-test the syntax

Take the concept from this page into the playground and deliberately break a pass, binding, or type signature to see how the compiler responds.

Try a Live Edit

Pipeline vertex inputs are declared in an attributes block:

bwsl
attributes {
position: float3
normal: float3
texcoord: float2
}

Rules

  • the first declared attribute must be position
  • attributes are declared at pipeline scope
  • graphics passes opt into them with use attributes { ... }
  • shader code reads them through attributes.<name>

Selecting Attributes Per Pass

bwsl
pass "Main" {
use attributes { position, normal, texcoord }
vertex {
output.position = float4(attributes.position, 1.0);
output.normal = attributes.normal;
output.uv = attributes.texcoord;
}
}

This allows different passes in the same pipeline to consume different subsets of the pipeline's attribute set.

Decorators

The current parser supports these decorators on attribute declarations:

  • @compressed(...)
  • @instance

Example:

bwsl
attributes {
position: float3 @compressed(10_10_10)
normal: float3 @compressed(octahedral16)
texcoord: float2 @compressed(unorm16_16)
instanceColor: float4 @instance
}

Optional Attribute Use

The pass grammar also accepts an optional marker in use attributes:

bwsl
use attributes { position, normal? }

This syntax introduces an implicit shader-variant fact named has_<attribute>. In the example above, the pipeline can branch on variants.has_normal and engine integrations can specialize the same pass for meshes that do or do not provide normal.

See Shader Variants for the full specialization model.

Access in Shader Code

bwsl
vertex {
float3 pos = attributes.position;
float3 n = attributes.normal;
float2 uv = attributes.texcoord;
output.position = float4(pos, 1.0);
output.normal = n;
output.uv = uv;
}