Skip to content
Merged
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
33 changes: 33 additions & 0 deletions daslib/shader_block_layout.das
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,45 @@ def public component_count(bt : Type) : int {
return 0
}

// Type -> (class, component bit WIDTH, lanes), extending the 32-bit classifiers above to the 64-bit
// scalars they exclude (int64/uint64/double, which lay out as block members but carry no arithmetic).
// Width travels with the class so a 32-bit-only consumer can test width, not just class.
def public numeric_info(bt : Type) : tuple<ok : bool; cls : int; width : int; lanes : int> {
if (bt == Type.tInt64) return (ok = true, cls = 0, width = 64, lanes = 1)
if (bt == Type.tUInt64) return (ok = true, cls = 1, width = 64, lanes = 1)
if (bt == Type.tDouble) return (ok = true, cls = 2, width = 64, lanes = 1)
let c = scalar_class(bt)
if (c < 0) return (ok = false, cls = -1, width = 0, lanes = 0)
return (ok = true, cls = c, width = scalar_width(bt), lanes = component_count(bt))
}

// ===== interface-block layout rules =====
enum public BlockLayoutRules {
std140
std430
}

// The widths the ARITHMETIC rail lowers: the 16/32-bit families (a native OpIAdd on OpTypeInt 16,
// as glslang gives i16vec2) plus the 64-bit FLOAT (OpFAdd on OpTypeFloat 64, GLSL's double). A
// 64-bit INT is excluded -- it lays out as a block member only, so it fails closed before an opcode.
def public arith_width_ok(cls, width : int) : bool {
return width <= 32 || (width == 64 && cls == 2)
}

// Class and lane count for the arithmetic rail, gated on arith_width_ok rather than a 32-bit width
// (it carries no byte layout). The 32-bit-only scalar_class / component_count stay as they are, so
// no 16-bit component or double can slip into the std140 layout whose sizes are hardcoded 4-byte.
def public arith_class(bt : Type) : int {
let ni = numeric_info(bt)
return ni.ok && arith_width_ok(ni.cls, ni.width) ? ni.cls : -1
}

def public arith_lanes(bt : Type) : int {
if (bt == Type.tBool) return 1
let ni = numeric_info(bt)
return ni.ok && arith_width_ok(ni.cls, ni.width) ? ni.lanes : 0
}

// ===== std140 layout for a UBO interface block =====
def public std140_align(bt : Type) : int {
let cc = component_count(bt)
Expand Down
50 changes: 50 additions & 0 deletions daslib/shader_lingua_franca.das
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ options indenting = 4

module shader_lingua_franca shared public

// float4x4 (the matrix constructors below) lives in math; the vector types are builtin.
require math

// ===== opaque resource types =====
// GL-superset: the `texture2D`/`texture3D` uint carries the GL texture-unit handle the host binds
// (glUniform1i). The SPIR-V emitter recognizes each type by NAME (classify_global / sampler_info)
Expand Down Expand Up @@ -49,6 +52,53 @@ var gl_LocalInvocationID : uint3 // this invocation's position within its w
var gl_GlobalInvocationID : uint3 // unique invocation index in the global grid
var gl_LocalInvocationIndex : uint // 1D flattening of gl_LocalInvocationID

// ===== matrix constructors =====
// matCxR(col0, ...) builds a matrix from its columns (GLSL core `mat4(vec4,...)`); daslang matrices
// are column-major, so the emitter lowers the call to one OpCompositeConstruct (glslang's mat4(cols)).
def public float4x4(c0, c1, c2, c3 : float4) : float4x4 {
var m : float4x4
m[0] = c0
m[1] = c1
m[2] = c2
m[3] = c3
return m
}

def public float3x3(c0, c1, c2 : float3) : float3x3 {
var m : float3x3
m[0] = c0
m[1] = c1
m[2] = c2
return m
}

// ===== row-vector times matrix =====
// GLSL's `v * m` (a ROW vector times m) that math lacks: component j is dot(v, column j), exactly
// SPIR-V's OpVectorTimesMatrix, which the emitter lowers to -- so this CPU body runs only on the host.
def public operator *(v : float4; m : float4x4) : float4 {
return float4(dot(v, m[0]), dot(v, m[1]), dot(v, m[2]), dot(v, m[3]))
}

def public operator *(v : float3; m : float3x3) : float3 {
return float3(dot(v, m[0]), dot(v, m[1]), dot(v, m[2]))
}

// ===== vector integer modulo =====
// GLSL's `uvec3 % uvec3` that daslang lacks -> one native OpUMod (the emitter lowers it). Unsigned,
// 3-lane only; signed `%` stays absent since daslang's OpSRem and GLSL's OpSMod differ on sign.
def public operator %(a, b : uint3) : uint3 {
return uint3(a.x % b.x, a.y % b.y, a.z % b.z)
}

// ===== float modulo =====
// GLSL's mod(x,y) = x - y*floor(x/y) (sign-of-DIVISOR, one OpFMod) vs daslang's fmod (sign-of-
// dividend, OpFRem): they differ on a negative dividend, so a mod() port must reach THIS, not fmod.
[unused_argument(x, y), sideeffects]
def public mod(x, y : float) : float { return 0.0 }

[unused_argument(x, y), sideeffects]
def public mod(x, y : float3) : float3 { return float3(0.0, 0.0, 0.0) }

// ===== texture sampling =====
// texture(s, uv): filtered sample with implicit LOD (fragment-only on both rails). textureSize:
// the mip-0 dimensions in texels.
Expand Down
7 changes: 6 additions & 1 deletion doc/source/reference/spirv.rst
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ Type and layout mapping
* - ``struct`` (in ``@uniform``)
- ``OpTypeStruct`` + ``Block``
- std140 member ``Offset``\ s
* - ``struct`` (local / parameter / result)
- ``OpTypeStruct``, undecorated
- none — a value, not an interface block

Matrix · vector and matrix · matrix use SPIR-V's default column-major convention, so a daslang
``M * v`` is ``OpMatrixTimesVector`` with the matrix uploaded as-is.
Expand All @@ -263,7 +266,9 @@ string builders / ``goto`` — these are not shader constructs.
(``OpLoopMerge``), ``break`` / ``continue``, early ``return``, and the ternary ``?:`` (``OpSelect``,
branchless).
* **Operators:** full scalar and vector arithmetic (``+ - * / %``, unary ``-``), comparisons
(``== != < > <= >=``), logical ``&&`` / ``||``, and matrix/vector products.
(``== != < > <= >=``), logical ``&&`` / ``||``, and matrix/vector products. A whole-vector
``==`` / ``!=`` yields a single ``bool`` (like GLSL): the component-wise compare is reduced with
``OpAll`` / ``OpAny``.
* **Math:** ``dot`` (``OpDot``) plus the GLSL.std.450 set — ``sin`` / ``cos`` / ``tan`` / ``pow`` /
``exp`` / ``log`` / ``sqrt`` / ``rsqrt`` / ``floor`` / ``ceil`` / ``fract`` / ``abs`` / ``min`` /
``max`` / ``lerp`` / ``length`` / ``distance`` / ``normalize`` / ``cross`` / ``reflect`` /
Expand Down
13 changes: 13 additions & 0 deletions modules/dasSpirv/spirv/spirv_builder.das
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,19 @@ def public const_uint(var m : SpirvModule; v : uint) : uint {
return id
}

// The zero of ANY type, whatever its shape -- OpConstantNull takes the type id and needs no
// per-component constituents, so one entry serves scalars, vectors, matrices and arrays alike.
// Keyed by that type id, which the type pool has already made unique.
def public const_null(var m : SpirvModule; t : uint) : uint {
let key = "cn_{t}"
let ex = m.const_pool?[key] ?? 0u
return ex if (ex != 0u)
let id = alloc_id(m)
emit(m, SEC_TYPES, SpvOp.ConstantNull, t, id)
m.const_pool |> insert(key, id)
return id
}

// Signed-int and float OpConstants. The literal word is the raw 32-bit pattern: uint(v) carries an
// int's two's-complement bits; reinterpret carries a float's IEEE-754 bits. Keyed separately from
// const_uint so int 2 and uint 2 (distinct types) don't collide in the pool.
Expand Down
Loading
Loading