2026-07-28 22:37:53 +01:00
|
|
|
//! ----------------------------------------------------
|
|
|
|
|
//! ----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
const std = @import("std");
|
|
|
|
|
const gtl = @import("gtl");
|
|
|
|
|
const wgpu = @import("wgpu");
|
|
|
|
|
const Self = @This();
|
|
|
|
|
|
|
|
|
|
const Material = @import("../../common/component/material.zig");
|
|
|
|
|
const ShaderCache = @import("shader_cache.zig");
|
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 02:06:24 +01:00
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const GPUMaterial = struct { // TODO: Should prob create Material Cache instead
|
|
|
|
|
buffer: *wgpu.WGPUBufferImpl,
|
|
|
|
|
bind_group: *wgpu.WGPUBindGroupImpl,
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-28 22:37:53 +01:00
|
|
|
//
|
|
|
|
|
// FIELDS
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
pipelines: gtl.KeyedSlotMap(Material.Unique, GPUPipeline),
|
2026-07-29 15:56:52 +01:00
|
|
|
materials: gtl.SlotMap(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-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| {
|
|
|
|
|
wgpu.wgpuRenderPipelineRelease(entry.value.raw);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-29 15:56:52 +01:00
|
|
|
// --- FREE MATERIALS ---
|
|
|
|
|
for (self.materials.items()) |gpu| {
|
|
|
|
|
wgpu.wgpuBufferRelease(gpu.buffer);
|
|
|
|
|
wgpu.wgpuBindGroupRelease(gpu.bind_group);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-28 22:37:53 +01:00
|
|
|
// --- # ---
|
|
|
|
|
self.pipelines.deinit();
|
2026-07-29 15:56:52 +01:00
|
|
|
self.materials.deinit();
|
2026-07-28 22:37:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn getOrCreate(
|
|
|
|
|
self: *Self,
|
|
|
|
|
key: Material.Unique,
|
|
|
|
|
) !*GPUPipeline {
|
|
|
|
|
return try self.pipelines.getOrPut(key, .{
|
|
|
|
|
.raw = try self.createPipeline(key),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
fn createPipeline(
|
|
|
|
|
self: *Self,
|
|
|
|
|
key: Material.Unique,
|
|
|
|
|
) !*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 },
|
|
|
|
|
.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;
|
|
|
|
|
};
|
|
|
|
|
}
|