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
{
int voxelSize;
FaceData faceData[];
};
@ -46,6 +45,7 @@ layout(std430, binding = 4) buffer _voxelModels
};
uniform int time;
uniform int voxelSize;
const vec3[6] normals = vec3[6](
vec3(0, 1, 0),

View File

@ -393,10 +393,12 @@ pub const meshing = struct {
@"waterFog.density": c_int,
time: c_int,
visibilityMask: c_int,
voxelSize: c_int,
} = undefined;
var vao: c_uint = undefined;
var vbo: c_uint = undefined;
var faces: std.ArrayList(u32) = undefined;
var faceData: graphics.LargeBuffer = undefined;
pub fn init() !void {
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);
faces = try std.ArrayList(u32).initCapacity(std.heap.page_allocator, 65536);
try faceData.init(main.globalAllocator, 64 << 20, 3);
}
pub fn deinit() void {
@ -424,6 +427,7 @@ pub const meshing = struct {
c.glDeleteVertexArrays(1, &vao);
c.glDeleteBuffers(1, &vbo);
faces.deinit();
faceData.deinit();
}
pub fn bindShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, time: u32) void {
@ -452,7 +456,7 @@ pub const meshing = struct {
size: ChunkCoordinate,
chunk: std.atomic.Atomic(?*Chunk),
faces: std.ArrayList(u32),
faceData: SSBO,
bufferAllocation: graphics.LargeBuffer.Allocation = .{.start = 0, .len = 0},
coreCount: u31 = 0,
neighborStart: [7]u31 = [_]u31{0} ** 7,
vertexCount: u31 = 0,
@ -466,12 +470,11 @@ pub const meshing = struct {
.size = chunkSize*pos.voxelSize,
.faces = std.ArrayList(u32).init(allocator),
.chunk = std.atomic.Atomic(?*Chunk).init(null),
.faceData = SSBO.init(),
};
}
pub fn deinit(self: *ChunkMesh) void {
self.faceData.deinit();
faceData.free(self.bufferAllocation) catch unreachable;
self.faces.deinit();
if(self.chunk.load(.Monotonic)) |ch| {
renderer.RenderStructure.allocator.destroy(ch);
@ -508,13 +511,13 @@ pub const meshing = struct {
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 (!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 {
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
self.faces.clearRetainingCapacity();
try self.faces.append(chunk.pos.voxelSize);
var n: u32 = 0;
var x: u8 = 0;
while(x < chunkSize): (x += 1) {
@ -582,7 +585,7 @@ pub const meshing = struct {
start.* -= 2;
}
} else {
searchStart = 1;
searchStart = 0;
searchEnd = self.coreCount;
self.coreCount -= 2;
for(self.neighborStart) |*start| {
@ -606,7 +609,7 @@ pub const meshing = struct {
if(fromNeighborChunk) |neighbor| {
searchRange = self.faces.items[self.neighborStart[neighbor]..self.neighborStart[neighbor+1]];
} else {
searchRange = self.faces.items[1..self.coreCount];
searchRange = self.faces.items[0..self.coreCount];
}
var i: u32 = 0;
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.uploadData();
try self.uploadData();
}
fn uploadData(self: *ChunkMesh) void {
self.vertexCount = @intCast(u31, 6*(self.faces.items.len-1)/2);
self.faceData.bufferData(u32, self.faces.items);
fn uploadData(self: *ChunkMesh) !void {
self.vertexCount = @intCast(u31, 6*self.faces.items.len/2);
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 {
@ -772,8 +776,7 @@ pub const meshing = struct {
for(neighborMesh.neighborStart[1+(neighbor ^ 1)..]) |*neighborStart| {
neighborStart.* = neighborStart.* - (rangeEnd - rangeStart) + @intCast(u31, additionalNeighborFaces.items.len);
}
neighborMesh.vertexCount = @intCast(u31, 6*(neighborMesh.faces.items.len-1)/2);
neighborMesh.faceData.bufferData(u32, neighborMesh.faces.items);
try neighborMesh.uploadData();
continue;
}
}
@ -826,7 +829,7 @@ pub const meshing = struct {
}
}
self.neighborStart[6] = @intCast(u31, self.faces.items.len);
self.uploadData();
try self.uploadData();
self.generated = true;
}
@ -842,8 +845,8 @@ pub const meshing = struct {
@floatCast(f32, @intToFloat(f64, self.pos.wz) - playerPosition[2])
);
c.glUniform1i(uniforms.visibilityMask, self.visibilityMask);
self.faceData.bind(3);
c.glDrawElements(c.GL_TRIANGLES, self.vertexCount, c.GL_UNSIGNED_INT, null);
c.glUniform1i(uniforms.voxelSize, self.pos.voxelSize);
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.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 {