// The raw shading layer: compileShader compiles one stage from complete GLSL
// ES (nothing injected - the source declares its own #version, precision,
// varyings and uniforms), linkProgram links a vertex and a fragment stage
// into a program, createRenderPipeline pairs the program with draw state
// (none here: attributeless triangles are the defaults), and
// createShaderTarget builds a render target over the pipeline. One program
// can back many pipelines, one pipeline many targets, and only the compile
// step compiles, so swapping precompiled programs is free of compilation.
// Stages can be destroyed right after linking; the program keeps its own
// compiled copies.
//
// A raw program carries its own vertex stage, so a fullscreen pass is a
// covering triangle from gl_VertexID with vertexCount: 3. Compare
// gpu-shader.tsx, where the fused createShaderTexture does all of this in
// one call with an injected preamble.
import { render, onFrame, createSignal } from "@solidrt/core"
import {
compileShader,
createRenderPipeline,
createShaderTarget,
destroyShader,
glsl,
linkProgram,
} from "@solidrt/core/gpu"
let VERTEX = glsl`#version 300 es
precision highp float;
out vec2 vUV;
void main() {
vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
vUV = p;
gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);
}
`
// Two fragment stages linked against the same vertex stage: two programs,
// one shared compile of the vertex half.
let WAVES = glsl`#version 300 es
precision highp float;
in vec2 vUV;
out vec4 fragColor;
uniform float uTime;
void main() {
float t = uTime * 2.0;
float a = 0.5 + 0.5 * sin(vUV.x * 10.0 + t);
float b = 0.5 + 0.5 * sin(vUV.y * 10.0 - t * 1.3);
fragColor = vec4(a, b, 1.0 - a * b, 1.0);
}
`
// The standard header ({ header: true }) declares #version, precision,
// iResolution and fragColor, so this source only adds its own inputs -
// including the app-driven time uniform.
let RINGS = glsl`
in vec2 vUV;
uniform float uTime;
void main() {
float d = length(vUV - 0.5);
float r = 0.5 + 0.5 * sin(d * 40.0 - uTime * 3.0);
fragColor = vec4(r, r * 0.6, 1.0 - r, 1.0);
}
`
function App() {
let vs = compileShader("vertex", VERTEX)
let wavesFs = compileShader("fragment", WAVES)
let ringsFs = compileShader("fragment", RINGS, { header: true })
// Labels name each object in the dev tooling's GPU inventory, which is
// what keeps a chain of programs, pipelines, and targets readable. They
// are free-form and need not be unique - one name per chain works, since
// the inventory already groups by kind.
let waves = linkProgram(vs, wavesFs, { label: "waves" })
let rings = linkProgram(vs, ringsFs, { label: "rings" })
destroyShader(vs)
destroyShader(wavesFs)
destroyShader(ringsFs)
let wavesPipeline = createRenderPipeline(waves, { label: "waves" })
let ringsPipeline = createRenderPipeline(rings, { label: "rings" })
let wavesId = createShaderTarget(wavesPipeline, 512, 512, { uTime: 0 }, { vertexCount: 3, label: "waves" })
let ringsId = createShaderTarget(ringsPipeline, 512, 512, { uTime: 0 }, { vertexCount: 3, label: "rings" })
let [time, setTime] = createSignal(0)
onFrame(tick => setTime(tick / 1000))
return (
)
}
render(() => )