2026-07-28 22:37:53 +01:00
|
|
|
//! ----------------------------------------------------
|
2026-07-31 01:11:10 +01:00
|
|
|
//! TODO: Move [materials, materials_gpu and GPUMaterial somewhere else]
|
|
|
|
|
//! Depricated
|
2026-07-28 22:37:53 +01:00
|
|
|
//! ----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
const std = @import("std");
|
|
|
|
|
const gtl = @import("gtl");
|
|
|
|
|
const wgpu = @import("wgpu");
|
|
|
|
|
const Self = @This();
|
|
|
|
|
|
2026-07-30 12:37:17 +02:00
|
|
|
const Material = @import("../../renderer/template/material.zig");
|
2026-07-28 22:37:53 +01:00
|
|
|
const ShaderCache = @import("shader_cache.zig");
|
2026-07-29 21:36:00 +01:00
|
|
|
const ShaderID = ShaderCache.ShaderID;
|
2026-07-29 02:06:24 +01:00
|
|
|
const Vertex = @import("mesh_cache.zig").Vertex;
|
2026-07-28 22:37:53 +01:00
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const GPUPipeline = struct {
|
|
|
|
|
raw: *wgpu.WGPURenderPipelineImpl,
|
2026-07-29 21:36:00 +01:00
|
|
|
layout: *wgpu.WGPUPipelineLayoutImpl,
|
2026-07-31 01:11:10 +01:00
|
|
|
bind_group_layouts: [3]*wgpu.WGPUBindGroupLayoutImpl,
|
2026-07-28 22:37:53 +01:00
|
|
|
};
|
|
|
|
|
|
2026-07-29 02:06:24 +01:00
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
2026-07-29 21:36:00 +01:00
|
|
|
pub const GPUMaterial = struct {
|
2026-07-29 02:06:24 +01:00
|
|
|
buffer: *wgpu.WGPUBufferImpl,
|
|
|
|
|
bind_group: *wgpu.WGPUBindGroupImpl,
|
2026-07-30 12:37:17 +02:00
|
|
|
dirty: bool = true,
|
2026-07-29 02:06:24 +01:00
|
|
|
};
|
|
|
|
|
|
2026-07-31 01:11:10 +01:00
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// TODO: Mabe mabe switch to it, someday
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const GPUUniform = struct {
|
|
|
|
|
buffer: wgpu.WGPUBuffer,
|
|
|
|
|
bind_group: wgpu.WGPUBindGroup,
|
|
|
|
|
dirty: bool = true,
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-28 22:37:53 +01:00
|
|
|
//
|
|
|
|
|
// FIELDS
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
pipelines: gtl.KeyedSlotMap(Material.Unique, GPUPipeline),
|
2026-07-29 21:36:00 +01:00
|
|
|
materials: gtl.SlotMap(Material),
|
|
|
|
|
materials_gpu: gtl.KeyedSlotMap(gtl.Handle(Material), GPUMaterial),
|
2026-07-28 22:37:53 +01:00
|
|
|
shader_cache: *ShaderCache,
|
|
|
|
|
queue: *wgpu.WGPUQueueImpl,
|
|
|
|
|
device: *wgpu.WGPUDeviceImpl,
|
|
|
|
|
alloc: std.mem.Allocator,
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn init(
|
|
|
|
|
shader_cache: *ShaderCache,
|
|
|
|
|
queue: *wgpu.WGPUQueueImpl,
|
|
|
|
|
device: *wgpu.WGPUDeviceImpl,
|
|
|
|
|
alloc: std.mem.Allocator,
|
|
|
|
|
) Self {
|
|
|
|
|
return .{
|
|
|
|
|
.pipelines = .init(alloc),
|
2026-07-29 15:56:52 +01:00
|
|
|
.materials = .init(alloc),
|
2026-07-29 21:36:00 +01:00
|
|
|
.materials_gpu = .init(alloc),
|
2026-07-28 22:37:53 +01:00
|
|
|
.shader_cache = shader_cache,
|
|
|
|
|
.queue = queue,
|
|
|
|
|
.device = device,
|
|
|
|
|
.alloc = alloc,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn deinit(self: *Self) void {
|
|
|
|
|
// --- FREE PIPELINES ---
|
|
|
|
|
for (self.pipelines.items()) |entry| {
|
2026-07-30 12:37:17 +02:00
|
|
|
wgpu.wgpuPipelineLayoutRelease(entry.value.layout);
|
2026-07-28 22:37:53 +01:00
|
|
|
wgpu.wgpuRenderPipelineRelease(entry.value.raw);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-29 15:56:52 +01:00
|
|
|
// --- FREE MATERIALS ---
|
2026-07-29 21:36:00 +01:00
|
|
|
for (self.materials_gpu.items()) |entry| {
|
|
|
|
|
wgpu.wgpuBufferRelease(entry.value.buffer);
|
|
|
|
|
wgpu.wgpuBindGroupRelease(entry.value.bind_group);
|
2026-07-29 15:56:52 +01:00
|
|
|
}
|
|
|
|
|
|
2026-07-28 22:37:53 +01:00
|
|
|
// --- # ---
|
|
|
|
|
self.pipelines.deinit();
|
2026-07-29 15:56:52 +01:00
|
|
|
self.materials.deinit();
|
2026-07-29 21:36:00 +01:00
|
|
|
self.materials_gpu.deinit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn getOrCreateMaterial(
|
|
|
|
|
self: *Self,
|
2026-07-30 12:37:17 +02:00
|
|
|
material_handle: gtl.Handle(Material),
|
2026-07-29 21:36:00 +01:00
|
|
|
) !*GPUMaterial {
|
2026-07-30 12:37:17 +02:00
|
|
|
return try self.materials_gpu.getOrPut(
|
|
|
|
|
material_handle,
|
2026-07-29 21:36:00 +01:00
|
|
|
blk: {
|
2026-07-30 12:37:17 +02:00
|
|
|
// --- # ---
|
|
|
|
|
if (self.materials_gpu.getByKey(material_handle)) |v| return v;
|
|
|
|
|
const material = self.materials.get(material_handle) orelse return error.MaterialNotFound;
|
|
|
|
|
|
|
|
|
|
// --- ENSURE PIPELINE ---
|
|
|
|
|
const pipeline = try self.getOrCreatePipeline(material);
|
|
|
|
|
|
2026-07-29 21:36:00 +01:00
|
|
|
// --- UNIFORM ---
|
|
|
|
|
const uniform = wgpu.wgpuDeviceCreateBuffer(self.device, &wgpu.WGPUBufferDescriptor{
|
|
|
|
|
.usage = wgpu.WGPUBufferUsage_CopyDst | wgpu.WGPUBufferUsage_Uniform,
|
|
|
|
|
.size = @sizeOf(Material.Style),
|
|
|
|
|
}) orelse return error.Buffer;
|
|
|
|
|
|
2026-07-30 12:37:17 +02:00
|
|
|
// --- WRITE TO UNIFORM ---
|
2026-07-29 21:36:00 +01:00
|
|
|
wgpu.wgpuQueueWriteBuffer(
|
|
|
|
|
self.queue,
|
|
|
|
|
uniform,
|
|
|
|
|
0,
|
|
|
|
|
&material.style,
|
|
|
|
|
@sizeOf(Material.Style),
|
|
|
|
|
);
|
|
|
|
|
|
2026-07-30 12:37:17 +02:00
|
|
|
// --- BIND GROUP ---
|
|
|
|
|
const bind_group = wgpu.wgpuDeviceCreateBindGroup(self.device, &wgpu.WGPUBindGroupDescriptor{
|
|
|
|
|
.layout = pipeline.bind_group_layouts[0],
|
|
|
|
|
.entryCount = 1,
|
|
|
|
|
.entries = &[_]wgpu.WGPUBindGroupEntry{
|
|
|
|
|
.{
|
|
|
|
|
.binding = 0,
|
|
|
|
|
.buffer = uniform,
|
|
|
|
|
.offset = 0,
|
|
|
|
|
.size = @sizeOf(Material.Style),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}) orelse return error.BindGroup;
|
|
|
|
|
|
2026-07-29 21:36:00 +01:00
|
|
|
// --- RESULT ---
|
|
|
|
|
break :blk .{
|
|
|
|
|
.buffer = uniform,
|
2026-07-30 12:37:17 +02:00
|
|
|
.bind_group = bind_group,
|
|
|
|
|
.dirty = false,
|
2026-07-29 21:36:00 +01:00
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
);
|
2026-07-28 22:37:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
2026-07-30 12:37:17 +02:00
|
|
|
pub fn getOrCreatePipeline(
|
2026-07-28 22:37:53 +01:00
|
|
|
self: *Self,
|
2026-07-30 12:37:17 +02:00
|
|
|
material: *const Material,
|
2026-07-28 22:37:53 +01:00
|
|
|
) !*GPUPipeline {
|
2026-07-30 12:37:17 +02:00
|
|
|
return try self.pipelines.getOrPut(material.unique, blk: {
|
|
|
|
|
// --- # ---
|
|
|
|
|
if (self.pipelines.getByKey(material.unique)) |v| return v;
|
|
|
|
|
|
2026-07-31 01:11:10 +01:00
|
|
|
//
|
|
|
|
|
// LAYOUTS
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
// --- CAMERA ---
|
|
|
|
|
const camera_layout = wgpu.wgpuDeviceCreateBindGroupLayout(self.device, &wgpu.WGPUBindGroupLayoutDescriptor{
|
|
|
|
|
.entryCount = 1,
|
|
|
|
|
.entries = &[_]wgpu.WGPUBindGroupLayoutEntry{
|
|
|
|
|
.{
|
|
|
|
|
.binding = 0,
|
|
|
|
|
.visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment,
|
|
|
|
|
.buffer = .{
|
|
|
|
|
.type = wgpu.WGPUBufferBindingType_Uniform,
|
|
|
|
|
.minBindingSize = @sizeOf(Material.Style),
|
|
|
|
|
.hasDynamicOffset = 0,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}) orelse return error.BindGroupLayout;
|
|
|
|
|
|
|
|
|
|
// --- MODEL ---
|
|
|
|
|
const model_layout = wgpu.wgpuDeviceCreateBindGroupLayout(self.device, &wgpu.WGPUBindGroupLayoutDescriptor{
|
|
|
|
|
.entryCount = 1,
|
|
|
|
|
.entries = &[_]wgpu.WGPUBindGroupLayoutEntry{
|
|
|
|
|
.{
|
|
|
|
|
.binding = 0,
|
|
|
|
|
.visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment,
|
|
|
|
|
.buffer = .{
|
|
|
|
|
.type = wgpu.WGPUBufferBindingType_Uniform,
|
|
|
|
|
.minBindingSize = @sizeOf(Material.Style),
|
|
|
|
|
.hasDynamicOffset = 0,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}) orelse return error.BindGroupLayout;
|
|
|
|
|
|
|
|
|
|
// --- MATERIAL ---
|
2026-07-30 12:37:17 +02:00
|
|
|
const material_layout = wgpu.wgpuDeviceCreateBindGroupLayout(self.device, &wgpu.WGPUBindGroupLayoutDescriptor{
|
|
|
|
|
.entryCount = 1,
|
|
|
|
|
.entries = &[_]wgpu.WGPUBindGroupLayoutEntry{
|
|
|
|
|
.{
|
|
|
|
|
.binding = 0,
|
|
|
|
|
.visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment,
|
|
|
|
|
.buffer = .{
|
|
|
|
|
.type = wgpu.WGPUBufferBindingType_Uniform,
|
|
|
|
|
.minBindingSize = @sizeOf(Material.Style),
|
|
|
|
|
.hasDynamicOffset = 0,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}) orelse return error.BindGroupLayout;
|
|
|
|
|
|
2026-07-31 01:11:10 +01:00
|
|
|
var bind_group_layouts = [_]wgpu.WGPUBindGroupLayout{
|
|
|
|
|
camera_layout,
|
|
|
|
|
model_layout,
|
|
|
|
|
material_layout,
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-30 12:37:17 +02:00
|
|
|
// --- PIPELINE LAYOUT ---
|
|
|
|
|
const pipeline_layout = wgpu.wgpuDeviceCreatePipelineLayout(
|
|
|
|
|
self.device,
|
|
|
|
|
&wgpu.WGPUPipelineLayoutDescriptor{
|
2026-07-31 01:11:10 +01:00
|
|
|
.bindGroupLayoutCount = 3,
|
|
|
|
|
.bindGroupLayouts = &bind_group_layouts,
|
2026-07-30 12:37:17 +02:00
|
|
|
},
|
|
|
|
|
) orelse return error.Layout;
|
|
|
|
|
|
|
|
|
|
// --- CREATE PIPELINE ---
|
|
|
|
|
const pipeline = try self.createPipeline(
|
|
|
|
|
material.unique,
|
|
|
|
|
pipeline_layout,
|
|
|
|
|
);
|
2026-07-29 21:36:00 +01:00
|
|
|
|
|
|
|
|
// --- RESULT ---
|
|
|
|
|
break :blk .{
|
2026-07-30 12:37:17 +02:00
|
|
|
.raw = pipeline,
|
|
|
|
|
.layout = pipeline_layout,
|
|
|
|
|
.bind_group_layouts = .{
|
2026-07-31 01:11:10 +01:00
|
|
|
camera_layout,
|
|
|
|
|
model_layout,
|
2026-07-30 12:37:17 +02:00
|
|
|
material_layout,
|
|
|
|
|
},
|
2026-07-29 21:36:00 +01:00
|
|
|
};
|
2026-07-28 22:37:53 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
fn createPipeline(
|
|
|
|
|
self: *Self,
|
|
|
|
|
key: Material.Unique,
|
2026-07-29 21:36:00 +01:00
|
|
|
layout: *wgpu.WGPUPipelineLayoutImpl,
|
2026-07-28 22:37:53 +01:00
|
|
|
) !*wgpu.WGPURenderPipelineImpl {
|
|
|
|
|
// --- ENSURE SHADER MODULE ---
|
|
|
|
|
const shader_module = try self.shader_cache.getOrCreate(
|
|
|
|
|
key.shader,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// --- RESULT ---
|
|
|
|
|
return wgpu.wgpuDeviceCreateRenderPipeline(self.device, &wgpu.WGPURenderPipelineDescriptor{
|
|
|
|
|
.label = .{ .data = @tagName(key.shader), .length = wgpu.WGPU_STRLEN },
|
2026-07-30 12:37:17 +02:00
|
|
|
.layout = layout,
|
2026-07-28 22:37:53 +01:00
|
|
|
.vertex = .{
|
|
|
|
|
.module = shader_module.raw,
|
|
|
|
|
.entryPoint = .{ .data = "vs_main", .length = wgpu.WGPU_STRLEN },
|
2026-07-29 01:15:31 +01:00
|
|
|
.bufferCount = 1,
|
|
|
|
|
.buffers = &wgpu.WGPUVertexBufferLayout{
|
2026-07-29 02:06:24 +01:00
|
|
|
.attributeCount = 3,
|
|
|
|
|
.attributes = &[_]wgpu.WGPUVertexAttribute{ // TODO: Mabe mabe allow for custom, mabe mabe later tho
|
|
|
|
|
.{
|
|
|
|
|
.format = wgpu.WGPUVertexFormat_Float32x3,
|
|
|
|
|
.offset = @offsetOf(Vertex, "position"),
|
|
|
|
|
.shaderLocation = 0,
|
|
|
|
|
},
|
|
|
|
|
.{
|
|
|
|
|
.format = wgpu.WGPUVertexFormat_Float32x3,
|
|
|
|
|
.offset = @offsetOf(Vertex, "normal"),
|
|
|
|
|
.shaderLocation = 1,
|
|
|
|
|
},
|
|
|
|
|
.{
|
|
|
|
|
.format = wgpu.WGPUVertexFormat_Float32x2,
|
|
|
|
|
.offset = @offsetOf(Vertex, "uv"),
|
|
|
|
|
.shaderLocation = 2,
|
|
|
|
|
},
|
2026-07-29 01:15:31 +01:00
|
|
|
},
|
2026-07-29 02:06:24 +01:00
|
|
|
.arrayStride = @sizeOf(Vertex),
|
2026-07-29 01:15:31 +01:00
|
|
|
.stepMode = wgpu.WGPUVertexStepMode_Vertex,
|
|
|
|
|
},
|
2026-07-28 22:37:53 +01:00
|
|
|
},
|
|
|
|
|
.fragment = &wgpu.WGPUFragmentState{
|
|
|
|
|
.module = shader_module.raw,
|
|
|
|
|
.entryPoint = .{ .data = "fs_main", .length = wgpu.WGPU_STRLEN },
|
|
|
|
|
.targetCount = 1,
|
|
|
|
|
.targets = &wgpu.WGPUColorTargetState{
|
2026-07-29 01:15:31 +01:00
|
|
|
.format = 28, // FIX: Use proper format
|
2026-07-28 22:37:53 +01:00
|
|
|
.blend = &wgpu.WGPUBlendState{
|
|
|
|
|
.color = .{
|
|
|
|
|
.srcFactor = wgpu.WGPUBlendFactor_SrcAlpha,
|
|
|
|
|
.dstFactor = wgpu.WGPUBlendFactor_OneMinusSrcAlpha,
|
|
|
|
|
.operation = wgpu.WGPUBlendOperation_Add,
|
|
|
|
|
},
|
|
|
|
|
.alpha = .{
|
|
|
|
|
.srcFactor = wgpu.WGPUBlendFactor_Zero,
|
|
|
|
|
.dstFactor = wgpu.WGPUBlendFactor_One,
|
|
|
|
|
.operation = wgpu.WGPUBlendOperation_Add,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
.writeMask = wgpu.WGPUColorWriteMask_All,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
.primitive = .{
|
|
|
|
|
.topology = switch (key.topology) {
|
|
|
|
|
.triangle_list => wgpu.WGPUPrimitiveTopology_TriangleList,
|
|
|
|
|
.point_list => wgpu.WGPUPrimitiveTopology_PointList,
|
|
|
|
|
.line_list => wgpu.WGPUPrimitiveTopology_LineList,
|
|
|
|
|
},
|
|
|
|
|
.frontFace = switch (key.front_face) {
|
|
|
|
|
.ccw => wgpu.WGPUFrontFace_CCW,
|
|
|
|
|
.cw => wgpu.WGPUFrontFace_CW,
|
|
|
|
|
},
|
|
|
|
|
.cullMode = switch (key.cull_mode) {
|
|
|
|
|
.back => wgpu.WGPUCullMode_Back,
|
|
|
|
|
.front => wgpu.WGPUCullMode_Front,
|
|
|
|
|
.none => wgpu.WGPUCullMode_None,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
.multisample = .{
|
|
|
|
|
.count = 1,
|
|
|
|
|
.mask = ~@as(u32, 0),
|
|
|
|
|
.alphaToCoverageEnabled = 0,
|
|
|
|
|
},
|
|
|
|
|
}) orelse {
|
|
|
|
|
return error.PipelineCreationFailed;
|
|
|
|
|
};
|
|
|
|
|
}
|