From d708144dca4593395166098468bbb707d925508c Mon Sep 17 00:00:00 2001 From: sergeypdev Date: Tue, 10 Sep 2024 13:05:27 +0400 Subject: [PATCH] Add compute shader support --- assets/shaders/bloom_downsample.prog | 3 +- assets/shaders/bloom_upsample.prog | 4 +- assets/shaders/cube_shadow.prog | 3 +- assets/shaders/debug.prog | 3 +- assets/shaders/mesh.prog | 3 +- assets/shaders/post_process.prog | 3 +- assets/shaders/shadow.prog | 3 +- assets/shaders/unlit.prog | 3 +- assets/shaders/z_prepass.prog | 3 +- src/AssetManager.zig | 40 ++++++++++---- src/Render.zig | 78 ++++++++++++++++------------ src/formats.zig | 11 ++-- tools/asset_compiler.zig | 3 +- 13 files changed, 101 insertions(+), 59 deletions(-) diff --git a/assets/shaders/bloom_downsample.prog b/assets/shaders/bloom_downsample.prog index 9097bb3..1f1481a 100644 --- a/assets/shaders/bloom_downsample.prog +++ b/assets/shaders/bloom_downsample.prog @@ -2,5 +2,6 @@ { "shader": "bloom_downsample.glsl", "vertex": true, - "fragment": true + "fragment": true, + "compute": false } diff --git a/assets/shaders/bloom_upsample.prog b/assets/shaders/bloom_upsample.prog index 75fea7b..e06045e 100644 --- a/assets/shaders/bloom_upsample.prog +++ b/assets/shaders/bloom_upsample.prog @@ -1,6 +1,6 @@ - { "shader": "bloom_upsample.glsl", "vertex": true, - "fragment": true + "fragment": true, + "compute": false } diff --git a/assets/shaders/cube_shadow.prog b/assets/shaders/cube_shadow.prog index 41fe9cf..07bcd18 100644 --- a/assets/shaders/cube_shadow.prog +++ b/assets/shaders/cube_shadow.prog @@ -2,5 +2,6 @@ { "shader": "cube_shadow.glsl", "vertex": true, - "fragment": true + "fragment": true, + "compute": false } diff --git a/assets/shaders/debug.prog b/assets/shaders/debug.prog index d97dba7..b65b560 100644 --- a/assets/shaders/debug.prog +++ b/assets/shaders/debug.prog @@ -1,5 +1,6 @@ { "shader": "debug.glsl", "vertex": true, - "fragment": true + "fragment": true, + "compute": false } diff --git a/assets/shaders/mesh.prog b/assets/shaders/mesh.prog index 5c2dfa3..476cbac 100644 --- a/assets/shaders/mesh.prog +++ b/assets/shaders/mesh.prog @@ -1,5 +1,6 @@ { "shader": "mesh.glsl", "vertex": true, - "fragment": true + "fragment": true, + "compute": false } diff --git a/assets/shaders/post_process.prog b/assets/shaders/post_process.prog index 657bd32..6038a08 100644 --- a/assets/shaders/post_process.prog +++ b/assets/shaders/post_process.prog @@ -2,5 +2,6 @@ { "shader": "post_process.glsl", "vertex": true, - "fragment": true + "fragment": true, + "compute": false } diff --git a/assets/shaders/shadow.prog b/assets/shaders/shadow.prog index 86bccb1..593774e 100644 --- a/assets/shaders/shadow.prog +++ b/assets/shaders/shadow.prog @@ -2,5 +2,6 @@ { "shader": "shadow.glsl", "vertex": true, - "fragment": true + "fragment": true, + "compute": false } diff --git a/assets/shaders/unlit.prog b/assets/shaders/unlit.prog index c9b77a8..79461b1 100644 --- a/assets/shaders/unlit.prog +++ b/assets/shaders/unlit.prog @@ -1,5 +1,6 @@ { "shader": "unlit.glsl", "vertex": true, - "fragment": true + "fragment": true, + "compute": false } diff --git a/assets/shaders/z_prepass.prog b/assets/shaders/z_prepass.prog index 9f9a0ad..0f75fef 100644 --- a/assets/shaders/z_prepass.prog +++ b/assets/shaders/z_prepass.prog @@ -2,5 +2,6 @@ { "shader": "z_prepass.glsl", "vertex": true, - "fragment": true + "fragment": true, + "compute": false } diff --git a/src/AssetManager.zig b/src/AssetManager.zig index bbc3578..9cee37b 100644 --- a/src/AssetManager.zig +++ b/src/AssetManager.zig @@ -304,6 +304,7 @@ fn didUpdate(self: *AssetManager, path: []const u8, last_modified: i128) bool { pub const ShaderProgramDefinition = struct { vertex: []const u8, fragment: []const u8, + compute: []const u8, }; pub fn loadShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram, permuted_id: AssetId, defines: []const DefinePair) LoadedShaderProgram { @@ -318,8 +319,11 @@ fn loadShaderProgramErr(self: *AssetManager, id: AssetId, permuted_id: AssetId, const data = try self.loadFile(self.frame_arena, asset_manifest.getPath(id), SHADER_MAX_BYTES); const program = formats.ShaderProgram.fromBuffer(data.bytes); - if (!program.flags.vertex or !program.flags.fragment) { - std.log.err("Can't compile shader program {s} without vertex AND fragment shaders\n", .{asset_manifest.getPath(id)}); + const graphics_pipeline = program.flags.vertex and program.flags.fragment; + const compute_pipeline = program.flags.compute; + + if (!graphics_pipeline and !compute_pipeline) { + std.log.err("Can't compile shader program {s} without vertex AND fragment shaders or a compute shader\n", .{asset_manifest.getPath(id)}); return error.UnsupportedShader; } @@ -330,17 +334,27 @@ fn loadShaderProgramErr(self: *AssetManager, id: AssetId, permuted_id: AssetId, const prog = gl.createProgram(); errdefer gl.deleteProgram(prog); - const vertex_shader = try self.compileShader(shader.source, .vertex); - defer gl.deleteShader(vertex_shader); - const fragment_shader = try self.compileShader(shader.source, .fragment); - defer gl.deleteShader(fragment_shader); + if (program.flags.vertex and program.flags.fragment) { + const vertex_shader = try self.compileShader(shader.source, .vertex); + defer gl.deleteShader(vertex_shader); + const fragment_shader = try self.compileShader(shader.source, .fragment); + defer gl.deleteShader(fragment_shader); - gl.attachShader(prog, vertex_shader); - defer gl.detachShader(prog, vertex_shader); - gl.attachShader(prog, fragment_shader); - defer gl.detachShader(prog, fragment_shader); + gl.attachShader(prog, vertex_shader); + defer gl.detachShader(prog, vertex_shader); + gl.attachShader(prog, fragment_shader); + defer gl.detachShader(prog, fragment_shader); - gl.linkProgram(prog); + gl.linkProgram(prog); + } else { + const compute_shader = try self.compileShader(shader.source, .compute); + defer gl.deleteShader(compute_shader); + + gl.attachShader(prog, compute_shader); + defer gl.detachShader(prog, compute_shader); + + gl.linkProgram(prog); + } var success: c_int = 0; gl.getProgramiv(prog, gl.LINK_STATUS, &success); @@ -774,20 +788,24 @@ pub const IndexSlice = struct { pub const ShaderType = enum { vertex, fragment, + compute, pub fn goGLType(self: ShaderType) gl.GLenum { return switch (self) { .vertex => gl.VERTEX_SHADER, .fragment => gl.FRAGMENT_SHADER, + .compute => gl.COMPUTE_SHADER, }; } const VERTEX_DEFINES = "#version 460 core\n#define VERTEX_SHADER 1\n#define VERTEX_EXPORT out\n"; const FRAGMENT_DEFINES = "#version 460 core\n#define FRAGMENT_SHADER 1\n#define VERTEX_EXPORT in\n"; + const COMPUTE_DEFINES = "#version 460 core\n#define COMPUTE_SHADER 1\n"; pub fn getDefines(self: ShaderType) []const u8 { return switch (self) { .vertex => VERTEX_DEFINES, .fragment => FRAGMENT_DEFINES, + .compute => COMPUTE_DEFINES, }; } }; diff --git a/src/Render.zig b/src/Render.zig index 83af20e..3478cc5 100644 --- a/src/Render.zig +++ b/src/Render.zig @@ -294,15 +294,16 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetm gl.createFramebuffers(1, &render.shadow_framebuffer); checkGLError(); std.debug.assert(render.shadow_framebuffer != 0); - gl.namedFramebufferDrawBuffer(render.shadow_framebuffer, gl.FRONT_LEFT); - gl.namedFramebufferReadBuffer(render.shadow_framebuffer, gl.NONE); } // Verify directional shadow framebuffer setup { gl.namedFramebufferTextureLayer(render.shadow_framebuffer, gl.COLOR_ATTACHMENT0, render.shadow_texture_array, 0, 0); + checkGLError(); gl.namedFramebufferTexture(render.shadow_framebuffer, gl.DEPTH_ATTACHMENT, render.direct_shadow_depth_buffer_texture, 0); + checkGLError(); const check_fbo_status = gl.checkNamedFramebufferStatus(render.shadow_framebuffer, gl.DRAW_FRAMEBUFFER); + checkGLError(); if (check_fbo_status != gl.FRAMEBUFFER_COMPLETE) { std.log.debug("Shadow Framebuffer Incomplete: {}\n", .{check_fbo_status}); } @@ -311,14 +312,18 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetm // Verify cube shadow framebuffer setup { gl.namedFramebufferTextureLayer(render.shadow_framebuffer, gl.COLOR_ATTACHMENT0, render.cube_shadow_texture_array, 0, 0); + checkGLError(); gl.namedFramebufferTexture(render.shadow_framebuffer, gl.DEPTH_ATTACHMENT, render.direct_shadow_depth_buffer_texture, 0); + checkGLError(); const check_fbo_status = gl.checkNamedFramebufferStatus(render.shadow_framebuffer, gl.DRAW_FRAMEBUFFER); + checkGLError(); if (check_fbo_status != gl.FRAMEBUFFER_COMPLETE) { std.log.debug("Shadow Framebuffer Incomplete: {}\n", .{check_fbo_status}); } } gl.createBuffers(1, &render.shadow_matrices_buffer); + checkGLError(); gl.namedBufferStorage( render.shadow_matrices_buffer, @@ -326,6 +331,7 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetm null, gl.DYNAMIC_STORAGE_BIT, ); + checkGLError(); // SHADOW VAO var vao: gl.GLuint = 0; @@ -338,35 +344,19 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetm gl.enableVertexArrayAttrib(vao, Attrib.Position.value()); gl.vertexArrayAttribBinding(vao, Attrib.Position.value(), 0); gl.vertexArrayAttribFormat(vao, Attrib.Position.value(), 3, gl.FLOAT, gl.FALSE, 0); + checkGLError(); } // Screen HDR FBO { gl.createFramebuffers(1, &render.screen_fbo); + checkGLError(); std.debug.assert(render.screen_fbo != 0); var width: c_int = 0; var height: c_int = 0; c.SDL_GL_GetDrawableSize(globals.g_init.window, &width, &height); - var textures = [2]gl.GLuint{ 0, 0 }; - gl.createTextures(gl.TEXTURE_2D, textures.len, &textures); - render.screen_color_texture = textures[0]; - render.screen_depth_texture = textures[1]; - - std.debug.assert(render.screen_color_texture != 0); - std.debug.assert(render.screen_depth_texture != 0); - - gl.textureParameteri(render.screen_color_texture, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.textureParameteri(render.screen_color_texture, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.textureParameteri(render.screen_color_texture, gl.TEXTURE_MIN_FILTER, gl.NEAREST); - gl.textureParameteri(render.screen_color_texture, gl.TEXTURE_MAG_FILTER, gl.NEAREST); - - gl.textureParameteri(render.screen_depth_texture, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.textureParameteri(render.screen_depth_texture, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.textureParameteri(render.screen_depth_texture, gl.TEXTURE_MIN_FILTER, gl.NEAREST); - gl.textureParameteri(render.screen_depth_texture, gl.TEXTURE_MAG_FILTER, gl.NEAREST); - render.updateScreenBufferSize(width, height); } @@ -425,21 +415,44 @@ fn calculateMipCount(width: c_int, height: c_int) usize { } fn updateScreenBufferSize(self: *Render, width: c_int, height: c_int) void { - const mip_count = calculateMipCount(width, height); - - gl.bindTexture(gl.TEXTURE_2D, self.screen_color_texture); - for (0..mip_count) |mip_level| { - const size = getMipSize(width, height, mip_level); - std.log.debug("screen_color mip {} size {}x{}\n", .{ mip_level, size.x(), size.y() }); - - gl.texImage2D(gl.TEXTURE_2D, @intCast(mip_level), gl.RGB16F, size.x(), size.y(), 0, gl.RGB, gl.HALF_FLOAT, null); - checkGLError(); + if (self.screen_tex_size.eql(Vec2_i32.new(width, height)) and self.screen_color_texture != 0) { + return; } - // Depth doesn't need any mips cause it's not filterable anyway - gl.bindTexture(gl.TEXTURE_2D, self.screen_depth_texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT32F, width, height, 0, gl.DEPTH_COMPONENT, gl.FLOAT, null); + if (self.screen_color_texture != 0) { + const old_textures = [_]gl.GLuint{ self.screen_color_texture, self.screen_depth_texture }; + gl.deleteTextures(old_textures.len, &old_textures); + checkGLError(); + self.screen_color_texture = 0; + self.screen_depth_texture = 0; + } + + var textures = [2]gl.GLuint{ 0, 0 }; + gl.createTextures(gl.TEXTURE_2D, textures.len, &textures); checkGLError(); + self.screen_color_texture = textures[0]; + self.screen_depth_texture = textures[1]; + + std.debug.assert(self.screen_color_texture != 0); + std.debug.assert(self.screen_depth_texture != 0); + + const mip_count = calculateMipCount(width, height); + + std.debug.print("screen_color_tex {}, depth {}, screen size {}x{}, mip count: {}\n", .{ self.screen_color_texture, self.screen_depth_texture, width, height, mip_count }); + gl.textureStorage2D(self.screen_color_texture, @intCast(mip_count), gl.RGBA16F, width, height); + checkGLError(); + gl.textureStorage2D(self.screen_depth_texture, 1, gl.DEPTH_COMPONENT24, width, height); + checkGLError(); + + gl.textureParameteri(self.screen_color_texture, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.textureParameteri(self.screen_color_texture, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.textureParameteri(self.screen_color_texture, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.textureParameteri(self.screen_color_texture, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + + gl.textureParameteri(self.screen_depth_texture, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.textureParameteri(self.screen_depth_texture, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.textureParameteri(self.screen_depth_texture, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.textureParameteri(self.screen_depth_texture, gl.TEXTURE_MAG_FILTER, gl.NEAREST); self.screen_tex_size = Vec2_i32.new(width, height); self.screen_mip_count = mip_count; @@ -1489,6 +1502,7 @@ pub fn checkGLError() void { std.log.scoped(.OpenGL).err("OpenGL Failure: {s}\n", .{name}); } + @panic("GL Error"); } pub const DrawCommand = struct { diff --git a/src/formats.zig b/src/formats.zig index 41f6cfa..97e50e4 100644 --- a/src/formats.zig +++ b/src/formats.zig @@ -124,7 +124,8 @@ pub const ShaderProgram = extern struct { pub const Flags = packed struct { vertex: bool, fragment: bool, - _pad: u6 = 0, + compute: bool, + _pad: u5 = 0, }; comptime { if (@bitSizeOf(Flags) != 8) { @@ -142,24 +143,24 @@ pub const ShaderProgram = extern struct { test "ShaderProgram serialization" { const source = ShaderProgram{ - .flags = .{ .vertex = true, .fragment = true }, + .flags = .{ .vertex = true, .fragment = true, .compute = true }, .shader = .{ .id = 123 }, }; var buf: [@sizeOf(ShaderProgram)]u8 = undefined; var stream = std.io.fixedBufferStream(&buf); - try writeShaderProgram(stream.writer(), source.shader.id, source.flags.vertex, source.flags.fragment, native_endian); + try writeShaderProgram(stream.writer(), source.shader.id, source.flags.vertex, source.flags.fragment, source.flags.compute, native_endian); const result: *align(1) ShaderProgram = @ptrCast(&buf); try std.testing.expectEqual(source, result.*); } -pub fn writeShaderProgram(writer: anytype, shader: u64, vertex: bool, fragment: bool, endian: std.builtin.Endian) !void { +pub fn writeShaderProgram(writer: anytype, shader: u64, vertex: bool, fragment: bool, compute: bool, endian: std.builtin.Endian) !void { try writer.writeInt(u64, shader, endian); try writer.writeInt( u8, - @bitCast(ShaderProgram.Flags{ .vertex = vertex, .fragment = fragment }), + @bitCast(ShaderProgram.Flags{ .vertex = vertex, .fragment = fragment, .compute = compute }), endian, ); } diff --git a/tools/asset_compiler.zig b/tools/asset_compiler.zig index 4f31a13..1b3698a 100644 --- a/tools/asset_compiler.zig +++ b/tools/asset_compiler.zig @@ -532,6 +532,7 @@ fn processShaderProgram(allocator: std.mem.Allocator, input: []const u8, output_ shader: []const u8, vertex: bool, fragment: bool, + compute: bool, }; const program = try std.json.parseFromSlice(ShaderProgram, allocator, file_contents, .{}); defer program.deinit(); @@ -549,7 +550,7 @@ fn processShaderProgram(allocator: std.mem.Allocator, input: []const u8, output_ defer output.file.close(); var buf_writer = std.io.bufferedWriter(output.file.writer()); - try formats.writeShaderProgram(buf_writer.writer(), shader_asset_id, program.value.vertex, program.value.fragment, formats.native_endian); + try formats.writeShaderProgram(buf_writer.writer(), shader_asset_id, program.value.vertex, program.value.fragment, program.value.compute, formats.native_endian); try buf_writer.flush(); } const MipLevel = struct {