feat: add gpu driven render graph pipeline

Amp-Thread-ID: https://ampcode.com/threads/T-019f9d91-77c1-7206-a60f-ed6554ce92ab

Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
Amp
2026-07-27 20:26:31 +00:00
co-authored by heaust
parent d4e8634f67
commit 05311e564c
51 changed files with 13262 additions and 743 deletions
+346 -28
View File
@@ -1,29 +1,347 @@
export const GRAPH_ID = "demo_forward";
export const CATALOG_VERSION = 1;
export const socketTypes = {
surface: { title: "Surface", color: "#62b0ff", acceptsFrom: ["surface"] },
depth: { title: "Depth", color: "#b58cff", acceptsFrom: ["depth"] },
};
export const theme = {
background:"#151820",grid:"#292e3a",frame:"#30343a80",frameHeader:"#59616c",body:"#292e39",control:"#191d26",controlFill:"#4775b8",controlEditing:"#101218",textSelection:"#4775b8",outline:"#0b0d12",text:"#edf1f7",muted:"#969eaa",shadow:"#00000088",nodeSelected:"#ff9f43",nodeActive:"#ffffff",unknownHeader:"#555b64",unknownSocket:"#999999",linkMuted:"#d94b4b",knifeMuted:"#e85b5b",emphasis:"#ffffff",focus:"#f5a623",editOutline:"#666a70",resize:"#8b8e95",muteOverlay:"#14141459",boxSelectionFill:"#f5a6231f",checkerLight:"#aaaaaa",checkerDark:"#777777",widgetBorder:"#111216",rampBorder:"#111111",resourceBackground:"#202228"
};
export const styles = { resource:{header:"#3977a8"}, pass:{header:"#426b43"}, output:{header:"#a75d37"} };
const socket = (title, direction, type) => ({ title,direction,type,maxIncomingLinks:direction === "input" ? 1 : 0,visible:true,value:null,showValue:false });
const node = (title, style, sockets, parameters = {}) => ({ version:1,title,behavior:"standard",style,parameters,sockets,ui:[...Object.keys(parameters).map(parameter=>({kind:"parameter",parameter})),...Object.keys(sockets).map(socket=>({kind:"socket",socket}))],muteBypass:[],migrations:[] });
export const nodeDefinitions = {
surface_color: node("Surface Color", "resource", { surface:socket("Surface","output","surface") }),
depth32: node("Depth 32", "resource", { depth:socket("Depth","output","depth") }),
scene_forward: node("Scene Forward", "pass", { color:socket("Color","input","surface"),depth:socket("Depth","input","depth"),result:socket("Result","output","surface") }, {
clearColor:{type:"color",default:{kind:"color",value:[0,0,0,1]},minimum:0,maximum:1},
clearDepth:{type:"number",default:{kind:"number",value:1},minimum:0,maximum:1,step:0.01},
}),
present: node("Present", "output", { surface:socket("Surface","input","surface") }),
};
export const descriptors = Object.freeze({
surface_color:{version:1,sockets:{surface:["output","surface"]},parameters:[]},
depth32:{version:1,sockets:{depth:["output","depth"]},parameters:[]},
scene_forward:{version:1,sockets:{color:["input","surface"],depth:["input","depth"],result:["output","surface"]},parameters:["clearColor","clearDepth"]},
present:{version:1,sockets:{surface:["input","surface"]},parameters:[]},
export const GRAPH_ID = "authored_gpu_culling";
export const CATALOG_VERSION = 2;
const exact = (type) => ({ kind: "exact", types: [type] });
const oneOf = (...types) => ({ kind: "one_of", types });
const i = (type, required = true, authoringType) => ({
accepted: typeof type === "string" ? exact(type) : type,
required,
...(authoringType ? { authoringType } : {}),
});
const o = (type) => ({ type });
const texture = {
residency: "transient",
texture: {
dimension: "d2",
format: "rgba16_float",
extent: {
kind: "surface_relative",
width: { numerator: 1, denominator: 1 },
height: { numerator: 1, denominator: 1 },
depthOrArrayLayers: 1,
},
mipLevelCount: 1,
sampleCount: 1,
viewFormats: [],
},
};
export const semanticCatalog = Object.freeze({
surface_target: {
execution: "source",
inputs: {},
outputs: { surface: o("surface_target") },
parameters: {},
},
texture_spec: {
execution: "source",
inputs: {},
outputs: { spec: o("texture_spec") },
parameters: structuredClone(texture),
},
scene_table: {
execution: "source",
inputs: {},
outputs: { scene: o("scene_table") },
parameters: {},
},
local_aabb_buffer: {
execution: "source",
inputs: { scene: i("scene_table") },
outputs: { localAabbs: o("local_aabb_buffer") },
parameters: {},
},
camera_frustum: {
execution: "source",
inputs: {},
outputs: { frustum: o("camera_frustum") },
parameters: {},
},
visibility_flags: {
execution: "source",
inputs: { scene: i("scene_table") },
outputs: {
flags: {
...o("boolean_flag_buffer"),
authoringType: "visibility_flag_buffer",
},
},
parameters: {},
},
frustum_cull: {
execution: "compute",
inputs: {
scene: i("scene_table"),
localAabbs: i("local_aabb_buffer"),
frustum: i("camera_frustum"),
},
outputs: {
flags: {
...o("boolean_flag_buffer"),
authoringType: "frustum_flag_buffer",
},
},
parameters: {},
},
mesh_query: {
execution: "compute",
inputs: {
scene: i("scene_table"),
isVisible: i("boolean_flag_buffer", false, "visibility_flag_buffer"),
isFrustumCulled: i("boolean_flag_buffer", false, "frustum_flag_buffer"),
},
outputs: { draws: o("draw_stream") },
parameters: {
filters: [
{ flag: "isVisible", predicate: "required_true" },
{ flag: "isFrustumCulled", predicate: "required_false" },
],
},
},
depth_stencil_config: {
execution: "source",
inputs: {},
outputs: { config: o("depth_stencil_config") },
parameters: {
depthCompare: "less_equal",
depthWriteEnabled: true,
clearDepth: 1,
},
},
legacy_forward: {
execution: "render",
inputs: {
scene: i("scene_table"),
draws: i("draw_stream"),
colorTarget: i(oneOf("surface_target", "texture_spec", "texture")),
depthTarget: i(oneOf("texture_spec", "texture")),
depthStencil: i("depth_stencil_config"),
},
outputs: { color: o("texture"), depth: o("texture") },
parameters: { clearColor: [0.015, 0.02, 0.03, 1] },
},
fullscreen_copy: {
execution: "render",
inputs: {
source: i("texture"),
colorTarget: i(oneOf("surface_target", "texture_spec", "texture")),
},
outputs: { color: o("texture") },
parameters: {},
},
tone_map: {
execution: "render",
inputs: {
source: i("texture"),
colorTarget: i(oneOf("surface_target", "texture_spec", "texture")),
},
outputs: { color: o("texture") },
parameters: { exposure: 1 },
},
bloom_extract: {
execution: "render",
inputs: {
source: i("texture"),
colorTarget: i(oneOf("surface_target", "texture_spec", "texture")),
},
outputs: { color: o("texture") },
parameters: { threshold: 1, knee: 0.5 },
},
bloom_blur: {
execution: "render",
inputs: {
source: i("texture"),
colorTarget: i(oneOf("surface_target", "texture_spec", "texture")),
},
outputs: { color: o("texture") },
parameters: { direction: [1, 0], radius: 1 },
},
bloom_composite: {
execution: "render",
inputs: {
source: i("texture"),
bloom: i("texture"),
colorTarget: i(oneOf("surface_target", "texture_spec", "texture")),
},
outputs: { color: o("texture") },
parameters: { intensity: 1 },
},
luminance_edge: {
execution: "render",
inputs: {
source: i("texture"),
colorTarget: i(oneOf("surface_target", "texture_spec", "texture")),
},
outputs: { color: o("texture") },
parameters: { strength: 2 },
},
present: {
execution: "present",
inputs: { surface: i("texture") },
outputs: {},
parameters: {},
},
});
const socketColors = [
"#d17c7c",
"#d19e7c",
"#d1c77c",
"#9ed17c",
"#7cd1a5",
"#7ccbd1",
"#7c98d1",
"#a27cd1",
"#d17cb8",
];
export const socketTypes = Object.fromEntries(
[
"surface_target",
"texture_spec",
"texture",
"scene_table",
"local_aabb_buffer",
"camera_frustum",
"boolean_flag_buffer",
"draw_stream",
"depth_stencil_config",
"visibility_flag_buffer",
"frustum_flag_buffer",
].map((type, index) => [
type,
{
title: type.replaceAll("_", " "),
color: socketColors[index % socketColors.length],
acceptsFrom: [type],
},
]),
);
socketTypes.surface_target.acceptsFrom = [
"surface_target",
"texture_spec",
"texture",
];
socketTypes.texture_spec.acceptsFrom = ["texture_spec", "texture"];
socketTypes.boolean_flag_buffer.acceptsFrom = [
"boolean_flag_buffer",
"visibility_flag_buffer",
"frustum_flag_buffer",
];
export const theme = {
background: "#151820",
grid: "#292e3a",
frame: "#30343a80",
frameHeader: "#59616c",
body: "#292e39",
control: "#24272b",
controlFill: "#4775b8",
controlEditing: "#181a1d",
textSelection: "#4775b8",
outline: "#0b0d12",
text: "#edf1f7",
muted: "#969eaa",
shadow: "#00000088",
nodeSelected: "#ff9f43",
nodeActive: "#ffffff",
unknownHeader: "#555b64",
unknownSocket: "#999999",
linkMuted: "#d94b4b",
knifeMuted: "#e85b5b",
emphasis: "#ffffff",
focus: "#f5a623",
editOutline: "#666a70",
resize: "#8b8e95",
muteOverlay: "#14141459",
boxSelectionFill: "#f5a6231f",
checkerLight: "#aaaaaa",
checkerDark: "#777777",
widgetBorder: "#111216",
rampBorder: "#111111",
resourceBackground: "#202228",
};
export const styles = {
source: { header: "#3977a8" },
compute: { header: "#725a9b" },
render: { header: "#426b43" },
present: { header: "#a75d37" },
};
const socket = (title, direction, type) => ({
title,
direction,
type,
maxIncomingLinks: direction === "input" ? 1 : 0,
visible: true,
value: null,
showValue: false,
});
const parameterSchema = (value) =>
typeof value === "number"
? { type: "number", default: { kind: "number", value } }
: typeof value === "string"
? { type: "string", default: { kind: "string", value } }
: typeof value === "boolean"
? { type: "boolean", default: { kind: "boolean", value } }
: { type: "json", default: { kind: "json", value } };
export const nodeDefinitions = Object.fromEntries(
Object.entries(semanticCatalog).map(([key, c]) => {
const sockets = {
...Object.fromEntries(
Object.entries(c.inputs).map(([n, v]) => [
n,
socket(n, "input", v.authoringType ?? v.accepted.types[0]),
]),
),
...Object.fromEntries(
Object.entries(c.outputs).map(([n, v]) => [
n,
socket(n, "output", v.authoringType ?? v.type),
]),
),
},
parameters = Object.fromEntries(
Object.entries(c.parameters).map(([name, value]) => [
name,
parameterSchema(value),
]),
);
return [
key,
{
version: 1,
title: key.replaceAll("_", " "),
behavior: "standard",
style: c.execution,
parameters,
sockets,
ui: [
...Object.keys(parameters).map((parameter) => ({
kind: "parameter",
parameter,
})),
...Object.keys(sockets).map((socket) => ({ kind: "socket", socket })),
],
muteBypass: [],
migrations: [],
},
];
}),
);
export const fxNodeComposition = Object.freeze({
schemaVersion: 2,
id: "yawn.render-graph",
version: CATALOG_VERSION,
compatibility: { wildcardInputTypes: [] },
socketTypes,
nodeStyles: styles,
resources: {},
theme,
nodes: nodeDefinitions,
});
export const descriptors = Object.fromEntries(
Object.entries(semanticCatalog).map(([key, c]) => [
key,
{
version: 1,
inputs: c.inputs,
outputs: c.outputs,
parameters: c.parameters,
},
]),
);