Mesh loading WIP
This commit is contained in:
parent
afa00106e4
commit
bd903da009
@ -4,6 +4,32 @@
|
||||
|
||||
#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
|
||||
|
||||
// QUAD
|
||||
@ -20,10 +46,12 @@ vec2 positions[6] = vec2[](
|
||||
layout(location = 0) out vec2 OutUV;
|
||||
|
||||
void main() {
|
||||
OutUV = positions[gl_VertexIndex] * 0.5 + 0.5;
|
||||
OutUV.y = 1 - OutUV.y;
|
||||
PositionBuffer positions_ptr = PushConstants.positions;
|
||||
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
|
||||
@ -31,16 +59,10 @@ void main() {
|
||||
#if FRAGMENT_SHADER
|
||||
|
||||
layout(location = 0) in vec2 InUV;
|
||||
|
||||
layout(location = 0) out vec4 FragColor;
|
||||
|
||||
layout(push_constant, std430) uniform constants {
|
||||
uint color_texture;
|
||||
uint color_sampler;
|
||||
} PushConstants;
|
||||
|
||||
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
|
||||
|
@ -182,7 +182,7 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, gc: *G
|
||||
.frame_arena = frame_arena,
|
||||
.exe_dir = exe_dir,
|
||||
.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,
|
||||
.descriptorman = descriptorman,
|
||||
.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 {
|
||||
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| {
|
||||
switch (asset.*) {
|
||||
.mesh => |mesh| {
|
||||
@ -552,34 +566,10 @@ const NullShaderProgram = LoadedShaderProgram{
|
||||
|
||||
const NullMesh = LoadedMesh{
|
||||
.aabb = .{},
|
||||
.heap_handle = .{},
|
||||
.positions = BufferSlice{
|
||||
.buffer = 0,
|
||||
.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,
|
||||
},
|
||||
.allocation = .{},
|
||||
.positions = 0,
|
||||
.other_data = 0,
|
||||
.indices_offset = 0,
|
||||
.material = .{},
|
||||
};
|
||||
|
||||
@ -609,67 +599,194 @@ fn loadMeshErr(self: *AssetManager, id: AssetId) !LoadedMesh {
|
||||
defer self.frame_arena.free(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 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));
|
||||
checkGLError();
|
||||
gl.namedBufferSubData(self.vertex_heap.normals.buffer, @intCast(vertex_offset * @sizeOf(formats.Vector3)), @intCast(vertices_len * @sizeOf(formats.Vector3)), @ptrCast(mesh.normals.ptr));
|
||||
checkGLError();
|
||||
gl.namedBufferSubData(self.vertex_heap.tangents.buffer, @intCast(vertex_offset * @sizeOf(formats.Vector3)), @intCast(vertices_len * @sizeOf(formats.Vector3)), @ptrCast(mesh.tangents.ptr));
|
||||
checkGLError();
|
||||
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 staging_buffer = try self.gc.device.createBuffer(&.{
|
||||
.usage = .{ .transfer_src_bit = true },
|
||||
.sharing_mode = .exclusive,
|
||||
.size = data_size,
|
||||
}, null);
|
||||
errdefer self.gc.device.destroyBuffer(staging_buffer, null);
|
||||
|
||||
const index_offset = allocation.index.offset;
|
||||
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 staging_mem_reqs = self.gc.device.getBufferMemoryRequirements(staging_buffer);
|
||||
|
||||
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 = .{
|
||||
.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),
|
||||
},
|
||||
.heap_handle = allocation,
|
||||
.material = mesh.material,
|
||||
.positions = .{
|
||||
.buffer = self.vertex_heap.vertices.buffer,
|
||||
.offset = @intCast(vertex_offset * @sizeOf(formats.Vector3)),
|
||||
.stride = @sizeOf(formats.Vector3),
|
||||
},
|
||||
.normals = .{
|
||||
.buffer = self.vertex_heap.normals.buffer,
|
||||
.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),
|
||||
},
|
||||
.vertex_count = @intCast(vertices_len),
|
||||
.index_count = @intCast(mesh.indices.len),
|
||||
.allocation = allocation,
|
||||
.transfer_fence = copy_fence,
|
||||
.staging_buf = staging_buffer,
|
||||
.staging_memory = staging_mem,
|
||||
.command_buffer = cmds.handle,
|
||||
};
|
||||
|
||||
{
|
||||
self.rw_lock.lock();
|
||||
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);
|
||||
}
|
||||
return loaded_mesh;
|
||||
|
||||
return NullMesh;
|
||||
}
|
||||
|
||||
fn loadTexture(self: *AssetManager, id: AssetId) LoadedTexture {
|
||||
@ -770,6 +887,8 @@ fn loadTextureErr(self: *AssetManager, id: AssetId) !LoadedTexture {
|
||||
offset += texture.data[mip].len;
|
||||
}
|
||||
|
||||
self.gc.device.unmapMemory(staging_mem);
|
||||
|
||||
const cmds = try self.command_pool.allocateCommandBuffer();
|
||||
|
||||
try cmds.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
||||
@ -881,56 +1000,6 @@ fn loadTextureErr(self: *AssetManager, id: AssetId) !LoadedTexture {
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -1002,6 +1071,32 @@ fn loadMaterialErr(self: *AssetManager, id: AssetId) !formats.Material {
|
||||
fn finishLoadingAsset(self: *AssetManager, id: AssetId) void {
|
||||
if (self.resolveLoadingAsset(id)) |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| {
|
||||
{
|
||||
self.rw_lock.lock();
|
||||
@ -1037,7 +1132,7 @@ const AssetType = enum {
|
||||
const LoadingAsset = union(AssetType) {
|
||||
shader: void,
|
||||
shaderProgram: void,
|
||||
mesh: void,
|
||||
mesh: LoadingMesh,
|
||||
texture: LoadingTexture,
|
||||
scene: void,
|
||||
material: void,
|
||||
@ -1061,15 +1156,32 @@ const LoadedShaderProgram = struct {
|
||||
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 {
|
||||
aabb: AABB,
|
||||
heap_handle: VertexBufferHeap.Alloc,
|
||||
positions: BufferSlice,
|
||||
normals: BufferSlice,
|
||||
tangents: BufferSlice,
|
||||
uvs: BufferSlice,
|
||||
indices: IndexSlice,
|
||||
allocation: VertexBufferHeap.Alloc,
|
||||
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 {
|
||||
@ -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 {
|
||||
bytes: []u8,
|
||||
modified: i128,
|
||||
@ -1190,13 +1255,6 @@ fn loadFile(self: *AssetManager, allocator: std.mem.Allocator, path: []const u8,
|
||||
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 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 {
|
||||
{
|
||||
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;
|
||||
switch (asset.*) {
|
||||
.mesh => |*mesh| {
|
||||
self.vertex_heap.free(mesh.heap_handle);
|
||||
self.vertex_heap.free(mesh.allocation);
|
||||
},
|
||||
.shader => |*shader| {
|
||||
self.allocator.free(shader.source);
|
||||
@ -1922,122 +1862,84 @@ const VertexBufferHeap = struct {
|
||||
const Self = @This();
|
||||
|
||||
pub const Alloc = struct {
|
||||
vertex: BuddyAllocator.Alloc = .{},
|
||||
index: BuddyAllocator.Alloc = .{},
|
||||
positions: BuddyAllocator.Alloc = .{},
|
||||
other_data: BuddyAllocator.Alloc = .{},
|
||||
indices: BuddyAllocator.Alloc = .{},
|
||||
};
|
||||
|
||||
pub const Buffer = struct {
|
||||
buffer: gl.GLuint,
|
||||
stride: gl.GLsizei,
|
||||
gc: *GraphicsContext,
|
||||
buddy: BuddyAllocator,
|
||||
// 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 {
|
||||
return .{
|
||||
.buffer = name,
|
||||
.stride = @intCast(stride),
|
||||
};
|
||||
pub fn init(allocator: std.mem.Allocator, gc: *GraphicsContext) !Self {
|
||||
const device = gc.device;
|
||||
|
||||
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 .{
|
||||
.vertex_buddy = vertex_buddy,
|
||||
.index_buddy = index_buddy,
|
||||
.vertices = vertices,
|
||||
.normals = normals,
|
||||
.tangents = tangents,
|
||||
.uvs = uvs,
|
||||
.indices = indices,
|
||||
.gc = gc,
|
||||
.buddy = buddy,
|
||||
.heap_memory = heap_memory,
|
||||
.heap_buffer = heap_buffer,
|
||||
.heap_address = heap_address,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.index_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);
|
||||
self.buddy.deinit();
|
||||
}
|
||||
|
||||
pub fn alloc(self: *Self, vertex_len: usize, index_len: usize) !Alloc {
|
||||
const vertex_alloc = try self.vertex_buddy.alloc(vertex_len);
|
||||
errdefer self.vertex_buddy.free(vertex_alloc);
|
||||
const positions_alloc = try self.buddy.alloc(@sizeOf(formats.Vector3) * vertex_len);
|
||||
errdefer self.buddy.free(positions_alloc);
|
||||
|
||||
const index_alloc = try self.index_buddy.alloc(index_len);
|
||||
errdefer self.index_buddy.free(index_alloc);
|
||||
const other_data_alloc = try self.buddy.alloc(@sizeOf(formats.Mesh.VertexDataForBasePass) * vertex_len);
|
||||
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 {
|
||||
self.vertex_buddy.free(allocation.vertex);
|
||||
self.index_buddy.free(allocation.index);
|
||||
self.buddy.free(allocation.positions);
|
||||
self.buddy.free(allocation.other_data);
|
||||
self.buddy.free(allocation.indices);
|
||||
}
|
||||
};
|
||||
|
@ -1177,7 +1177,7 @@ pub fn finish(self: *Render) void {
|
||||
gl.bindVertexArray(self.z_prepass_vao);
|
||||
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();
|
||||
self.assetman.vertex_heap.uvs.bind(Render.Attrib.UV.value());
|
||||
checkGLError();
|
||||
@ -1210,7 +1210,7 @@ pub fn finish(self: *Render) void {
|
||||
checkGLError();
|
||||
self.assetman.vertex_heap.uvs.bind(Render.Attrib.UV.value());
|
||||
checkGLError();
|
||||
self.assetman.vertex_heap.vertices.bind(Render.Attrib.Position.value());
|
||||
self.assetman.vertex_heap.vertex_data.bind(Render.Attrib.Position.value());
|
||||
checkGLError();
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.assetman.vertex_heap.indices.buffer);
|
||||
checkGLError();
|
||||
@ -1439,7 +1439,7 @@ fn renderShadow(self: *Render, frustum: *const math.Frustum) void {
|
||||
_ = frustum; // autofix
|
||||
const zone = tracy.initZone(@src(), .{ .name = "Render.renderShadow" });
|
||||
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);
|
||||
|
||||
// TODO: this wastes space in temp allocator
|
||||
|
@ -8,6 +8,7 @@ const za = @import("zalgebra");
|
||||
const Vec3 = za.Vec3;
|
||||
const Mat4 = za.Mat4;
|
||||
const common = @import("common.zig");
|
||||
const formats = @import("formats.zig");
|
||||
const render_common = @import("render_common.zig");
|
||||
const MAX_FRAME_LAG = render_common.MAX_FRAME_LAG;
|
||||
const DeferredDestroyQueue = render_common.DeferredDestroyQueue;
|
||||
@ -44,6 +45,9 @@ command_pool: GraphicsContext.CommandPool,
|
||||
vulkan_frame_arena: VulkanPerFrameArena,
|
||||
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
|
||||
screen_color_sampler: vk.Sampler = .null_handle,
|
||||
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);
|
||||
}
|
||||
|
||||
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 device = gc.device;
|
||||
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.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{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
@ -591,7 +601,21 @@ pub fn draw(self: *Render2) !void {
|
||||
.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
|
||||
@ -668,6 +692,8 @@ pub fn draw(self: *Render2) !void {
|
||||
}
|
||||
try cmds.endCommandBuffer();
|
||||
|
||||
self.draw_command_count = 0;
|
||||
|
||||
try gc.queues.graphics.submit(
|
||||
&GraphicsContext.SubmitInfo{
|
||||
.wait_semaphores = &.{frame.acquire_swapchain_image},
|
||||
@ -716,6 +742,9 @@ const GlobalUniform = extern struct {
|
||||
};
|
||||
|
||||
const UnlitPushConstants = extern struct {
|
||||
local_to_world: Mat4,
|
||||
positions_address: u64,
|
||||
other_data_address: u64,
|
||||
color_texture: u32,
|
||||
color_sampler: u32,
|
||||
};
|
||||
|
@ -23,14 +23,19 @@ pub const AABB = extern struct {
|
||||
};
|
||||
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 VertexDataForBasePass = extern struct {
|
||||
normal: Vector3,
|
||||
tangent: Vector3,
|
||||
uv: Vector2,
|
||||
};
|
||||
|
||||
aabb: AABB,
|
||||
material: Material,
|
||||
|
||||
vertices: []align(1) Vector3,
|
||||
normals: []align(1) Vector3,
|
||||
tangents: []align(1) Vector3,
|
||||
uvs: []align(1) Vector2,
|
||||
positions: []align(1) Vector3,
|
||||
other_data: []align(1) VertexDataForBasePass,
|
||||
indices: []align(1) Index,
|
||||
|
||||
// This will panic if data is incorrect
|
||||
@ -61,15 +66,11 @@ pub const Mesh = struct {
|
||||
offset += @sizeOf(usize);
|
||||
|
||||
size = vert_len * @sizeOf(Vector3);
|
||||
const vertices = 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]);
|
||||
const positions = std.mem.bytesAsSlice(Vector3, buffer[offset .. offset + size]);
|
||||
offset += size;
|
||||
|
||||
size = vert_len * @sizeOf(Vector2);
|
||||
const uvs = std.mem.bytesAsSlice(Vector2, buffer[offset .. offset + size]);
|
||||
size = vert_len * @sizeOf(VertexDataForBasePass);
|
||||
const other_data = std.mem.bytesAsSlice(VertexDataForBasePass, buffer[offset .. offset + size]);
|
||||
offset += size;
|
||||
|
||||
size = ind_len * @sizeOf(Index);
|
||||
@ -79,17 +80,15 @@ pub const Mesh = struct {
|
||||
return .{
|
||||
.aabb = aabb,
|
||||
.material = material.*,
|
||||
.vertices = vertices,
|
||||
.normals = normals,
|
||||
.tangents = tangents,
|
||||
.uvs = uvs,
|
||||
.positions = positions,
|
||||
.other_data = other_data,
|
||||
.indices = indices,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
@ -100,20 +99,16 @@ pub fn writeMesh(writer: anytype, value: Mesh, endian: std.builtin.Endian) !void
|
||||
try writer.writeStruct(value.material);
|
||||
|
||||
// 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);
|
||||
|
||||
for (value.vertices) |v| {
|
||||
for (value.positions) |v| {
|
||||
try writeVector3(writer, v, endian);
|
||||
}
|
||||
for (value.normals) |n| {
|
||||
try writeVector3(writer, n, endian);
|
||||
}
|
||||
for (value.tangents) |t| {
|
||||
try writeVector3(writer, t, endian);
|
||||
}
|
||||
for (value.uvs) |uv| {
|
||||
try writeVector2(writer, uv, endian);
|
||||
for (value.other_data) |d| {
|
||||
try writeVector3(writer, d.normal, endian);
|
||||
try writeVector3(writer, d.tangent, endian);
|
||||
try writeVector2(writer, d.uv, endian);
|
||||
}
|
||||
for (value.indices) |i| {
|
||||
try writer.writeInt(Index, i, endian);
|
||||
|
162
src/game.zig
162
src/game.zig
@ -79,6 +79,8 @@ fn game_init_window_err(global_allocator: std.mem.Allocator) !void {
|
||||
.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);
|
||||
|
||||
const version = &globals.g_init.syswm_info.version;
|
||||
@ -234,62 +236,62 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
|
||||
// });
|
||||
|
||||
// Plane
|
||||
// _ = globals.g_mem.world.addEntity(.{
|
||||
// .flags = .{ .mesh = true },
|
||||
// .transform = .{ .scale = Vec3.one().scale(10) },
|
||||
// .mesh = .{
|
||||
// .handle = a.Meshes.plane.Plane,
|
||||
// .material = .{
|
||||
// .albedo = Vec4.one(),
|
||||
// .normal_map = a.Textures.@"tile.norm",
|
||||
// },
|
||||
// .override_material = true,
|
||||
// },
|
||||
// });
|
||||
_ = globals.g_mem.world.addEntity(.{
|
||||
.flags = .{ .mesh = true },
|
||||
.transform = .{ .scale = Vec3.one().scale(10) },
|
||||
.mesh = .{
|
||||
.handle = a.Meshes.plane.Plane,
|
||||
.material = .{
|
||||
.albedo = Vec4.one(),
|
||||
.normal_map = a.Textures.@"tile.norm",
|
||||
},
|
||||
.override_material = true,
|
||||
},
|
||||
});
|
||||
|
||||
// 10 dielectric bunnies
|
||||
// {
|
||||
// for (0..100) |y| {
|
||||
// for (0..1) |x| {
|
||||
// _ = 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) },
|
||||
{
|
||||
for (0..100) |y| {
|
||||
for (0..1) |x| {
|
||||
_ = 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) },
|
||||
|
||||
// .flags = .{ .mesh = true },
|
||||
// .mesh = .{
|
||||
// .handle = a.Meshes.bunny.BunnyStanfordUVUnwrapped_res1_bun_zipper_res1,
|
||||
// .material = .{
|
||||
.flags = .{ .mesh = true },
|
||||
.mesh = .{
|
||||
.handle = a.Meshes.bunny.BunnyStanfordUVUnwrapped_res1_bun_zipper_res1,
|
||||
.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,
|
||||
// // .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,
|
||||
// // .normal_map = a.Textures.@"tile.norm",
|
||||
// .roughness = @as(f32, @floatFromInt(i + 1)) / 10.0,
|
||||
// .metallic = 1.0,
|
||||
// },
|
||||
// .override_material = true,
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// .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 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
|
||||
if (false) {
|
||||
{
|
||||
const zone = tracy.initZone(@src(), .{ .name = "game.render()" });
|
||||
defer zone.deinit();
|
||||
gmem.render.begin();
|
||||
defer gmem.render.finish();
|
||||
defer gmem.render2.finish() catch @panic("render.finish()");
|
||||
|
||||
// Collect point lights
|
||||
{
|
||||
@ -478,47 +477,44 @@ export fn game_update() bool {
|
||||
|
||||
if (ent.data.flags.mesh) {
|
||||
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,
|
||||
.material_override = mat_override,
|
||||
.transform = ent.globalMatrix(&gmem.world).*,
|
||||
});
|
||||
} else if (ent.data.flags.point_light) {
|
||||
gmem.render.draw(.{
|
||||
gmem.render2.draw(.{
|
||||
.mesh = a.Meshes.sphere.Icosphere,
|
||||
.material_override = .{ .albedo = Vec4.new(0, 0, 0, 1), .emission = ent.data.light.premultipliedColor() },
|
||||
.transform = ent.globalMatrix(&gmem.world).*,
|
||||
});
|
||||
}
|
||||
|
||||
if (ent.data.flags.point_light) {
|
||||
const pos = ent.globalMatrix(&gmem.world).extractTranslation();
|
||||
const color = ent.data.light.premultipliedColor();
|
||||
// if (ent.data.flags.point_light) {
|
||||
// const pos = ent.globalMatrix(&gmem.world).extractTranslation();
|
||||
// const color = ent.data.light.premultipliedColor();
|
||||
|
||||
gmem.render.drawLight(.{
|
||||
.point = .{
|
||||
.pos = pos,
|
||||
.radius = ent.data.point_light.radius,
|
||||
.color = color,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (ent.data.flags.dir_light) {
|
||||
const dir4 = ent.globalMatrix(&gmem.world).mulByVec4(Vec4.forward());
|
||||
const color = ent.data.light.premultipliedColor();
|
||||
// gmem.render.drawLight(.{
|
||||
// .point = .{
|
||||
// .pos = pos,
|
||||
// .radius = ent.data.point_light.radius,
|
||||
// .color = color,
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
// if (ent.data.flags.dir_light) {
|
||||
// const dir4 = ent.globalMatrix(&gmem.world).mulByVec4(Vec4.forward());
|
||||
// const color = ent.data.light.premultipliedColor();
|
||||
|
||||
gmem.render.drawLight(.{
|
||||
.directional = .{
|
||||
.dir = dir4.toVec3(),
|
||||
.color = color,
|
||||
},
|
||||
});
|
||||
// gmem.render.drawLight(.{
|
||||
// .directional = .{
|
||||
// .dir = dir4.toVec3(),
|
||||
// .color = color,
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: get rid of this
|
||||
gmem.render.flushUBOs();
|
||||
}
|
||||
}
|
||||
|
||||
// {
|
||||
|
@ -32,6 +32,10 @@ const scene_formats = [_][]const u8{
|
||||
".glb",
|
||||
};
|
||||
|
||||
pub fn float(int: anytype) f32 {
|
||||
return @floatFromInt(int);
|
||||
}
|
||||
|
||||
pub fn resolveAssetTypeByExtension(path: []const u8) ?AssetType {
|
||||
for (scene_formats) |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.mNumUVComponents[0] != 2) return error.WrongUVComponents;
|
||||
|
||||
var vertices = try allocator.alloc(Vector3, @intCast(mesh.mNumVertices));
|
||||
var normals = try allocator.alloc(Vector3, @intCast(mesh.mNumVertices));
|
||||
var tangents = try allocator.alloc(Vector3, @intCast(mesh.mNumVertices));
|
||||
var uvs = try allocator.alloc(Vector2, @intCast(mesh.mNumVertices));
|
||||
var positions = try allocator.alloc(Vector3, @intCast(mesh.mNumVertices));
|
||||
var other_data = try allocator.alloc(formats.Mesh.VertexDataForBasePass, @intCast(mesh.mNumVertices));
|
||||
|
||||
var indices = try allocator.alloc(formats.Index, @intCast(mesh.mNumFaces * 3)); // triangles
|
||||
|
||||
for (0..mesh.mNumVertices) |i| {
|
||||
vertices[i] = .{
|
||||
positions[i] = .{
|
||||
.x = mesh.mVertices[i].x,
|
||||
.y = mesh.mVertices[i].y,
|
||||
.z = mesh.mVertices[i].z,
|
||||
};
|
||||
normals[i] = .{
|
||||
other_data[i].normal = .{
|
||||
.x = mesh.mNormals[i].x,
|
||||
.y = mesh.mNormals[i].y,
|
||||
.z = mesh.mNormals[i].z,
|
||||
};
|
||||
tangents[i] = .{
|
||||
other_data[i].tangent = .{
|
||||
.x = mesh.mTangents[i].x,
|
||||
.y = mesh.mTangents[i].y,
|
||||
.z = mesh.mTangents[i].z,
|
||||
};
|
||||
uvs[i] = .{
|
||||
other_data[i].uv = .{
|
||||
.x = mesh.mTextureCoords[0][i].x,
|
||||
.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,
|
||||
},
|
||||
},
|
||||
.vertices = vertices,
|
||||
.normals = normals,
|
||||
.tangents = tangents,
|
||||
.uvs = uvs,
|
||||
.positions = positions,
|
||||
.other_data = other_data,
|
||||
.indices = indices,
|
||||
.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(.{
|
||||
.allocator = allocator,
|
||||
.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 &.{},
|
||||
flags,
|
||||
&.{input},
|
||||
@ -821,7 +821,7 @@ fn processTexture(allocator: std.mem.Allocator, texture_type: TextureType, conte
|
||||
|
||||
const mip_levels_to_gen = 1 + @as(
|
||||
u32,
|
||||
@intFromFloat(@log2(@as(f32, @floatFromInt(@max(width, height))))),
|
||||
@intFromFloat(@log2(float(@max(width, height)))),
|
||||
);
|
||||
var actual_mip_count: usize = 1;
|
||||
|
||||
@ -972,10 +972,10 @@ fn premultiplyAlpha(img: []u8) void {
|
||||
for (0..img.len / 4) |i| {
|
||||
const pixel = img[i * 4 .. i * 4 + 4];
|
||||
|
||||
const r = @as(f32, @floatFromInt(pixel[0])) / 255.0;
|
||||
const g = @as(f32, @floatFromInt(pixel[1])) / 255.0;
|
||||
const b = @as(f32, @floatFromInt(pixel[2])) / 255.0;
|
||||
const a = @as(f32, @floatFromInt(pixel[3])) / 255.0;
|
||||
const r = float(pixel[0]) / 255.0;
|
||||
const g = float(pixel[1]) / 255.0;
|
||||
const b = float(pixel[2]) / 255.0;
|
||||
const a = float(pixel[3]) / 255.0;
|
||||
|
||||
pixel[0] = @intFromFloat(r * 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);
|
||||
|
||||
return @Vector(2, f32){
|
||||
@as(f32, @floatFromInt(pixel[0])),
|
||||
@as(f32, @floatFromInt(pixel[1])),
|
||||
float(pixel[0]),
|
||||
float(pixel[1]),
|
||||
} / @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);
|
||||
|
||||
return @Vector(4, f32){
|
||||
@as(f32, @floatFromInt(pixel[0])),
|
||||
@as(f32, @floatFromInt(pixel[1])),
|
||||
@as(f32, @floatFromInt(pixel[2])),
|
||||
@as(f32, @floatFromInt(pixel[3])),
|
||||
float(pixel[0]),
|
||||
float(pixel[1]),
|
||||
float(pixel[2]),
|
||||
float(pixel[3]),
|
||||
} / @as(@Vector(4, f32), @splat(255.0));
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user