//! ---------------------------------------------------- //! ---------------------------------------------------- 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"); /// ---------------------------------------------------- /// ---------------------------------------------------- pub const GPUPipeline = struct { raw: *wgpu.WGPURenderPipelineImpl, }; // // FIELDS // pipelines: gtl.KeyedSlotMap(Material.Unique, GPUPipeline), 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), .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); } // --- # --- self.pipelines.deinit(); } /// ---------------------------------------------------- /// ---------------------------------------------------- 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 }, }, .fragment = &wgpu.WGPUFragmentState{ .module = shader_module.raw, .entryPoint = .{ .data = "fs_main", .length = wgpu.WGPU_STRLEN }, .targetCount = 1, .targets = &wgpu.WGPUColorTargetState{ .format = 28, .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; }; }