Add compute shader support
This commit is contained in:
parent
743e3293bd
commit
d708144dca
@ -2,5 +2,6 @@
|
||||
{
|
||||
"shader": "bloom_downsample.glsl",
|
||||
"vertex": true,
|
||||
"fragment": true
|
||||
"fragment": true,
|
||||
"compute": false
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
{
|
||||
"shader": "bloom_upsample.glsl",
|
||||
"vertex": true,
|
||||
"fragment": true
|
||||
"fragment": true,
|
||||
"compute": false
|
||||
}
|
||||
|
@ -2,5 +2,6 @@
|
||||
{
|
||||
"shader": "cube_shadow.glsl",
|
||||
"vertex": true,
|
||||
"fragment": true
|
||||
"fragment": true,
|
||||
"compute": false
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"shader": "debug.glsl",
|
||||
"vertex": true,
|
||||
"fragment": true
|
||||
"fragment": true,
|
||||
"compute": false
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"shader": "mesh.glsl",
|
||||
"vertex": true,
|
||||
"fragment": true
|
||||
"fragment": true,
|
||||
"compute": false
|
||||
}
|
||||
|
@ -2,5 +2,6 @@
|
||||
{
|
||||
"shader": "post_process.glsl",
|
||||
"vertex": true,
|
||||
"fragment": true
|
||||
"fragment": true,
|
||||
"compute": false
|
||||
}
|
||||
|
@ -2,5 +2,6 @@
|
||||
{
|
||||
"shader": "shadow.glsl",
|
||||
"vertex": true,
|
||||
"fragment": true
|
||||
"fragment": true,
|
||||
"compute": false
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"shader": "unlit.glsl",
|
||||
"vertex": true,
|
||||
"fragment": true
|
||||
"fragment": true,
|
||||
"compute": false
|
||||
}
|
||||
|
@ -2,5 +2,6 @@
|
||||
{
|
||||
"shader": "z_prepass.glsl",
|
||||
"vertex": true,
|
||||
"fragment": true
|
||||
"fragment": true,
|
||||
"compute": false
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user