Mesh loading WIP

This commit is contained in:
sergeypdev 2024-12-31 03:54:01 +04:00
parent afa00106e4
commit bd903da009
7 changed files with 499 additions and 555 deletions

View File

@ -4,6 +4,32 @@
#include "global.glsl" #include "global.glsl"
layout(std430, buffer_reference, buffer_reference_align = 4) readonly buffer PositionBuffer
{
vec3 positions[];
};
struct OtherData
{
vec3 normal;
vec3 tangent;
vec2 uv;
};
layout(std430, buffer_reference, buffer_reference_align = 4) readonly buffer OtherDataBuffer
{
OtherData other_data[];
};
layout(push_constant, std430) uniform constants
{
mat4 local_to_world;
PositionBuffer positions;
OtherDataBuffer other_data;
uint color_texture;
uint color_sampler;
} PushConstants;
#if VERTEX_SHADER #if VERTEX_SHADER
// QUAD // QUAD
@ -20,10 +46,12 @@ vec2 positions[6] = vec2[](
layout(location = 0) out vec2 OutUV; layout(location = 0) out vec2 OutUV;
void main() { void main() {
OutUV = positions[gl_VertexIndex] * 0.5 + 0.5; PositionBuffer positions_ptr = PushConstants.positions;
OutUV.y = 1 - OutUV.y; OtherDataBuffer other_data_ptr = PushConstants.other_data;
gl_Position = vec4(positions[gl_VertexIndex] + vec2(gl_InstanceIndex, 0), gl_InstanceIndex, 1.0) * Global.view.world_to_clip; OutUV = other_data_ptr.other_data[gl_VertexIndex].uv;
gl_Position = vec4(positions_ptr.positions[gl_VertexIndex], 1.0) * PushConstants.local_to_world * Global.view.world_to_clip;
} }
#endif #endif
@ -31,16 +59,10 @@ void main() {
#if FRAGMENT_SHADER #if FRAGMENT_SHADER
layout(location = 0) in vec2 InUV; layout(location = 0) in vec2 InUV;
layout(location = 0) out vec4 FragColor; layout(location = 0) out vec4 FragColor;
layout(push_constant, std430) uniform constants {
uint color_texture;
uint color_sampler;
} PushConstants;
void main() { void main() {
FragColor = vec4(texture(sampler2D(global_textures2d[PushConstants.color_texture], global_samplers[PushConstants.color_sampler]), InUV).rgb, 1.0); FragColor = vec4(1); // vec4(texture(sampler2D(global_textures2d[PushConstants.color_texture], global_samplers[PushConstants.color_sampler]), InUV).rgb, 1.0);
} }
#endif #endif

View File

@ -182,7 +182,7 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, gc: *G
.frame_arena = frame_arena, .frame_arena = frame_arena,
.exe_dir = exe_dir, .exe_dir = exe_dir,
.texture_heap = try BuddyAllocator.init(allocator, 64, 22), // 256MB .texture_heap = try BuddyAllocator.init(allocator, 64, 22), // 256MB
.vertex_heap = VertexBufferHeap.init(allocator) catch @panic("OOM"), .vertex_heap = VertexBufferHeap.init(allocator, gc) catch @panic("OOM"),
.gc = gc, .gc = gc,
.descriptorman = descriptorman, .descriptorman = descriptorman,
.frame_state = frame_state, .frame_state = frame_state,
@ -283,6 +283,20 @@ pub fn resolveShaderProgram(self: *AssetManager, handle: Handle.ShaderProgram) L
pub fn resolveMesh(self: *AssetManager, handle: Handle.Mesh) LoadedMesh { pub fn resolveMesh(self: *AssetManager, handle: Handle.Mesh) LoadedMesh {
if (handle.id == 0) return NullMesh; if (handle.id == 0) return NullMesh;
if (self.resolveLoadingAsset(handle.id)) |loading_mesh| {
const status = self.gc.device.getFenceStatus(loading_mesh.mesh.transfer_fence) catch @panic("getFenceStatus");
switch (status) {
.success => {
self.finishLoadingAsset(handle.id);
},
.not_ready => {
return NullMesh;
},
else => unreachable,
}
}
if (self.resolveAsset(handle.id)) |asset| { if (self.resolveAsset(handle.id)) |asset| {
switch (asset.*) { switch (asset.*) {
.mesh => |mesh| { .mesh => |mesh| {
@ -552,34 +566,10 @@ const NullShaderProgram = LoadedShaderProgram{
const NullMesh = LoadedMesh{ const NullMesh = LoadedMesh{
.aabb = .{}, .aabb = .{},
.heap_handle = .{}, .allocation = .{},
.positions = BufferSlice{ .positions = 0,
.buffer = 0, .other_data = 0,
.offset = 0, .indices_offset = 0,
.stride = 0,
},
.normals = BufferSlice{
.buffer = 0,
.offset = 0,
.stride = 0,
},
.tangents = BufferSlice{
.buffer = 0,
.offset = 0,
.stride = 0,
},
.uvs = BufferSlice{
.buffer = 0,
.offset = 0,
.stride = 0,
},
.indices = IndexSlice{
.buffer = 0,
.offset = 0,
.count = 0,
.type = gl.UNSIGNED_SHORT,
.base_vertex = 0,
},
.material = .{}, .material = .{},
}; };
@ -609,67 +599,194 @@ fn loadMeshErr(self: *AssetManager, id: AssetId) !LoadedMesh {
defer self.frame_arena.free(data.bytes); defer self.frame_arena.free(data.bytes);
const mesh = formats.Mesh.fromBuffer(data.bytes); const mesh = formats.Mesh.fromBuffer(data.bytes);
const vertices_len = mesh.vertices.len; const vertices_len = mesh.positions.len;
const allocation = try self.vertex_heap.alloc(vertices_len, mesh.indices.len); const allocation = try self.vertex_heap.alloc(vertices_len, mesh.indices.len);
const positions_size = @sizeOf(formats.Vector3) * vertices_len;
const other_data_size = @sizeOf(formats.Mesh.VertexDataForBasePass) * vertices_len;
const indices_size = @sizeOf(formats.Index) * mesh.indices.len;
const vertex_offset = allocation.vertex.offset; const data_size = positions_size + other_data_size + indices_size;
gl.namedBufferSubData(self.vertex_heap.vertices.buffer, @intCast(vertex_offset * @sizeOf(formats.Vector3)), @intCast(vertices_len * @sizeOf(formats.Vector3)), @ptrCast(mesh.vertices.ptr)); const staging_buffer = try self.gc.device.createBuffer(&.{
checkGLError(); .usage = .{ .transfer_src_bit = true },
gl.namedBufferSubData(self.vertex_heap.normals.buffer, @intCast(vertex_offset * @sizeOf(formats.Vector3)), @intCast(vertices_len * @sizeOf(formats.Vector3)), @ptrCast(mesh.normals.ptr)); .sharing_mode = .exclusive,
checkGLError(); .size = data_size,
gl.namedBufferSubData(self.vertex_heap.tangents.buffer, @intCast(vertex_offset * @sizeOf(formats.Vector3)), @intCast(vertices_len * @sizeOf(formats.Vector3)), @ptrCast(mesh.tangents.ptr)); }, null);
checkGLError(); errdefer self.gc.device.destroyBuffer(staging_buffer, null);
gl.namedBufferSubData(self.vertex_heap.uvs.buffer, @intCast(vertex_offset * @sizeOf(formats.Vector2)), @intCast(vertices_len * @sizeOf(formats.Vector2)), @ptrCast(mesh.uvs.ptr));
checkGLError();
const index_offset = allocation.index.offset; const staging_mem_reqs = self.gc.device.getBufferMemoryRequirements(staging_buffer);
gl.namedBufferSubData(self.vertex_heap.indices.buffer, @intCast(index_offset * @sizeOf(formats.Index)), @intCast(mesh.indices.len * @sizeOf(formats.Index)), @ptrCast(mesh.indices.ptr));
const loaded_mesh = LoadedMesh{ const staging_mem = try self.gc.device.allocateMemory(&.{
.memory_type_index = self.gc.memory_config.cpu.type_index,
.allocation_size = staging_mem_reqs.size,
}, null);
errdefer self.gc.device.freeMemory(staging_mem, null);
try self.gc.device.bindBufferMemory(staging_buffer, staging_mem, 0);
const staging_bytes: []u8 = @as([*]u8, @ptrCast(try self.gc.device.mapMemory(staging_mem, 0, data_size, .{})))[0..data_size];
const positions_offset = 0;
var size = positions_size;
const positions = std.mem.bytesAsSlice(formats.Vector3, staging_bytes[positions_offset .. positions_offset + size]);
const other_data_offset = positions_offset + positions_size;
size = other_data_size;
const other_data = std.mem.bytesAsSlice(formats.Mesh.VertexDataForBasePass, staging_bytes[other_data_offset .. other_data_offset + size]);
const indices_offset = other_data_offset + other_data_size;
size = indices_size;
const indices = std.mem.bytesAsSlice(formats.Index, staging_bytes[indices_offset .. indices_offset + size]);
@memcpy(positions, mesh.positions);
@memcpy(other_data, mesh.other_data);
@memcpy(indices, mesh.indices);
self.gc.device.unmapMemory(staging_mem);
const cmds = try self.command_pool.allocateCommandBuffer();
try cmds.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
{
cmds.pipelineBarrier2(&.{
.memory_barrier_count = 0,
.buffer_memory_barrier_count = 4,
.p_buffer_memory_barriers = &.{
vk.BufferMemoryBarrier2{
.buffer = staging_buffer,
.src_stage_mask = .{ .host_bit = true },
.src_access_mask = .{ .host_write_bit = true },
.dst_stage_mask = .{ .copy_bit = true },
.dst_access_mask = .{ .transfer_read_bit = true },
.offset = 0,
.size = data_size,
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
},
vk.BufferMemoryBarrier2{
.buffer = self.vertex_heap.heap_buffer,
.src_stage_mask = .{},
.src_access_mask = .{},
.dst_stage_mask = .{ .copy_bit = true },
.dst_access_mask = .{ .transfer_write_bit = true },
.offset = allocation.positions.offset,
.size = positions_size,
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
},
vk.BufferMemoryBarrier2{
.buffer = self.vertex_heap.heap_buffer,
.src_stage_mask = .{},
.src_access_mask = .{},
.dst_stage_mask = .{ .copy_bit = true },
.dst_access_mask = .{ .transfer_write_bit = true },
.offset = allocation.other_data.offset,
.size = other_data_size,
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
},
vk.BufferMemoryBarrier2{
.buffer = self.vertex_heap.heap_buffer,
.src_stage_mask = .{},
.src_access_mask = .{},
.dst_stage_mask = .{ .copy_bit = true },
.dst_access_mask = .{ .transfer_write_bit = true },
.offset = allocation.indices.offset,
.size = indices_size,
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
},
},
});
}
cmds.copyBuffer(staging_buffer, self.vertex_heap.heap_buffer, 3, &.{
vk.BufferCopy{
.src_offset = positions_offset,
.dst_offset = allocation.positions.offset,
.size = positions_size,
},
vk.BufferCopy{
.src_offset = other_data_offset,
.dst_offset = allocation.other_data.offset,
.size = other_data_size,
},
vk.BufferCopy{
.src_offset = indices_offset,
.dst_offset = allocation.indices.offset,
.size = indices_size,
},
});
cmds.pipelineBarrier2(&.{
.buffer_memory_barrier_count = 3,
.p_buffer_memory_barriers = &.{
vk.BufferMemoryBarrier2{
.buffer = self.vertex_heap.heap_buffer,
.src_stage_mask = .{ .copy_bit = true },
.src_access_mask = .{ .transfer_write_bit = true },
.dst_stage_mask = .{ .vertex_shader_bit = true, .fragment_shader_bit = true, .compute_shader_bit = true },
.dst_access_mask = .{ .shader_storage_read_bit = true },
.src_queue_family_index = self.queue.family,
.dst_queue_family_index = self.gc.queues.graphics.family,
.offset = allocation.positions.offset,
.size = positions_size,
},
vk.BufferMemoryBarrier2{
.buffer = self.vertex_heap.heap_buffer,
.src_stage_mask = .{ .copy_bit = true },
.src_access_mask = .{ .transfer_write_bit = true },
.dst_stage_mask = .{ .vertex_shader_bit = true, .fragment_shader_bit = true, .compute_shader_bit = true },
.dst_access_mask = .{ .shader_storage_read_bit = true },
.src_queue_family_index = self.queue.family,
.dst_queue_family_index = self.gc.queues.graphics.family,
.offset = allocation.other_data.offset,
.size = other_data_size,
},
vk.BufferMemoryBarrier2{
.buffer = self.vertex_heap.heap_buffer,
.src_stage_mask = .{ .copy_bit = true },
.src_access_mask = .{ .transfer_write_bit = true },
.dst_stage_mask = .{ .vertex_shader_bit = true, .fragment_shader_bit = true, .compute_shader_bit = true, .index_input_bit = true },
.dst_access_mask = .{ .shader_storage_read_bit = true, .index_read_bit = true },
.src_queue_family_index = self.queue.family,
.dst_queue_family_index = self.gc.queues.graphics.family,
.offset = allocation.indices.offset,
.size = indices_size,
},
},
});
try cmds.endCommandBuffer();
const copy_fence = try self.gc.device.createFence(&.{}, null);
try self.queue.submit(&.{ .command_buffers = &.{cmds.handle} }, copy_fence);
const loading_mesh = LoadingMesh{
.aabb = .{ .aabb = .{
.min = Vec3.new(mesh.aabb.min.x, mesh.aabb.min.y, mesh.aabb.min.z), .min = Vec3.new(mesh.aabb.min.x, mesh.aabb.min.y, mesh.aabb.min.z),
.max = Vec3.new(mesh.aabb.max.x, mesh.aabb.max.y, mesh.aabb.max.z), .max = Vec3.new(mesh.aabb.max.x, mesh.aabb.max.y, mesh.aabb.max.z),
}, },
.heap_handle = allocation,
.material = mesh.material, .material = mesh.material,
.positions = .{ .vertex_count = @intCast(vertices_len),
.buffer = self.vertex_heap.vertices.buffer, .index_count = @intCast(mesh.indices.len),
.offset = @intCast(vertex_offset * @sizeOf(formats.Vector3)), .allocation = allocation,
.stride = @sizeOf(formats.Vector3), .transfer_fence = copy_fence,
}, .staging_buf = staging_buffer,
.normals = .{ .staging_memory = staging_mem,
.buffer = self.vertex_heap.normals.buffer, .command_buffer = cmds.handle,
.offset = @intCast(vertex_offset * @sizeOf(formats.Vector3)),
.stride = @sizeOf(formats.Vector3),
},
.tangents = .{
.buffer = self.vertex_heap.tangents.buffer,
.offset = @intCast(vertex_offset * @sizeOf(formats.Vector3)),
.stride = @sizeOf(formats.Vector3),
},
.uvs = .{
.buffer = self.vertex_heap.uvs.buffer,
.offset = @intCast(vertex_offset * @sizeOf(formats.Vector2)),
.stride = @sizeOf(formats.Vector2),
},
.indices = .{
.buffer = self.vertex_heap.indices.buffer,
.offset = @intCast(index_offset * @sizeOf(formats.Index)),
.count = @intCast(mesh.indices.len),
.type = gl.UNSIGNED_INT,
.base_vertex = @intCast(vertex_offset),
},
}; };
{ {
self.rw_lock.lock(); self.rw_lock.lock();
defer self.rw_lock.unlock(); defer self.rw_lock.unlock();
try self.loaded_assets.put(self.allocator, id, .{ .mesh = loaded_mesh }); try self.loading_assets.put(self.allocator, id, .{ .mesh = loading_mesh });
try self.modified_times.put(self.allocator, id, data.modified); try self.modified_times.put(self.allocator, id, data.modified);
} }
return loaded_mesh;
return NullMesh;
} }
fn loadTexture(self: *AssetManager, id: AssetId) LoadedTexture { fn loadTexture(self: *AssetManager, id: AssetId) LoadedTexture {
@ -770,6 +887,8 @@ fn loadTextureErr(self: *AssetManager, id: AssetId) !LoadedTexture {
offset += texture.data[mip].len; offset += texture.data[mip].len;
} }
self.gc.device.unmapMemory(staging_mem);
const cmds = try self.command_pool.allocateCommandBuffer(); const cmds = try self.command_pool.allocateCommandBuffer();
try cmds.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } }); try cmds.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
@ -881,56 +1000,6 @@ fn loadTextureErr(self: *AssetManager, id: AssetId) !LoadedTexture {
} }
return NullTexture; return NullTexture;
// return loaded_texture;
// var name: gl.GLuint = 0;
// gl.createTextures(gl.TEXTURE_2D, 1, &name);
// if (name == 0) {
// return error.GLCreateTexture;
// }
// errdefer gl.deleteTextures(1, &name);
// const gl_format: gl.GLenum = switch (texture.header.format) {
// .bc7 => gl.COMPRESSED_RGBA_BPTC_UNORM,
// .bc5 => gl.COMPRESSED_RG_RGTC2,
// .bc6 => gl.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT,
// };
// gl.textureStorage2D(
// name,
// @intCast(texture.mipLevels()),
// gl_format,
// @intCast(texture.header.padded_width),
// @intCast(texture.header.padded_height),
// );
// checkGLError();
// for (0..texture.mipLevels()) |mip_level| {
// const desc = texture.getMipDesc(mip_level);
// gl.compressedTextureSubImage2D(
// name,
// @intCast(mip_level),
// 0,
// 0,
// @intCast(desc.width),
// @intCast(desc.height),
// gl_format,
// @intCast(texture.data[mip_level].len),
// @ptrCast(texture.data[mip_level].ptr),
// );
// checkGLError();
// }
// const uv_scale = Vec2.new(
// @as(f32, @floatFromInt(texture.header.width)) / @as(f32, @floatFromInt(texture.header.padded_width)),
// @as(f32, @floatFromInt(texture.header.height)) / @as(f32, @floatFromInt(texture.header.padded_height)),
// );
// const handle = gl.GL_ARB_bindless_texture.getTextureHandleARB(name);
// gl.GL_ARB_bindless_texture.makeTextureHandleResidentARB(handle);
// errdefer gl.GL_ARB_bindless_texture.makeTextureHandleNonResidentARB(handle);
} }
fn loadScene(self: *AssetManager, id: AssetId) LoadedScene { fn loadScene(self: *AssetManager, id: AssetId) LoadedScene {
@ -1002,6 +1071,32 @@ fn loadMaterialErr(self: *AssetManager, id: AssetId) !formats.Material {
fn finishLoadingAsset(self: *AssetManager, id: AssetId) void { fn finishLoadingAsset(self: *AssetManager, id: AssetId) void {
if (self.resolveLoadingAsset(id)) |loading_asset| { if (self.resolveLoadingAsset(id)) |loading_asset| {
switch (loading_asset.*) { switch (loading_asset.*) {
.mesh => |loading_mesh| {
{
self.rw_lock.lock();
defer self.rw_lock.unlock();
_ = self.loading_assets.remove(id);
self.loaded_assets.put(self.allocator, id, LoadedAsset{
.mesh = LoadedMesh{
.aabb = loading_mesh.aabb,
.material = loading_mesh.material,
.vertex_count = loading_mesh.vertex_count,
.index_count = loading_mesh.index_count,
.allocation = loading_mesh.allocation,
.positions = self.vertex_heap.heap_address + loading_mesh.allocation.positions.offset,
.other_data = self.vertex_heap.heap_address + loading_mesh.allocation.other_data.offset,
.indices_offset = loading_mesh.allocation.indices.offset,
},
}) catch @panic("OOM");
}
self.gc.device.freeCommandBuffers(self.command_pool.handle, 1, &.{loading_mesh.command_buffer});
self.gc.device.destroyBuffer(loading_mesh.staging_buf, null);
self.gc.device.freeMemory(loading_mesh.staging_memory, null);
self.gc.device.destroyFence(loading_mesh.transfer_fence, null);
},
.texture => |loading_texture| { .texture => |loading_texture| {
{ {
self.rw_lock.lock(); self.rw_lock.lock();
@ -1037,7 +1132,7 @@ const AssetType = enum {
const LoadingAsset = union(AssetType) { const LoadingAsset = union(AssetType) {
shader: void, shader: void,
shaderProgram: void, shaderProgram: void,
mesh: void, mesh: LoadingMesh,
texture: LoadingTexture, texture: LoadingTexture,
scene: void, scene: void,
material: void, material: void,
@ -1061,15 +1156,32 @@ const LoadedShaderProgram = struct {
pipeline: vk.Pipeline, pipeline: vk.Pipeline,
}; };
pub const LoadingMesh = struct {
aabb: AABB,
material: formats.Material,
vertex_count: u32 = 0,
index_count: u32 = 0,
allocation: VertexBufferHeap.Alloc,
transfer_fence: vk.Fence,
staging_buf: vk.Buffer,
staging_memory: vk.DeviceMemory,
command_buffer: vk.CommandBuffer,
};
pub const LoadedMesh = struct { pub const LoadedMesh = struct {
aabb: AABB, aabb: AABB,
heap_handle: VertexBufferHeap.Alloc, allocation: VertexBufferHeap.Alloc,
positions: BufferSlice,
normals: BufferSlice,
tangents: BufferSlice,
uvs: BufferSlice,
indices: IndexSlice,
material: formats.Material, material: formats.Material,
vertex_count: u32 = 0,
index_count: u32 = 0,
positions: u64, // Buffer Device Address
other_data: u64,
// Offset from start of buffer
indices_offset: u32,
}; };
const LoadingTexture = struct { const LoadingTexture = struct {
@ -1129,53 +1241,6 @@ pub const AABB = struct {
} }
}; };
pub const BufferSlice = struct {
buffer: gl.GLuint,
offset: gl.GLintptr,
stride: gl.GLsizei,
pub fn bind(self: *const BufferSlice, index: gl.GLuint) void {
gl.bindVertexBuffer(index, self.buffer, 0, self.stride);
}
};
pub const IndexSlice = struct {
buffer: gl.GLuint,
offset: gl.GLuint,
count: gl.GLuint,
type: gl.GLenum,
base_vertex: gl.GLint,
pub fn bind(self: *const IndexSlice) void {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.buffer);
}
};
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,
};
}
};
const AssetData = struct { const AssetData = struct {
bytes: []u8, bytes: []u8,
modified: i128, modified: i128,
@ -1190,13 +1255,6 @@ fn loadFile(self: *AssetManager, allocator: std.mem.Allocator, path: []const u8,
return .{ .bytes = bytes, .modified = meta.modified() }; return .{ .bytes = bytes, .modified = meta.modified() };
} }
fn loadShader(self: *AssetManager, id: AssetId, permuted_id: AssetId, defines: []const DefinePair) LoadedShader {
return self.loadShaderErr(id, permuted_id, defines) catch |err| {
std.log.err("Error: {} when loading shader id {} {s}", .{ err, id, asset_manifest.getPath(id) });
return NullShader;
};
}
const ShaderTokenizer = struct { const ShaderTokenizer = struct {
const Self = @This(); const Self = @This();
@ -1732,124 +1790,6 @@ test "ShaderTokenizer" {
} }
} }
fn loadShaderErr(self: *AssetManager, id: AssetId, permuted_id: AssetId, defines: []const DefinePair) !LoadedShader {
const path = asset_manifest.getPath(id);
const dir = std.fs.path.dirname(path) orelse @panic("No dir");
const data = try self.loadFile(self.frame_arena, path, SHADER_MAX_BYTES);
var included_asset_ids = std.ArrayList(AssetId).init(self.frame_arena);
var preprocessed_segments = std.SegmentedList([]const u8, 64){};
var final_len: usize = 0;
// Just append defines here, no need to manually preprocess
for (defines) |define| {
const define_str = try std.fmt.allocPrint(self.frame_arena, "#define {s} {s}\n", .{ define.key, define.value });
try preprocessed_segments.append(self.frame_arena, define_str);
final_len += define_str.len;
}
// Preprocess
{
var tokenizer = ShaderTokenizer.init(data.bytes);
var last_offset: usize = 0;
var token = tokenizer.next();
while (token.type != .End) : (token = tokenizer.next()) {
switch (token.type) {
.Directive => {
if (std.mem.eql(u8, token.text, "include")) {
// Append section of text up to this directive
try preprocessed_segments.append(self.frame_arena, data.bytes[last_offset..token.start]);
final_len += token.start - last_offset;
const include_path = tokenizer.next();
// Next section will start after this directive, this allows replacing #include with its content
last_offset = include_path.end;
if (include_path.type != .String) {
return error.InvalidInclude;
}
const included_file_path = try std.fs.path.resolve(self.frame_arena, &.{ dir, include_path.text });
const included_asset_id = assets.AssetPath.fromString(included_file_path).hash();
if (included_asset_id != 0) {
const included_shader = self.resolveShaderWithDefines(.{ .id = included_asset_id }, defines);
try included_asset_ids.append(included_shader.permuted_id);
try preprocessed_segments.append(self.frame_arena, included_shader.source);
final_len += included_shader.source.len;
}
}
},
else => {},
}
}
{
const remaining = data.bytes.len - last_offset;
if (remaining > 0) {
try preprocessed_segments.append(self.frame_arena, data.bytes[last_offset..data.bytes.len]);
final_len += remaining;
}
}
}
var result_source = try self.allocator.alloc(u8, final_len);
// Join source sections
{
var cursor: usize = 0;
var iter = preprocessed_segments.constIterator(0);
while (iter.next()) |slice| {
@memcpy(result_source[cursor .. cursor + slice.len], slice.*);
cursor += slice.len;
}
}
const loaded_shader = LoadedShader{ .source = result_source, .permuted_id = permuted_id };
{
self.rw_lock.lock();
defer self.rw_lock.unlock();
try self.loaded_assets.put(self.allocator, permuted_id, .{ .shader = loaded_shader });
try self.modified_times.put(self.allocator, id, data.modified);
try self.addDependencies(permuted_id, &.{id});
try self.addDependencies(permuted_id, included_asset_ids.items);
}
return loaded_shader;
}
fn compileShader(self: *AssetManager, source: []const u8, shader_type: ShaderType) !gl.GLuint {
const shader = gl.createShader(shader_type.goGLType());
errdefer gl.deleteShader(shader);
std.debug.assert(shader != 0); // should only happen if incorect shader type is passed
const defines = shader_type.getDefines();
// Spirv
gl.shaderBinary(1, &shader, 0x9551, source.ptr, @intCast(source.len));
gl.compileShader(shader);
var success: c_int = 0;
gl.getShaderiv(shader, gl.COMPILE_STATUS, &success);
if (success == 0) {
var info_len: gl.GLint = 0;
gl.getShaderiv(shader, gl.INFO_LOG_LENGTH, &info_len);
if (info_len > 0) {
const info_log = try self.frame_arena.allocSentinel(u8, @intCast(info_len - 1), 0);
gl.getShaderInfoLog(shader, @intCast(info_log.len), null, info_log);
std.log.err("ERROR::SHADER::COMPILATION_FAILED\n{s}\n{s}\n", .{ defines, info_log });
} else {
std.log.err("ERROR::SHADER::COMPILIATION_FAILED\n{s}\nNo info log.\n", .{defines});
}
return error.ShaderCompilationFailed;
}
return shader;
}
fn addDependencies(self: *AssetManager, id: AssetId, dependencies: []const AssetId) !void { fn addDependencies(self: *AssetManager, id: AssetId, dependencies: []const AssetId) !void {
{ {
const gop = try self.dependencies.getOrPut(self.allocator, id); const gop = try self.dependencies.getOrPut(self.allocator, id);
@ -1882,7 +1822,7 @@ fn freeAsset(self: *AssetManager, asset: *LoadedAsset) void {
const destroy_queue = &self.frame_state.frame_data[self.frame_state.frame].destroy_queue; const destroy_queue = &self.frame_state.frame_data[self.frame_state.frame].destroy_queue;
switch (asset.*) { switch (asset.*) {
.mesh => |*mesh| { .mesh => |*mesh| {
self.vertex_heap.free(mesh.heap_handle); self.vertex_heap.free(mesh.allocation);
}, },
.shader => |*shader| { .shader => |*shader| {
self.allocator.free(shader.source); self.allocator.free(shader.source);
@ -1922,122 +1862,84 @@ const VertexBufferHeap = struct {
const Self = @This(); const Self = @This();
pub const Alloc = struct { pub const Alloc = struct {
vertex: BuddyAllocator.Alloc = .{}, positions: BuddyAllocator.Alloc = .{},
index: BuddyAllocator.Alloc = .{}, other_data: BuddyAllocator.Alloc = .{},
indices: BuddyAllocator.Alloc = .{},
}; };
pub const Buffer = struct { gc: *GraphicsContext,
buffer: gl.GLuint, buddy: BuddyAllocator,
stride: gl.GLsizei, // Heap contains both vertex and index data, split manually in two
heap_memory: vk.DeviceMemory,
heap_buffer: vk.Buffer,
heap_address: vk.DeviceAddress,
pub fn init(name: gl.GLuint, stride: usize) Buffer { pub fn init(allocator: std.mem.Allocator, gc: *GraphicsContext) !Self {
return .{ const device = gc.device;
.buffer = name,
.stride = @intCast(stride), var buddy = try BuddyAllocator.init(allocator, 64, 23); // 512MB
}; errdefer buddy.deinit();
const heap_buffer = try device.createBuffer(&vk.BufferCreateInfo{
.size = buddy.getSize(),
.sharing_mode = .exclusive,
.usage = .{ .shader_device_address_bit = true, .storage_buffer_bit = true, .transfer_dst_bit = true, .index_buffer_bit = true },
}, null);
errdefer device.destroyBuffer(heap_buffer, null);
const mem_reqs = device.getBufferMemoryRequirements(heap_buffer);
const heap_memory = try device.allocateMemory(&vk.MemoryAllocateInfo{
.p_next = &vk.MemoryAllocateFlagsInfo{
.flags = .{ .device_address_bit = true },
.device_mask = 0,
},
.allocation_size = mem_reqs.size,
.memory_type_index = gc.memory_config.gpu.type_index,
}, null);
errdefer device.freeMemory(heap_memory, null);
try device.bindBufferMemory(heap_buffer, heap_memory, 0);
const heap_address = device.getBufferDeviceAddress(&vk.BufferDeviceAddressInfo{
.buffer = heap_buffer,
});
if (heap_address == 0) {
return error.InvalidBDA;
} }
pub fn bind(self: *const Buffer, index: gl.GLuint) void {
gl.bindVertexBuffer(index, self.buffer, 0, self.stride);
}
};
vertex_buddy: BuddyAllocator,
index_buddy: BuddyAllocator,
vertices: Buffer,
normals: Buffer,
tangents: Buffer,
uvs: Buffer,
indices: Buffer,
pub fn init(allocator: std.mem.Allocator) !Self {
var vertex_buddy = try BuddyAllocator.init(allocator, 4096, 13);
errdefer vertex_buddy.deinit();
var index_buddy = try BuddyAllocator.init(allocator, 4096, 13);
errdefer index_buddy.deinit();
// const vertex_buf_size = vertex_buddy.getSize();
// const index_buf_size = index_buddy.getSize();
const bufs = [_]gl.GLuint{ 0, 0, 0, 0, 0 };
// gl.createBuffers(bufs.len, &bufs);
// errdefer gl.deleteBuffers(bufs.len, &bufs);
// for (bufs) |buf| {
// if (buf == 0) {
// return error.BufferAllocationFailed;
// }
// }
const vertices = Buffer.init(bufs[0], @sizeOf(formats.Vector3));
const normals = Buffer.init(bufs[1], @sizeOf(formats.Vector3));
const tangents = Buffer.init(bufs[2], @sizeOf(formats.Vector3));
const uvs = Buffer.init(bufs[3], @sizeOf(formats.Vector2));
const indices = Buffer.init(bufs[4], @sizeOf(formats.Index));
// gl.namedBufferStorage(
// vertices.buffer,
// @intCast(vertex_buf_size * @sizeOf(formats.Vector3)),
// null,
// gl.DYNAMIC_STORAGE_BIT,
// );
// gl.namedBufferStorage(
// normals.buffer,
// @intCast(vertex_buf_size * @sizeOf(formats.Vector3)),
// null,
// gl.DYNAMIC_STORAGE_BIT,
// );
// gl.namedBufferStorage(
// tangents.buffer,
// @intCast(vertex_buf_size * @sizeOf(formats.Vector3)),
// null,
// gl.DYNAMIC_STORAGE_BIT,
// );
// gl.namedBufferStorage(
// uvs.buffer,
// @intCast(vertex_buf_size * @sizeOf(formats.Vector2)),
// null,
// gl.DYNAMIC_STORAGE_BIT,
// );
// gl.namedBufferStorage(
// indices.buffer,
// @intCast(index_buf_size * @sizeOf(formats.Index)),
// null,
// gl.DYNAMIC_STORAGE_BIT,
// );
return .{ return .{
.vertex_buddy = vertex_buddy, .gc = gc,
.index_buddy = index_buddy, .buddy = buddy,
.vertices = vertices, .heap_memory = heap_memory,
.normals = normals, .heap_buffer = heap_buffer,
.tangents = tangents, .heap_address = heap_address,
.uvs = uvs,
.indices = indices,
}; };
} }
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
self.index_buddy.deinit(); self.buddy.deinit();
self.vertex_buddy.deinit();
const bufs = [_]gl.GLuint{ self.vertices, self.normals, self.tangents, self.uvs, self.indices };
gl.deleteBuffers(bufs.len, &bufs);
} }
pub fn alloc(self: *Self, vertex_len: usize, index_len: usize) !Alloc { pub fn alloc(self: *Self, vertex_len: usize, index_len: usize) !Alloc {
const vertex_alloc = try self.vertex_buddy.alloc(vertex_len); const positions_alloc = try self.buddy.alloc(@sizeOf(formats.Vector3) * vertex_len);
errdefer self.vertex_buddy.free(vertex_alloc); errdefer self.buddy.free(positions_alloc);
const index_alloc = try self.index_buddy.alloc(index_len); const other_data_alloc = try self.buddy.alloc(@sizeOf(formats.Mesh.VertexDataForBasePass) * vertex_len);
errdefer self.index_buddy.free(index_alloc); errdefer self.buddy.free(other_data_alloc);
return Alloc{ .vertex = vertex_alloc, .index = index_alloc }; const index_alloc = try self.buddy.alloc(index_len);
errdefer self.buddy.free(index_alloc);
return Alloc{
.positions = positions_alloc,
.other_data = other_data_alloc,
.indices = index_alloc,
};
} }
pub fn free(self: *Self, allocation: Alloc) void { pub fn free(self: *Self, allocation: Alloc) void {
self.vertex_buddy.free(allocation.vertex); self.buddy.free(allocation.positions);
self.index_buddy.free(allocation.index); self.buddy.free(allocation.other_data);
self.buddy.free(allocation.indices);
} }
}; };

View File

@ -1177,7 +1177,7 @@ pub fn finish(self: *Render) void {
gl.bindVertexArray(self.z_prepass_vao); gl.bindVertexArray(self.z_prepass_vao);
gl.depthFunc(gl.LESS); gl.depthFunc(gl.LESS);
self.assetman.vertex_heap.vertices.bind(Render.Attrib.Position.value()); self.assetman.vertex_heap.vertex_data.bind(Render.Attrib.Position.value());
checkGLError(); checkGLError();
self.assetman.vertex_heap.uvs.bind(Render.Attrib.UV.value()); self.assetman.vertex_heap.uvs.bind(Render.Attrib.UV.value());
checkGLError(); checkGLError();
@ -1210,7 +1210,7 @@ pub fn finish(self: *Render) void {
checkGLError(); checkGLError();
self.assetman.vertex_heap.uvs.bind(Render.Attrib.UV.value()); self.assetman.vertex_heap.uvs.bind(Render.Attrib.UV.value());
checkGLError(); checkGLError();
self.assetman.vertex_heap.vertices.bind(Render.Attrib.Position.value()); self.assetman.vertex_heap.vertex_data.bind(Render.Attrib.Position.value());
checkGLError(); checkGLError();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.assetman.vertex_heap.indices.buffer); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.assetman.vertex_heap.indices.buffer);
checkGLError(); checkGLError();
@ -1439,7 +1439,7 @@ fn renderShadow(self: *Render, frustum: *const math.Frustum) void {
_ = frustum; // autofix _ = frustum; // autofix
const zone = tracy.initZone(@src(), .{ .name = "Render.renderShadow" }); const zone = tracy.initZone(@src(), .{ .name = "Render.renderShadow" });
defer zone.deinit(); defer zone.deinit();
self.assetman.vertex_heap.vertices.bind(Render.Attrib.Position.value()); self.assetman.vertex_heap.vertex_data.bind(Render.Attrib.Position.value());
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.assetman.vertex_heap.indices.buffer); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.assetman.vertex_heap.indices.buffer);
// TODO: this wastes space in temp allocator // TODO: this wastes space in temp allocator

View File

@ -8,6 +8,7 @@ const za = @import("zalgebra");
const Vec3 = za.Vec3; const Vec3 = za.Vec3;
const Mat4 = za.Mat4; const Mat4 = za.Mat4;
const common = @import("common.zig"); const common = @import("common.zig");
const formats = @import("formats.zig");
const render_common = @import("render_common.zig"); const render_common = @import("render_common.zig");
const MAX_FRAME_LAG = render_common.MAX_FRAME_LAG; const MAX_FRAME_LAG = render_common.MAX_FRAME_LAG;
const DeferredDestroyQueue = render_common.DeferredDestroyQueue; const DeferredDestroyQueue = render_common.DeferredDestroyQueue;
@ -44,6 +45,9 @@ command_pool: GraphicsContext.CommandPool,
vulkan_frame_arena: VulkanPerFrameArena, vulkan_frame_arena: VulkanPerFrameArena,
camera: *Camera = &default_camera, camera: *Camera = &default_camera,
draw_commands: [4096]DrawCommand = undefined,
draw_command_count: u32 = 0,
// Global sampler to use for reading screen color in post processing // Global sampler to use for reading screen color in post processing
screen_color_sampler: vk.Sampler = .null_handle, screen_color_sampler: vk.Sampler = .null_handle,
screen_color_sampler_descriptor_handle: DescriptorManager.DescriptorHandle = .{}, screen_color_sampler_descriptor_handle: DescriptorManager.DescriptorHandle = .{},
@ -424,7 +428,18 @@ fn pushConstants(self: *Render2, cmds: GraphicsContext.CommandBuffer, stage_flag
cmds.pushConstants(self.descriptorman.pipeline_layout, stage_flags, 0, @sizeOf(@TypeOf(value)), &value); cmds.pushConstants(self.descriptorman.pipeline_layout, stage_flags, 0, @sizeOf(@TypeOf(value)), &value);
} }
pub fn draw(self: *Render2) !void { pub const DrawCommand = struct {
mesh: AssetManager.Handle.Mesh,
material_override: ?formats.Material,
transform: Mat4,
};
pub fn draw(self: *Render2, cmd: DrawCommand) void {
self.draw_commands[self.draw_command_count] = cmd;
self.draw_command_count += 1;
}
pub fn finish(self: *Render2) !void {
const gc = self.gc; const gc = self.gc;
const device = gc.device; const device = gc.device;
const frame = &self.frame_state.frame_data[self.frame_state.frame]; const frame = &self.frame_state.frame_data[self.frame_state.frame];
@ -573,11 +588,6 @@ pub fn draw(self: *Render2) !void {
cmds.bindPipeline(.graphics, unlit.pipeline); cmds.bindPipeline(.graphics, unlit.pipeline);
cmds.bindDescriptorSets(.graphics, self.descriptorman.pipeline_layout, 0, 1, &.{global_descriptor_set}, 0, null); cmds.bindDescriptorSets(.graphics, self.descriptorman.pipeline_layout, 0, 1, &.{global_descriptor_set}, 0, null);
self.pushConstants(cmds, .{ .vertex_bit = true, .fragment_bit = true }, UnlitPushConstants{
.color_texture = self.assetman.resolveTexture(a.Textures.bunny_tex1).descriptor_handle.index,
.color_sampler = self.screen_color_sampler_descriptor_handle.index,
});
cmds.setViewportWithCount(1, &.{vk.Viewport{ cmds.setViewportWithCount(1, &.{vk.Viewport{
.x = 0, .x = 0,
.y = 0, .y = 0,
@ -591,7 +601,21 @@ pub fn draw(self: *Render2) !void {
.extent = gc.swapchain_extent, .extent = gc.swapchain_extent,
}}); }});
cmds.draw(6, 2, 0, 0); cmds.bindIndexBuffer(self.assetman.vertex_heap.heap_buffer, 0, vk.IndexType.uint32);
for (self.draw_commands[0..self.draw_command_count]) |cmd| {
const mesh = self.assetman.resolveMesh(cmd.mesh);
self.pushConstants(cmds, .{ .vertex_bit = true, .fragment_bit = true }, UnlitPushConstants{
.local_to_world = cmd.transform,
.positions_address = mesh.positions,
.other_data_address = mesh.other_data,
.color_texture = self.assetman.resolveTexture(a.Textures.bunny_tex1).descriptor_handle.index,
.color_sampler = self.screen_color_sampler_descriptor_handle.index,
});
cmds.drawIndexed(mesh.index_count, 1, mesh.indices_offset, 0, 0);
}
} }
// Post process and convert from f16 to rgba8_unorm // Post process and convert from f16 to rgba8_unorm
@ -668,6 +692,8 @@ pub fn draw(self: *Render2) !void {
} }
try cmds.endCommandBuffer(); try cmds.endCommandBuffer();
self.draw_command_count = 0;
try gc.queues.graphics.submit( try gc.queues.graphics.submit(
&GraphicsContext.SubmitInfo{ &GraphicsContext.SubmitInfo{
.wait_semaphores = &.{frame.acquire_swapchain_image}, .wait_semaphores = &.{frame.acquire_swapchain_image},
@ -716,6 +742,9 @@ const GlobalUniform = extern struct {
}; };
const UnlitPushConstants = extern struct { const UnlitPushConstants = extern struct {
local_to_world: Mat4,
positions_address: u64,
other_data_address: u64,
color_texture: u32, color_texture: u32,
color_sampler: u32, color_sampler: u32,
}; };

View File

@ -23,14 +23,19 @@ pub const AABB = extern struct {
}; };
pub const Index = u32; pub const Index = u32;
/// On gpu positions will go packed into one buffer normals, tangents and uvs will go to another
/// buffer. This is to allow a more efficient depth only pass that requires positions only.
pub const Mesh = struct { pub const Mesh = struct {
pub const VertexDataForBasePass = extern struct {
normal: Vector3,
tangent: Vector3,
uv: Vector2,
};
aabb: AABB, aabb: AABB,
material: Material, material: Material,
positions: []align(1) Vector3,
vertices: []align(1) Vector3, other_data: []align(1) VertexDataForBasePass,
normals: []align(1) Vector3,
tangents: []align(1) Vector3,
uvs: []align(1) Vector2,
indices: []align(1) Index, indices: []align(1) Index,
// This will panic if data is incorrect // This will panic if data is incorrect
@ -61,15 +66,11 @@ pub const Mesh = struct {
offset += @sizeOf(usize); offset += @sizeOf(usize);
size = vert_len * @sizeOf(Vector3); size = vert_len * @sizeOf(Vector3);
const vertices = std.mem.bytesAsSlice(Vector3, buffer[offset .. offset + size]); const positions = std.mem.bytesAsSlice(Vector3, buffer[offset .. offset + size]);
offset += size;
const normals = std.mem.bytesAsSlice(Vector3, buffer[offset .. offset + size]);
offset += size;
const tangents = std.mem.bytesAsSlice(Vector3, buffer[offset .. offset + size]);
offset += size; offset += size;
size = vert_len * @sizeOf(Vector2); size = vert_len * @sizeOf(VertexDataForBasePass);
const uvs = std.mem.bytesAsSlice(Vector2, buffer[offset .. offset + size]); const other_data = std.mem.bytesAsSlice(VertexDataForBasePass, buffer[offset .. offset + size]);
offset += size; offset += size;
size = ind_len * @sizeOf(Index); size = ind_len * @sizeOf(Index);
@ -79,17 +80,15 @@ pub const Mesh = struct {
return .{ return .{
.aabb = aabb, .aabb = aabb,
.material = material.*, .material = material.*,
.vertices = vertices, .positions = positions,
.normals = normals, .other_data = other_data,
.tangents = tangents,
.uvs = uvs,
.indices = indices, .indices = indices,
}; };
} }
}; };
pub fn writeMesh(writer: anytype, value: Mesh, endian: std.builtin.Endian) !void { pub fn writeMesh(writer: anytype, value: Mesh, endian: std.builtin.Endian) !void {
std.debug.assert(value.vertices.len == value.normals.len); std.debug.assert(value.positions.len == value.other_data.len);
// AABB // AABB
{ {
@ -100,20 +99,16 @@ pub fn writeMesh(writer: anytype, value: Mesh, endian: std.builtin.Endian) !void
try writer.writeStruct(value.material); try writer.writeStruct(value.material);
// Sizes // Sizes
try writer.writeInt(usize, value.vertices.len, endian); try writer.writeInt(usize, value.positions.len, endian);
try writer.writeInt(usize, value.indices.len, endian); try writer.writeInt(usize, value.indices.len, endian);
for (value.vertices) |v| { for (value.positions) |v| {
try writeVector3(writer, v, endian); try writeVector3(writer, v, endian);
} }
for (value.normals) |n| { for (value.other_data) |d| {
try writeVector3(writer, n, endian); try writeVector3(writer, d.normal, endian);
} try writeVector3(writer, d.tangent, endian);
for (value.tangents) |t| { try writeVector2(writer, d.uv, endian);
try writeVector3(writer, t, endian);
}
for (value.uvs) |uv| {
try writeVector2(writer, uv, endian);
} }
for (value.indices) |i| { for (value.indices) |i| {
try writer.writeInt(Index, i, endian); try writer.writeInt(Index, i, endian);

View File

@ -79,6 +79,8 @@ fn game_init_window_err(global_allocator: std.mem.Allocator) !void {
.height = globals.DEFAULT_HEIGHT, .height = globals.DEFAULT_HEIGHT,
}; };
// TODO: move this into game_init so that if game is recreated vulkan device is also recreated.
// This means all gpu resources will be destroyed together with the device. Seems like an easier way to handle this case
try globals.g_init.gc.init(global_allocator, window); try globals.g_init.gc.init(global_allocator, window);
const version = &globals.g_init.syswm_info.version; const version = &globals.g_init.syswm_info.version;
@ -234,62 +236,62 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
// }); // });
// Plane // Plane
// _ = globals.g_mem.world.addEntity(.{ _ = globals.g_mem.world.addEntity(.{
// .flags = .{ .mesh = true }, .flags = .{ .mesh = true },
// .transform = .{ .scale = Vec3.one().scale(10) }, .transform = .{ .scale = Vec3.one().scale(10) },
// .mesh = .{ .mesh = .{
// .handle = a.Meshes.plane.Plane, .handle = a.Meshes.plane.Plane,
// .material = .{ .material = .{
// .albedo = Vec4.one(), .albedo = Vec4.one(),
// .normal_map = a.Textures.@"tile.norm", .normal_map = a.Textures.@"tile.norm",
// }, },
// .override_material = true, .override_material = true,
// }, },
// }); });
// 10 dielectric bunnies // 10 dielectric bunnies
// { {
// for (0..100) |y| { for (0..100) |y| {
// for (0..1) |x| { for (0..1) |x| {
// _ = globals.g_mem.world.addEntity(.{ _ = globals.g_mem.world.addEntity(.{
// .transform = .{ .pos = Vec3.new(@as(f32, @floatFromInt(x)) * 0.3 - 0.3 * 4.5, 0, @as(f32, @floatFromInt(y)) * 0.3 - 0.3 * 4.5) }, .transform = .{ .pos = Vec3.new(@as(f32, @floatFromInt(x)) * 0.3 - 0.3 * 4.5, 0, @as(f32, @floatFromInt(y)) * 0.3 - 0.3 * 4.5) },
// .flags = .{ .mesh = true }, .flags = .{ .mesh = true },
// .mesh = .{ .mesh = .{
// .handle = a.Meshes.bunny.BunnyStanfordUVUnwrapped_res1_bun_zipper_res1, .handle = a.Meshes.bunny.BunnyStanfordUVUnwrapped_res1_bun_zipper_res1,
// .material = .{ .material = .{
.albedo_map = a.Textures.bunny_tex1,
// .normal_map = a.Textures.@"tile.norm",
.roughness = @as(f32, @floatFromInt(y)) / 100.0,
},
.override_material = true,
},
});
}
}
}
// 10 metallic bunnies
{
for (0..10) |i| {
_ = globals.g_mem.world.addEntity(.{
.transform = .{ .pos = Vec3.new(@as(f32, @floatFromInt(i)) * 0.3 - 0.3 * 4.5, 0.3, 0) },
.flags = .{ .mesh = true },
.mesh = .{
.handle = a.Meshes.bunny.BunnyStanfordUVUnwrapped_res1_bun_zipper_res1,
.material = .{
.blend_mode = .AlphaBlend,
.albedo = Vec4.new(1.000, 0.766, 0.336, 0.5),
// .albedo_map = a.Textures.bunny_tex1, // .albedo_map = a.Textures.bunny_tex1,
// // .normal_map = a.Textures.@"tile.norm", // .normal_map = a.Textures.@"tile.norm",
// .roughness = @as(f32, @floatFromInt(y)) / 100.0, .roughness = @as(f32, @floatFromInt(i + 1)) / 10.0,
// }, .metallic = 1.0,
// .override_material = true, },
// }, .override_material = true,
// }); },
// } });
// } }
// } }
// // 10 metallic bunnies
// {
// for (0..10) |i| {
// _ = globals.g_mem.world.addEntity(.{
// .transform = .{ .pos = Vec3.new(@as(f32, @floatFromInt(i)) * 0.3 - 0.3 * 4.5, 0.3, 0) },
// .flags = .{ .mesh = true },
// .mesh = .{
// .handle = a.Meshes.bunny.BunnyStanfordUVUnwrapped_res1_bun_zipper_res1,
// .material = .{
// .blend_mode = .AlphaBlend,
// .albedo = Vec4.new(1.000, 0.766, 0.336, 0.5),
// // .albedo_map = a.Textures.bunny_tex1,
// // .normal_map = a.Textures.@"tile.norm",
// .roughness = @as(f32, @floatFromInt(i + 1)) / 10.0,
// .metallic = 1.0,
// },
// .override_material = true,
// },
// });
// }
// }
// const scene = globals.g_mem.world.createScene(globals.g_assetman.resolveScene(a.Scenes.bistro.scene)); // const scene = globals.g_mem.world.createScene(globals.g_assetman.resolveScene(a.Scenes.bistro.scene));
// const ent = globals.g_mem.world.getEntity(scene) orelse @panic("WTF"); // const ent = globals.g_mem.world.getEntity(scene) orelse @panic("WTF");
@ -461,14 +463,11 @@ export fn game_update() bool {
} }
} }
gmem.render2.draw() catch @panic("RENDER ERROR");
// Render // Render
if (false) { {
const zone = tracy.initZone(@src(), .{ .name = "game.render()" }); const zone = tracy.initZone(@src(), .{ .name = "game.render()" });
defer zone.deinit(); defer zone.deinit();
gmem.render.begin(); defer gmem.render2.finish() catch @panic("render.finish()");
defer gmem.render.finish();
// Collect point lights // Collect point lights
{ {
@ -478,47 +477,44 @@ export fn game_update() bool {
if (ent.data.flags.mesh) { if (ent.data.flags.mesh) {
const mat_override: ?formats.Material = if (ent.data.mesh.override_material) ent.data.mesh.material else null; const mat_override: ?formats.Material = if (ent.data.mesh.override_material) ent.data.mesh.material else null;
gmem.render.draw(.{ gmem.render2.draw(.{
.mesh = ent.data.mesh.handle, .mesh = ent.data.mesh.handle,
.material_override = mat_override, .material_override = mat_override,
.transform = ent.globalMatrix(&gmem.world).*, .transform = ent.globalMatrix(&gmem.world).*,
}); });
} else if (ent.data.flags.point_light) { } else if (ent.data.flags.point_light) {
gmem.render.draw(.{ gmem.render2.draw(.{
.mesh = a.Meshes.sphere.Icosphere, .mesh = a.Meshes.sphere.Icosphere,
.material_override = .{ .albedo = Vec4.new(0, 0, 0, 1), .emission = ent.data.light.premultipliedColor() }, .material_override = .{ .albedo = Vec4.new(0, 0, 0, 1), .emission = ent.data.light.premultipliedColor() },
.transform = ent.globalMatrix(&gmem.world).*, .transform = ent.globalMatrix(&gmem.world).*,
}); });
} }
if (ent.data.flags.point_light) { // if (ent.data.flags.point_light) {
const pos = ent.globalMatrix(&gmem.world).extractTranslation(); // const pos = ent.globalMatrix(&gmem.world).extractTranslation();
const color = ent.data.light.premultipliedColor(); // const color = ent.data.light.premultipliedColor();
gmem.render.drawLight(.{ // gmem.render.drawLight(.{
.point = .{ // .point = .{
.pos = pos, // .pos = pos,
.radius = ent.data.point_light.radius, // .radius = ent.data.point_light.radius,
.color = color, // .color = color,
}, // },
}); // });
} // }
if (ent.data.flags.dir_light) { // if (ent.data.flags.dir_light) {
const dir4 = ent.globalMatrix(&gmem.world).mulByVec4(Vec4.forward()); // const dir4 = ent.globalMatrix(&gmem.world).mulByVec4(Vec4.forward());
const color = ent.data.light.premultipliedColor(); // const color = ent.data.light.premultipliedColor();
gmem.render.drawLight(.{ // gmem.render.drawLight(.{
.directional = .{ // .directional = .{
.dir = dir4.toVec3(), // .dir = dir4.toVec3(),
.color = color, // .color = color,
}, // },
}); // });
// }
} }
} }
// TODO: get rid of this
gmem.render.flushUBOs();
}
} }
// { // {

View File

@ -32,6 +32,10 @@ const scene_formats = [_][]const u8{
".glb", ".glb",
}; };
pub fn float(int: anytype) f32 {
return @floatFromInt(int);
}
pub fn resolveAssetTypeByExtension(path: []const u8) ?AssetType { pub fn resolveAssetTypeByExtension(path: []const u8) ?AssetType {
for (scene_formats) |ext| { for (scene_formats) |ext| {
if (std.mem.endsWith(u8, path, ext)) { if (std.mem.endsWith(u8, path, ext)) {
@ -498,30 +502,28 @@ fn processMesh(allocator: std.mem.Allocator, scene: *const c.aiScene, material_o
if (mesh.mTextureCoords[0] == null) return error.MissingUVs; if (mesh.mTextureCoords[0] == null) return error.MissingUVs;
if (mesh.mNumUVComponents[0] != 2) return error.WrongUVComponents; if (mesh.mNumUVComponents[0] != 2) return error.WrongUVComponents;
var vertices = try allocator.alloc(Vector3, @intCast(mesh.mNumVertices)); var positions = try allocator.alloc(Vector3, @intCast(mesh.mNumVertices));
var normals = try allocator.alloc(Vector3, @intCast(mesh.mNumVertices)); var other_data = try allocator.alloc(formats.Mesh.VertexDataForBasePass, @intCast(mesh.mNumVertices));
var tangents = try allocator.alloc(Vector3, @intCast(mesh.mNumVertices));
var uvs = try allocator.alloc(Vector2, @intCast(mesh.mNumVertices));
var indices = try allocator.alloc(formats.Index, @intCast(mesh.mNumFaces * 3)); // triangles var indices = try allocator.alloc(formats.Index, @intCast(mesh.mNumFaces * 3)); // triangles
for (0..mesh.mNumVertices) |i| { for (0..mesh.mNumVertices) |i| {
vertices[i] = .{ positions[i] = .{
.x = mesh.mVertices[i].x, .x = mesh.mVertices[i].x,
.y = mesh.mVertices[i].y, .y = mesh.mVertices[i].y,
.z = mesh.mVertices[i].z, .z = mesh.mVertices[i].z,
}; };
normals[i] = .{ other_data[i].normal = .{
.x = mesh.mNormals[i].x, .x = mesh.mNormals[i].x,
.y = mesh.mNormals[i].y, .y = mesh.mNormals[i].y,
.z = mesh.mNormals[i].z, .z = mesh.mNormals[i].z,
}; };
tangents[i] = .{ other_data[i].tangent = .{
.x = mesh.mTangents[i].x, .x = mesh.mTangents[i].x,
.y = mesh.mTangents[i].y, .y = mesh.mTangents[i].y,
.z = mesh.mTangents[i].z, .z = mesh.mTangents[i].z,
}; };
uvs[i] = .{ other_data[i].uv = .{
.x = mesh.mTextureCoords[0][i].x, .x = mesh.mTextureCoords[0][i].x,
.y = mesh.mTextureCoords[0][i].y, .y = mesh.mTextureCoords[0][i].y,
}; };
@ -554,10 +556,8 @@ fn processMesh(allocator: std.mem.Allocator, scene: *const c.aiScene, material_o
.z = mesh.mAABB.mMax.z, .z = mesh.mAABB.mMax.z,
}, },
}, },
.vertices = vertices, .positions = positions,
.normals = normals, .other_data = other_data,
.tangents = tangents,
.uvs = uvs,
.indices = indices, .indices = indices,
.material = material, .material = material,
}; };
@ -593,7 +593,7 @@ fn processShader(allocator: std.mem.Allocator, flags: []const []const u8, input:
const compile_result = try std.process.Child.run(.{ const compile_result = try std.process.Child.run(.{
.allocator = allocator, .allocator = allocator,
.argv = try std.mem.concat(allocator, []const u8, &.{ .argv = try std.mem.concat(allocator, []const u8, &.{
&.{ "glslc", "--target-env=vulkan1.3", "-std=460core", "-g", "-o", "-" }, &.{ "glslc", "--target-env=vulkan1.3", "-std=460core", "-g", "-O0", "-o", "-" },
if (maybe_dep_file) |dep| &.{ "-MD", "-MF", dep } else &.{}, if (maybe_dep_file) |dep| &.{ "-MD", "-MF", dep } else &.{},
flags, flags,
&.{input}, &.{input},
@ -821,7 +821,7 @@ fn processTexture(allocator: std.mem.Allocator, texture_type: TextureType, conte
const mip_levels_to_gen = 1 + @as( const mip_levels_to_gen = 1 + @as(
u32, u32,
@intFromFloat(@log2(@as(f32, @floatFromInt(@max(width, height))))), @intFromFloat(@log2(float(@max(width, height)))),
); );
var actual_mip_count: usize = 1; var actual_mip_count: usize = 1;
@ -972,10 +972,10 @@ fn premultiplyAlpha(img: []u8) void {
for (0..img.len / 4) |i| { for (0..img.len / 4) |i| {
const pixel = img[i * 4 .. i * 4 + 4]; const pixel = img[i * 4 .. i * 4 + 4];
const r = @as(f32, @floatFromInt(pixel[0])) / 255.0; const r = float(pixel[0]) / 255.0;
const g = @as(f32, @floatFromInt(pixel[1])) / 255.0; const g = float(pixel[1]) / 255.0;
const b = @as(f32, @floatFromInt(pixel[2])) / 255.0; const b = float(pixel[2]) / 255.0;
const a = @as(f32, @floatFromInt(pixel[3])) / 255.0; const a = float(pixel[3]) / 255.0;
pixel[0] = @intFromFloat(r * a * 255.0); pixel[0] = @intFromFloat(r * a * 255.0);
pixel[1] = @intFromFloat(g * a * 255.0); pixel[1] = @intFromFloat(g * a * 255.0);
@ -1040,8 +1040,8 @@ inline fn loadColorVec2(pixel: []const u8) @Vector(2, f32) {
std.debug.assert(pixel.len >= 2); std.debug.assert(pixel.len >= 2);
return @Vector(2, f32){ return @Vector(2, f32){
@as(f32, @floatFromInt(pixel[0])), float(pixel[0]),
@as(f32, @floatFromInt(pixel[1])), float(pixel[1]),
} / @as(@Vector(2, f32), @splat(255.0)); } / @as(@Vector(2, f32), @splat(255.0));
} }
@ -1060,10 +1060,10 @@ inline fn loadColorVec4(pixel: []const u8) @Vector(4, f32) {
std.debug.assert(pixel.len >= 4); std.debug.assert(pixel.len >= 4);
return @Vector(4, f32){ return @Vector(4, f32){
@as(f32, @floatFromInt(pixel[0])), float(pixel[0]),
@as(f32, @floatFromInt(pixel[1])), float(pixel[1]),
@as(f32, @floatFromInt(pixel[2])), float(pixel[2]),
@as(f32, @floatFromInt(pixel[3])), float(pixel[3]),
} / @as(@Vector(4, f32), @splat(255.0)); } / @as(@Vector(4, f32), @splat(255.0));
} }