Move all chunk data into single SSBO.

Right now this doesn't change much, but in the future this will be helpful when(if?) I get started with multidraw and putting stuff like frustum culling onto the gpu.
This commit is contained in:
IntegratedQuantum 2022-11-26 20:45:47 +01:00
parent a777449a7c
commit 0d6303f7de
3 changed files with 137 additions and 18 deletions

View File

@ -28,7 +28,6 @@ struct FaceData {
}; };
layout(std430, binding = 3) buffer _faceData layout(std430, binding = 3) buffer _faceData
{ {
int voxelSize;
FaceData faceData[]; FaceData faceData[];
}; };
@ -46,6 +45,7 @@ layout(std430, binding = 4) buffer _voxelModels
}; };
uniform int time; uniform int time;
uniform int voxelSize;
const vec3[6] normals = vec3[6]( const vec3[6] normals = vec3[6](
vec3(0, 1, 0), vec3(0, 1, 0),

View File

@ -393,10 +393,12 @@ pub const meshing = struct {
@"waterFog.density": c_int, @"waterFog.density": c_int,
time: c_int, time: c_int,
visibilityMask: c_int, visibilityMask: c_int,
voxelSize: c_int,
} = undefined; } = undefined;
var vao: c_uint = undefined; var vao: c_uint = undefined;
var vbo: c_uint = undefined; var vbo: c_uint = undefined;
var faces: std.ArrayList(u32) = undefined; var faces: std.ArrayList(u32) = undefined;
var faceData: graphics.LargeBuffer = undefined;
pub fn init() !void { pub fn init() !void {
shader = try Shader.create("assets/cubyz/shaders/chunks/chunk_vertex.vs", "assets/cubyz/shaders/chunks/chunk_fragment.fs"); shader = try Shader.create("assets/cubyz/shaders/chunks/chunk_vertex.vs", "assets/cubyz/shaders/chunks/chunk_fragment.fs");
@ -417,6 +419,7 @@ pub const meshing = struct {
c.glBindVertexArray(0); c.glBindVertexArray(0);
faces = try std.ArrayList(u32).initCapacity(std.heap.page_allocator, 65536); faces = try std.ArrayList(u32).initCapacity(std.heap.page_allocator, 65536);
try faceData.init(main.globalAllocator, 64 << 20, 3);
} }
pub fn deinit() void { pub fn deinit() void {
@ -424,6 +427,7 @@ pub const meshing = struct {
c.glDeleteVertexArrays(1, &vao); c.glDeleteVertexArrays(1, &vao);
c.glDeleteBuffers(1, &vbo); c.glDeleteBuffers(1, &vbo);
faces.deinit(); faces.deinit();
faceData.deinit();
} }
pub fn bindShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, time: u32) void { pub fn bindShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, time: u32) void {
@ -452,7 +456,7 @@ pub const meshing = struct {
size: ChunkCoordinate, size: ChunkCoordinate,
chunk: std.atomic.Atomic(?*Chunk), chunk: std.atomic.Atomic(?*Chunk),
faces: std.ArrayList(u32), faces: std.ArrayList(u32),
faceData: SSBO, bufferAllocation: graphics.LargeBuffer.Allocation = .{.start = 0, .len = 0},
coreCount: u31 = 0, coreCount: u31 = 0,
neighborStart: [7]u31 = [_]u31{0} ** 7, neighborStart: [7]u31 = [_]u31{0} ** 7,
vertexCount: u31 = 0, vertexCount: u31 = 0,
@ -466,12 +470,11 @@ pub const meshing = struct {
.size = chunkSize*pos.voxelSize, .size = chunkSize*pos.voxelSize,
.faces = std.ArrayList(u32).init(allocator), .faces = std.ArrayList(u32).init(allocator),
.chunk = std.atomic.Atomic(?*Chunk).init(null), .chunk = std.atomic.Atomic(?*Chunk).init(null),
.faceData = SSBO.init(),
}; };
} }
pub fn deinit(self: *ChunkMesh) void { pub fn deinit(self: *ChunkMesh) void {
self.faceData.deinit(); faceData.free(self.bufferAllocation) catch unreachable;
self.faces.deinit(); self.faces.deinit();
if(self.chunk.load(.Monotonic)) |ch| { if(self.chunk.load(.Monotonic)) |ch| {
renderer.RenderStructure.allocator.destroy(ch); renderer.RenderStructure.allocator.destroy(ch);
@ -508,13 +511,13 @@ pub const meshing = struct {
or other.typ == 0 or other.typ == 0
or false // TODO: Blocks.mode(other).checkTransparency(other, neighbor) // TODO: make blocks.meshes.modelIndices(other) != 0 more strict to avoid overdraw. or false // TODO: Blocks.mode(other).checkTransparency(other, neighbor) // TODO: make blocks.meshes.modelIndices(other) != 0 more strict to avoid overdraw.
or (!std.meta.eql(block, other) and other.viewThrough()) or (!std.meta.eql(block, other) and other.viewThrough())
or blocks.meshes.modelIndices(other) != 0); or blocks.meshes.modelIndices(other) != 0
);
} }
pub fn regenerateMainMesh(self: *ChunkMesh, chunk: *Chunk) !void { pub fn regenerateMainMesh(self: *ChunkMesh, chunk: *Chunk) !void {
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function. std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
self.faces.clearRetainingCapacity(); self.faces.clearRetainingCapacity();
try self.faces.append(chunk.pos.voxelSize);
var n: u32 = 0; var n: u32 = 0;
var x: u8 = 0; var x: u8 = 0;
while(x < chunkSize): (x += 1) { while(x < chunkSize): (x += 1) {
@ -582,7 +585,7 @@ pub const meshing = struct {
start.* -= 2; start.* -= 2;
} }
} else { } else {
searchStart = 1; searchStart = 0;
searchEnd = self.coreCount; searchEnd = self.coreCount;
self.coreCount -= 2; self.coreCount -= 2;
for(self.neighborStart) |*start| { for(self.neighborStart) |*start| {
@ -606,7 +609,7 @@ pub const meshing = struct {
if(fromNeighborChunk) |neighbor| { if(fromNeighborChunk) |neighbor| {
searchRange = self.faces.items[self.neighborStart[neighbor]..self.neighborStart[neighbor+1]]; searchRange = self.faces.items[self.neighborStart[neighbor]..self.neighborStart[neighbor+1]];
} else { } else {
searchRange = self.faces.items[1..self.coreCount]; searchRange = self.faces.items[0..self.coreCount];
} }
var i: u32 = 0; var i: u32 = 0;
while(i < searchRange.len): (i += 2) { while(i < searchRange.len): (i += 2) {
@ -699,15 +702,16 @@ pub const meshing = struct {
} }
} }
} }
if(neighborMesh != self) neighborMesh.uploadData(); if(neighborMesh != self) try neighborMesh.uploadData();
} }
self.chunk.load(.Monotonic).?.blocks[getIndex(x, y, z)] = newBlock; self.chunk.load(.Monotonic).?.blocks[getIndex(x, y, z)] = newBlock;
self.uploadData(); try self.uploadData();
} }
fn uploadData(self: *ChunkMesh) void { fn uploadData(self: *ChunkMesh) !void {
self.vertexCount = @intCast(u31, 6*(self.faces.items.len-1)/2); self.vertexCount = @intCast(u31, 6*self.faces.items.len/2);
self.faceData.bufferData(u32, self.faces.items); try faceData.realloc(&self.bufferAllocation, @intCast(u31, 4*self.faces.items.len));
faceData.bufferSubData(self.bufferAllocation.start, u32, self.faces.items);
} }
pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void { pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void {
@ -772,8 +776,7 @@ pub const meshing = struct {
for(neighborMesh.neighborStart[1+(neighbor ^ 1)..]) |*neighborStart| { for(neighborMesh.neighborStart[1+(neighbor ^ 1)..]) |*neighborStart| {
neighborStart.* = neighborStart.* - (rangeEnd - rangeStart) + @intCast(u31, additionalNeighborFaces.items.len); neighborStart.* = neighborStart.* - (rangeEnd - rangeStart) + @intCast(u31, additionalNeighborFaces.items.len);
} }
neighborMesh.vertexCount = @intCast(u31, 6*(neighborMesh.faces.items.len-1)/2); try neighborMesh.uploadData();
neighborMesh.faceData.bufferData(u32, neighborMesh.faces.items);
continue; continue;
} }
} }
@ -826,7 +829,7 @@ pub const meshing = struct {
} }
} }
self.neighborStart[6] = @intCast(u31, self.faces.items.len); self.neighborStart[6] = @intCast(u31, self.faces.items.len);
self.uploadData(); try self.uploadData();
self.generated = true; self.generated = true;
} }
@ -842,8 +845,8 @@ pub const meshing = struct {
@floatCast(f32, @intToFloat(f64, self.pos.wz) - playerPosition[2]) @floatCast(f32, @intToFloat(f64, self.pos.wz) - playerPosition[2])
); );
c.glUniform1i(uniforms.visibilityMask, self.visibilityMask); c.glUniform1i(uniforms.visibilityMask, self.visibilityMask);
self.faceData.bind(3); c.glUniform1i(uniforms.voxelSize, self.pos.voxelSize);
c.glDrawElements(c.GL_TRIANGLES, self.vertexCount, c.GL_UNSIGNED_INT, null); c.glDrawElementsBaseVertex(c.GL_TRIANGLES, self.vertexCount, c.GL_UNSIGNED_INT, null, self.bufferAllocation.start/8*4);
} }
}; };
}; };

View File

@ -423,6 +423,122 @@ pub const SSBO = struct {
c.glBufferData(c.GL_SHADER_STORAGE_BUFFER, @intCast(c_long, data.len*@sizeOf(T)), data.ptr, c.GL_STATIC_DRAW); c.glBufferData(c.GL_SHADER_STORAGE_BUFFER, @intCast(c_long, data.len*@sizeOf(T)), data.ptr, c.GL_STATIC_DRAW);
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, 0); c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, 0);
} }
pub fn createDynamicBuffer(self: SSBO, size: usize) void {
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, self.bufferID);
c.glBufferData(c.GL_SHADER_STORAGE_BUFFER, @intCast(c_long, size), null, c.GL_DYNAMIC_DRAW);
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, 0);
}
};
/// A big SSBO that is able to allocate/free smaller regions.
pub const LargeBuffer = struct {
pub const Allocation = struct {
start: u31,
len: u31,
};
ssbo: SSBO,
freeBlocks: std.ArrayList(Allocation),
pub fn init(self: *LargeBuffer, allocator: Allocator, size: u31, binding: c_uint) !void {
self.ssbo = SSBO.init();
self.ssbo.createDynamicBuffer(size);
self.ssbo.bind(binding);
self.freeBlocks = std.ArrayList(Allocation).init(allocator);
try self.freeBlocks.append(.{.start = 0, .len = size});
}
pub fn deinit(self: *LargeBuffer) void {
self.ssbo.deinit();
self.freeBlocks.deinit();
}
fn alloc(self: *LargeBuffer, size: u31) !Allocation {
var smallestBlock: ?*Allocation = null;
for(self.freeBlocks.items) |*block, i| {
if(size == block.len) {
return self.freeBlocks.swapRemove(i);
}
if(size < block.len and if(smallestBlock) |_smallestBlock| block.len > _smallestBlock.len else true) {
smallestBlock = block;
}
}
if(smallestBlock) |block| {
const result = Allocation {.start = block.start, .len = size};
block.start += size;
block.len -= size;
return result;
} else return error.OutOfMemory; // TODO: Increase the buffer size.
}
pub fn free(self: *LargeBuffer, _allocation: Allocation) !void {
var allocation = _allocation;
if(allocation.len == 0) return;
for(self.freeBlocks.items) |*block, i| {
if(allocation.start + allocation.len == block.start) {
allocation.len += block.len;
_ = self.freeBlocks.swapRemove(i);
break;
}
}
for(self.freeBlocks.items) |*block| {
if(allocation.start == block.start + block.len) {
block.len += allocation.len;
return;
}
}
try self.freeBlocks.append(allocation);
}
pub fn realloc(self: *LargeBuffer, allocation: *Allocation, newSize: u31) !void {
if(allocation.len == 0) allocation.* = try self.alloc(newSize);
if(newSize == allocation.len) return;
if(newSize < allocation.len) {
const diff = allocation.len - newSize;
// Check if there is a free block directly after:
for(self.freeBlocks.items) |*block| {
if(allocation.start + allocation.len == block.start and block.len + allocation.len >= newSize) {
block.start -= diff;
block.len += diff;
allocation.len -= diff;
return;
}
}
// Create a new free block:
allocation.len -= diff;
try self.freeBlocks.append(.{.start = allocation.start + allocation.len, .len = diff});
} else {
// Check if the buffer can be extended without a problem:
for(self.freeBlocks.items) |*block, i| {
if(allocation.start + allocation.len == block.start and block.len + allocation.len >= newSize) {
const diff = newSize - allocation.len;
allocation.len += diff;
if(block.len != diff) {
block.start += diff;
block.len -= diff;
} else {
_ = self.freeBlocks.swapRemove(i);
}
return;
}
}
const oldAllocation = allocation.*;
allocation.* = try self.alloc(newSize);
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, self.ssbo.bufferID);
c.glCopyBufferSubData(c.GL_SHADER_STORAGE_BUFFER, c.GL_SHADER_STORAGE_BUFFER, oldAllocation.start, allocation.start, oldAllocation.len);
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, 0);
try self.free(oldAllocation);
}
}
pub fn bufferSubData(self: *LargeBuffer, offset: u31, comptime T: type, data: []T) void {
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, self.ssbo.bufferID);
c.glBufferSubData(c.GL_SHADER_STORAGE_BUFFER, offset, @sizeOf(T)*@intCast(c_long, data.len), data.ptr);
c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, 0);
}
}; };
pub const FrameBuffer = struct { pub const FrameBuffer = struct {