From c5941773852e172a9759362db04da191bff15474 Mon Sep 17 00:00:00 2001 From: Interrupt Date: Mon, 23 Sep 2024 23:14:30 -0700 Subject: [PATCH] Fixing up examples, fixing bug when making common pipelines --- src/examples/collision.zig | 5 +- src/examples/fonts.zig | 2 +- src/examples/forest.zig | 2 +- src/examples/frustums.zig | 2 +- src/examples/lighting.zig | 4 +- src/examples/meshbuilder.zig | 4 +- src/examples/meshes.zig | 8 +- src/examples/quakemap.zig | 2 +- src/examples/rays.zig | 2 +- src/examples/skinned-meshes.zig | 10 +-- src/examples/sprites.zig | 4 +- src/framework/graphics/shaders.zig | 2 +- .../platform/backends/sokol/graphics.zig | 77 ++++++++++--------- src/framework/platform/graphics.zig | 22 ++++-- src/framework/utils/quakemdl.zig | 2 +- 15 files changed, 77 insertions(+), 71 deletions(-) diff --git a/src/examples/collision.zig b/src/examples/collision.zig index babcb934..1baa0fc4 100644 --- a/src/examples/collision.zig +++ b/src/examples/collision.zig @@ -18,6 +18,8 @@ const Vec2 = math.Vec2; const Rect = delve.spatial.Rect; const TextureRegion = delve.graphics.sprites.TextureRegion; +const def_shader = delve.shaders.default; + var shader_default: graphics.Shader = undefined; var sprite_batch: batcher.SpriteBatcher = undefined; @@ -58,14 +60,13 @@ pub fn registerModule() !void { fn on_init() !void { debug.log("Collision example module initializing", .{}); + shader_default = try graphics.Shader.initDefault(.{}); sprite_batch = batcher.SpriteBatcher.init(.{}) catch { debug.showErrorScreen("Fatal error during batch init!"); return; }; - shader_default = graphics.Shader.initDefault(.{}); - graphics.setClearColor(colors.examples_bg_light); } diff --git a/src/examples/fonts.zig b/src/examples/fonts.zig index 19ebb678..9c85d0c6 100644 --- a/src/examples/fonts.zig +++ b/src/examples/fonts.zig @@ -67,7 +67,7 @@ fn on_init() !void { _ = try delve.fonts.loadFont("IBMPlexSerif", "assets/fonts/IBMPlexSerif-Regular.ttf", 1024, 200); // make a shader with alpha blending - shader_blend = graphics.Shader.initDefault(.{ .blend_mode = graphics.BlendMode.BLEND }); + shader_blend = try graphics.Shader.initDefault(.{ .blend_mode = graphics.BlendMode.BLEND }); } fn on_tick(delta: f32) void { diff --git a/src/examples/forest.zig b/src/examples/forest.zig index 17b77d63..444c0627 100644 --- a/src/examples/forest.zig +++ b/src/examples/forest.zig @@ -192,7 +192,7 @@ fn on_init() !void { tex_treesheet = graphics.Texture.init(&treesheet_img); // make our default shader - shader_blend = graphics.Shader.initDefault(.{ .blend_mode = .NONE, .cull_mode = .NONE }); + shader_blend = try graphics.Shader.initDefault(.{ .blend_mode = .NONE, .cull_mode = .NONE }); // set the sky color graphics.setClearColor(sky_color); diff --git a/src/examples/frustums.zig b/src/examples/frustums.zig index 75adf3ca..70de216d 100644 --- a/src/examples/frustums.zig +++ b/src/examples/frustums.zig @@ -45,7 +45,7 @@ pub fn main() !void { } pub fn on_init() !void { - shader = delve.platform.graphics.Shader.initFromBuiltin(.{ .vertex_attributes = delve.graphics.mesh.getShaderAttributes() }, delve.shaders.default_mesh).?; + shader = try delve.platform.graphics.Shader.initFromBuiltin(.{ .vertex_attributes = delve.graphics.mesh.getShaderAttributes() }, delve.shaders.default_mesh); // Create some materials material_frustum = try delve.platform.graphics.Material.init(.{ diff --git a/src/examples/lighting.zig b/src/examples/lighting.zig index 98654bdf..d12b8852 100644 --- a/src/examples/lighting.zig +++ b/src/examples/lighting.zig @@ -85,8 +85,8 @@ fn on_init() !void { camera.direction = Vec3.new(0.0, 0.0, 1.0); // make shaders for skinned and unskinned meshes - skinned_shader = graphics.Shader.initFromBuiltin(.{ .vertex_attributes = skinned_mesh.getSkinnedShaderAttributes() }, skinned_lit_shader).?; - static_shader = graphics.Shader.initFromBuiltin(.{ .vertex_attributes = delve.graphics.mesh.getShaderAttributes() }, lit_shader).?; + skinned_shader = try graphics.Shader.initFromBuiltin(.{ .vertex_attributes = skinned_mesh.getSkinnedShaderAttributes() }, skinned_lit_shader); + static_shader = try graphics.Shader.initFromBuiltin(.{ .vertex_attributes = delve.graphics.mesh.getShaderAttributes() }, lit_shader); var base_img: images.Image = try images.loadFile(mesh_texture_file); defer base_img.deinit(); diff --git a/src/examples/meshbuilder.zig b/src/examples/meshbuilder.zig index f01f6151..3a242894 100644 --- a/src/examples/meshbuilder.zig +++ b/src/examples/meshbuilder.zig @@ -51,11 +51,11 @@ pub fn on_init() !void { defer img.deinit(); const tex = graphics.Texture.init(&img); - const shader = graphics.Shader.initFromBuiltin(.{ .vertex_attributes = delve.graphics.mesh.getShaderAttributes() }, delve.shaders.default_mesh); + const shader = try graphics.Shader.initFromBuiltin(.{ .vertex_attributes = delve.graphics.mesh.getShaderAttributes() }, delve.shaders.default_mesh); // Create a material out of the texture material = try graphics.Material.init(.{ - .shader = shader.?, + .shader = shader, .own_shader = true, .texture_0 = tex, .samplers = &[_]graphics.FilterMode{.NEAREST}, diff --git a/src/examples/meshes.zig b/src/examples/meshes.zig index f6cec0fb..53184736 100644 --- a/src/examples/meshes.zig +++ b/src/examples/meshes.zig @@ -90,13 +90,7 @@ fn on_init() !void { const tex_emissive = graphics.Texture.init(&emissive_img); // Make our emissive shader from one that is pre-compiled - const loaded_shader = graphics.Shader.initFromBuiltin(.{ .vertex_attributes = mesh.getShaderAttributes() }, emissive_shader_builtin); - - if (loaded_shader == null) { - debug.log("Could not get emissive shader", .{}); - return; - } - shader = loaded_shader.?; + shader = try graphics.Shader.initFromBuiltin(.{ .vertex_attributes = mesh.getShaderAttributes() }, emissive_shader_builtin); // Create a material out of our shader and textures material = try graphics.Material.init(.{ diff --git a/src/examples/quakemap.zig b/src/examples/quakemap.zig index 11f29148..b85a2675 100644 --- a/src/examples/quakemap.zig +++ b/src/examples/quakemap.zig @@ -149,7 +149,7 @@ pub fn on_init() !void { var err: delve.utils.quakemap.ErrorInfo = undefined; quake_map = try delve.utils.quakemap.QuakeMap.read(allocator, test_map_file, map_transform, &err); - shader = graphics.Shader.initDefault(.{ .vertex_attributes = delve.graphics.mesh.getShaderAttributes() }); + shader = try graphics.Shader.initDefault(.{ .vertex_attributes = delve.graphics.mesh.getShaderAttributes() }); // Create a material out of the texture fallback_material = try graphics.Material.init(.{ diff --git a/src/examples/rays.zig b/src/examples/rays.zig index cd4ed4ff..a1b28268 100644 --- a/src/examples/rays.zig +++ b/src/examples/rays.zig @@ -45,7 +45,7 @@ pub fn main() !void { } pub fn on_init() !void { - const shader = delve.platform.graphics.Shader.initFromBuiltin(.{ .vertex_attributes = delve.graphics.mesh.getShaderAttributes() }, delve.shaders.default_mesh); + const shader = try delve.platform.graphics.Shader.initFromBuiltin(.{ .vertex_attributes = delve.graphics.mesh.getShaderAttributes() }, delve.shaders.default_mesh); // Create some materials material_frustum = try delve.platform.graphics.Material.init(.{ diff --git a/src/examples/skinned-meshes.zig b/src/examples/skinned-meshes.zig index fd0bf6d0..2a721d7c 100644 --- a/src/examples/skinned-meshes.zig +++ b/src/examples/skinned-meshes.zig @@ -78,23 +78,19 @@ fn on_init() !void { camera.direction = Vec3.new(0.0, 0.0, 1.0); // Make our emissive shader from one that is pre-compiled - const shader = graphics.Shader.initFromBuiltin(.{ .vertex_attributes = skinned_mesh.getSkinnedShaderAttributes() }, shader_builtin); - - if (shader == null) { - debug.log("Could not get shader", .{}); - return; - } + const shader = try graphics.Shader.initFromBuiltin(.{ .vertex_attributes = skinned_mesh.getSkinnedShaderAttributes() }, shader_builtin); var base_img: images.Image = images.loadFile(mesh_texture_file) catch { debug.log("Assets: Error loading image asset: {s}", .{mesh_texture_file}); return; }; defer base_img.deinit(); + const tex_base = graphics.Texture.init(&base_img); // Create a material out of our shader and textures material = try delve.platform.graphics.Material.init(.{ - .shader = shader.?, + .shader = shader, .own_shader = true, .texture_0 = tex_base, .texture_1 = delve.platform.graphics.createSolidTexture(0x00000000), diff --git a/src/examples/sprites.zig b/src/examples/sprites.zig index 1d098bac..44190ab1 100644 --- a/src/examples/sprites.zig +++ b/src/examples/sprites.zig @@ -91,8 +91,8 @@ fn on_init() !void { texture_2 = graphics.Texture.init(&test_image_2); // make some shaders for testing - shader_opaque = graphics.Shader.initDefault(.{}); - shader_blend = graphics.Shader.initDefault(.{ .blend_mode = graphics.BlendMode.BLEND }); + shader_opaque = try graphics.Shader.initDefault(.{}); + shader_blend = try graphics.Shader.initDefault(.{ .blend_mode = graphics.BlendMode.BLEND }); // Create some test materials out of our shader and textures test_material_1 = try graphics.Material.init(.{ diff --git a/src/framework/graphics/shaders.zig b/src/framework/graphics/shaders.zig index 798f8ee3..fd40d89c 100644 --- a/src/framework/graphics/shaders.zig +++ b/src/framework/graphics/shaders.zig @@ -34,7 +34,7 @@ pub fn loadFromYaml(cfg: graphics.ShaderConfig, file_path: []const u8) !?graphic break; } - return graphics.Shader.initFromShaderInfo(cfg, shader_info); + return try graphics.Shader.initFromShaderInfo(cfg, shader_info); } fn loadShaderSource(allocator: std.mem.Allocator, shader_path: []const u8) ![:0]const u8 { diff --git a/src/framework/platform/backends/sokol/graphics.zig b/src/framework/platform/backends/sokol/graphics.zig index 6af6a294..837d575f 100644 --- a/src/framework/platform/backends/sokol/graphics.zig +++ b/src/framework/platform/backends/sokol/graphics.zig @@ -239,20 +239,22 @@ pub const ShaderImpl = struct { is_instance: bool = false, // One shader can have many pipelines, so different VertexLayouts can apply it - sokol_pipelines: *std.ArrayList(PipelineBinding), + sokol_pipelines: std.ArrayList(PipelineBinding), /// Create a new shader using the default pub fn initDefault(cfg: graphics.ShaderConfig) !Shader { - return try initFromBuiltin(cfg, shader_default); + return initFromBuiltin(cfg, shader_default); } /// Creates a shader from a shader built in as a zig file pub fn initFromBuiltin(cfg: graphics.ShaderConfig, comptime builtin: anytype) !Shader { const shader_desc_fn = getBuiltinSokolCreateFunction(builtin); - if (shader_desc_fn == null) + if (shader_desc_fn == null) { + debug.log("Shader builtin not found!", .{}); return ShaderInitError.ShaderNotFound; + } - return try initSokolShader(cfg, shader_desc_fn.?(sg.queryBackend())); + return initSokolShader(cfg, shader_desc_fn.?(sg.queryBackend())); } fn terminateString(allocator: std.mem.Allocator, in_string: []const u8) ![:0]const u8 { @@ -462,28 +464,26 @@ pub const ShaderImpl = struct { var updated_cfg = cfg; updated_cfg.shader_program_def = shader_program; - return try initSokolShader(updated_cfg, desc); + return initSokolShader(updated_cfg, desc); } - pub fn makeNewInstance(cfg: graphics.ShaderConfig, shader: ?Shader) !Shader { - if (shader) |*s| { - const pipeline_list = graphics.allocator.create(std.ArrayList(PipelineBinding)) catch { - debug.err("Could not create new shader instance!", .{}); - return s.*; - }; - pipeline_list.* = std.ArrayList(PipelineBinding).init(graphics.allocator); + pub fn makeNewInstance(cfg: graphics.ShaderConfig, shader: Shader) !Shader { + const impl = try graphics.allocator.create(ShaderImpl); - // Return a copy of our shader, but mark that this is a clone so that we don't free the sokol shader twice - var newShader = s.*; - newShader.impl.sokol_pipelines = pipeline_list; - newShader.impl.cfg = cfg; - newShader.impl.is_instance = true; - return newShader; - } + // Make a new implementation that uses our existing loaded shader, but a fresh pipeline list + // Mark it as being an instance, so we don't clean up our parent shader on destroy + impl.* = .{ + .sokol_pipelines = std.ArrayList(PipelineBinding).init(graphics.allocator), + .sokol_shader = shader.impl.sokol_shader, + .sokol_shader_desc = shader.impl.sokol_shader_desc, + .cfg = cfg, + .is_instance = true, + }; - // fallback shader! - // TODO: Should we return null instead? - return try initDefault(cfg); + // Copy the shader, but switch out its guts with our new one + var newShader = shader; + newShader.impl = impl; + return newShader; } /// Find the shader function in the builtin that can actually make the ShaderDesc @@ -561,17 +561,18 @@ pub const ShaderImpl = struct { } } - const pipeline_list = try graphics.allocator.create(std.ArrayList(PipelineBinding)); - pipeline_list.* = std.ArrayList(PipelineBinding).init(graphics.allocator); + const impl = try graphics.allocator.create(ShaderImpl); + errdefer graphics.allocator.destroy(impl); + impl.* = .{ + .sokol_pipelines = std.ArrayList(PipelineBinding).init(graphics.allocator), + .sokol_shader = shader, + .sokol_shader_desc = shader_desc, + .cfg = cfg, + }; defer graphics.next_shader_handle += 1; var built_shader = Shader{ - .impl = .{ - .sokol_pipelines = pipeline_list, - .sokol_shader = shader, - .sokol_shader_desc = shader_desc, - .cfg = cfg, - }, + .impl = impl, .handle = graphics.next_shader_handle, .cfg = cfg, .fs_texture_slots = num_fs_images, @@ -579,11 +580,6 @@ pub const ShaderImpl = struct { .shader_program_def = cfg.shader_program_def, }; - // Cache some common pipelines - for (common_vertex_layouts) |l| { - _ = built_shader.impl.makePipeline(l); - } - // Set the uniformblocks to use for this shader for (cfg.vs_uniformblocks, 0..) |block, i| { built_shader.vs_uniformblocks[i] = block; @@ -595,6 +591,13 @@ pub const ShaderImpl = struct { return built_shader; } + /// Cache our common pipelines, as an optimization step + pub fn makeCommonPipelines(self: *Shader) void { + for (graphics.getCommonVertexLayouts()) |l| { + _ = self.impl.makePipeline(l); + } + } + pub fn apply(self: *Shader, layout: graphics.VertexLayout) bool { // Find the pipeline that matches our vertex layout var pipeline: ?sg.Pipeline = null; @@ -639,12 +642,14 @@ pub const ShaderImpl = struct { for (self.impl.sokol_pipelines.items) |p| { sg.destroyPipeline(p.sokol_pipeline); } + self.impl.sokol_pipelines.deinit(); - graphics.allocator.destroy(self.impl.sokol_pipelines); if (!self.impl.is_instance) { sg.destroyShader(self.impl.sokol_shader); } + + graphics.allocator.destroy(self.impl); } }; diff --git a/src/framework/platform/graphics.zig b/src/framework/platform/graphics.zig index 468008fb..3658809a 100644 --- a/src/framework/platform/graphics.zig +++ b/src/framework/platform/graphics.zig @@ -363,28 +363,32 @@ pub const Shader = struct { shader_program_def: ?shaders.ShaderProgram = null, - impl: ShaderImpl, + impl: *ShaderImpl, /// Create a new shader using the default pub fn initDefault(cfg: ShaderConfig) !Shader { - return try ShaderImpl.initDefault(cfg); + return ShaderImpl.initDefault(cfg); } /// Creates a shader from a shader built in as a zig file pub fn initFromBuiltin(cfg: ShaderConfig, comptime builtin: anytype) !Shader { - return try ShaderImpl.initFromBuiltin(cfg, builtin); + var shader = try ShaderImpl.initFromBuiltin(cfg, builtin); + shader.makeCommonPipelines(); + return shader; } pub fn initFromShaderInfo(cfg: ShaderConfig, shader_info: shaders.ShaderInfo) !Shader { - return try ShaderImpl.initFromShaderInfo(cfg, shader_info); + var shader = try ShaderImpl.initFromShaderInfo(cfg, shader_info); + shader.makeCommonPipelines(); + return shader; } /// Returns a new instance of this shader pub fn makeNewInstance(cfg: ShaderConfig, shader: ?Shader) !Shader { if (shader != null) { - return try ShaderImpl.makeNewInstance(cfg, shader.?); + return ShaderImpl.makeNewInstance(cfg, shader.?); } - return try initDefault(cfg); + return initDefault(cfg); } /// Updates the graphics state to draw using this shader @@ -422,6 +426,10 @@ pub const Shader = struct { } } + pub fn makeCommonPipelines(self: *Shader) void { + ShaderImpl.makeCommonPipelines(self); + } + pub fn destroy(self: *Shader) void { return ShaderImpl.destroy(self); } @@ -1058,6 +1066,8 @@ pub fn init() !void { // Use the default shader for debug drawing state.default_shader = try Shader.initDefault(.{ .cull_mode = .NONE }); + state.default_shader.makeCommonPipelines(); + state.debug_material = try Material.init(.{ .shader = state.default_shader, .texture_0 = tex_white, diff --git a/src/framework/utils/quakemdl.zig b/src/framework/utils/quakemdl.zig index 85c92237..5958278d 100644 --- a/src/framework/utils/quakemdl.zig +++ b/src/framework/utils/quakemdl.zig @@ -465,7 +465,7 @@ pub fn open(in_allocator: Allocator, path: []const u8) !MDL { // Material const default_material = try graphics.Material.init(.{ - .shader = graphics.Shader.initFromBuiltin(.{ .vertex_attributes = mesh.getShaderAttributes() }, default_mesh), + .shader = try graphics.Shader.initFromBuiltin(.{ .vertex_attributes = mesh.getShaderAttributes() }, default_mesh), .own_shader = true, .texture_0 = skins[0].single.texture, .samplers = &[_]graphics.FilterMode{.NEAREST},