feat: parallelise chunkgen, model generation and uploading
This commit is contained in:
parent
dd41aabf0f
commit
c029f40bc7
6 changed files with 150 additions and 46 deletions
|
|
@ -8,6 +8,8 @@ pub fn build(b: *std.Build) void {
|
|||
const rl = b.dependency("raylib", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
//.config = "-DSUPPORT_CUSTOM_FRAME_CONTROL",
|
||||
.config = @as([]const u8, "-DSUPPORT_CUSTOM_FRAME_CONTROL"),
|
||||
});
|
||||
break :raylib rl;
|
||||
};
|
||||
|
|
|
|||
2
raylib
2
raylib
|
|
@ -1 +1 @@
|
|||
Subproject commit 77e626060d3c5419c539edfa4fe4f268f9015ade
|
||||
Subproject commit f740d0941f1bf73077bb369c161bc673e27b735a
|
||||
127
src/main.zig
127
src/main.zig
|
|
@ -14,6 +14,43 @@ const TILE_TEXTURE_RESOLUTION = 16;
|
|||
|
||||
const debug = true;
|
||||
|
||||
const FPS_HISTORY_LENGTH = 30;
|
||||
var time: struct {
|
||||
current: f64 = 0.0,
|
||||
previous: f64 = 0.0,
|
||||
target: f64 = 1.0/165.0,
|
||||
frameCounter: u64 = 0,
|
||||
fpsHistory: [FPS_HISTORY_LENGTH]f64 = .{0.0} ** FPS_HISTORY_LENGTH,
|
||||
|
||||
pub fn deltaTime(self: @This()) f64 {
|
||||
return self.current - self.previous;
|
||||
}
|
||||
|
||||
pub fn fps(self: *@This()) f64 {
|
||||
self.fpsHistory[self.frameCounter % FPS_HISTORY_LENGTH] = 1/(self.current - self.previous);
|
||||
if(self.frameCounter < FPS_HISTORY_LENGTH) return 0;
|
||||
|
||||
var result: f64 = 0.0;
|
||||
for(self.fpsHistory) |entry| {
|
||||
result += entry;
|
||||
}
|
||||
result /= FPS_HISTORY_LENGTH;
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn update(self: *@This()) void {
|
||||
self.previous = self.current;
|
||||
self.current = raylib.GetTime();
|
||||
|
||||
if (self.deltaTime() < self.target){
|
||||
raylib.WaitTime(self.target - self.deltaTime());
|
||||
self.current = raylib.GetTime();
|
||||
}
|
||||
|
||||
self.frameCounter += 1;
|
||||
}
|
||||
} = .{};
|
||||
|
||||
pub fn drawCameraPosition(camera: raylib.Camera3D, x: i32, y: i32) !void {
|
||||
var buf: [256:0]u8 = undefined;
|
||||
|
||||
|
|
@ -25,6 +62,17 @@ pub fn drawCameraPosition(camera: raylib.Camera3D, x: i32, y: i32) !void {
|
|||
|
||||
raylib.DrawText(slice, x, y, 20, raylib.YELLOW);
|
||||
}
|
||||
pub fn drawFmtText(comptime fmt: []const u8, args: anytype, x: i32, y: i32) !void {
|
||||
var buf: [256:0]u8 = undefined;
|
||||
|
||||
const slice = try std.fmt.bufPrintZ(
|
||||
&buf,
|
||||
fmt,
|
||||
args,
|
||||
);
|
||||
|
||||
raylib.DrawText(slice, x, y, 20, raylib.BLUE);
|
||||
}
|
||||
|
||||
pub fn moveCamera(camera: *raylib.Camera3D, vec: raylib.Vector3) void {
|
||||
camera.position = v3.add(camera.position, vec);
|
||||
|
|
@ -34,17 +82,13 @@ pub fn moveCamera(camera: *raylib.Camera3D, vec: raylib.Vector3) void {
|
|||
pub fn main() !void {
|
||||
if (!debug) raylib.SetTraceLogLevel(raylib.LOG_ERROR);
|
||||
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{.thread_safe = true}){};
|
||||
const allocator = gpa.allocator();
|
||||
defer {
|
||||
const status = gpa.deinit();
|
||||
if (status == .leak) std.debug.print("MEMORY LEAK DETECTED!!!!!!!!!!!!!!!!!!!!!!\n", .{}) else std.debug.print("no leaks detected.\n", .{});
|
||||
}
|
||||
|
||||
var thread_pool: std.Thread.Pool = undefined;
|
||||
try thread_pool.init(.{.allocator = allocator});
|
||||
defer thread_pool.deinit();
|
||||
|
||||
raylib.SetConfigFlags(raylib.FLAG_WINDOW_RESIZABLE | raylib.FLAG_FULLSCREEN_MODE);
|
||||
|
||||
const display = raylib.GetCurrentMonitor();
|
||||
|
|
@ -79,21 +123,26 @@ pub fn main() !void {
|
|||
defer raylib.UnloadShader(shader);
|
||||
raylib.SetShaderValue(shader, raylib.GetShaderLocation(shader, "textureTiling"), &.{ @as(f32, @floatFromInt(tile_columns)), @as(f32, @floatFromInt(tile_rows)) }, raylib.SHADER_UNIFORM_VEC2);
|
||||
|
||||
var world_state: WorldState = WorldState.init(thread_pool, allocator);
|
||||
var thread_pool: std.Thread.Pool = undefined;
|
||||
try thread_pool.init(.{.allocator = allocator});
|
||||
defer thread_pool.deinit();
|
||||
|
||||
var global_graphics_mutex: std.Thread.Mutex = undefined;
|
||||
|
||||
var world_state: WorldState = WorldState.init(&thread_pool, allocator);
|
||||
defer world_state.deinit();
|
||||
|
||||
for (0..10) |x| for (0..10) |z| {
|
||||
_ = try world_state.generateChunk(.{@intCast(x), 0, @intCast(z)});
|
||||
_ = try world_state.generateChunkModel(.{@intCast(x), 0, @intCast(z)}, tile_rows, tile_columns, texture, ambient_occlusion_texture, shader);
|
||||
for (0..50) |x| for (0..50) |z| {
|
||||
try world_state.queueLoadChunk(.{@intCast(x), 0, @intCast(z)}, tile_rows, tile_columns, texture, ambient_occlusion_texture, shader, &global_graphics_mutex);
|
||||
};
|
||||
|
||||
while (!raylib.WindowShouldClose()) {
|
||||
raylib.ClearBackground(raylib.BLACK);
|
||||
raylib.PollInputEvents();
|
||||
|
||||
const right = v3.neg(v3.nor(v3.cross(camera.up, v3.sub(camera.target, camera.position))));
|
||||
const forward = v3.cross(right, v3.neg(camera.up));
|
||||
|
||||
const speed = @as(f32, if (raylib.IsKeyDown(raylib.KEY_LEFT_CONTROL)) 25 else 5) * raylib.GetFrameTime();
|
||||
const speed = @as(f32, if (raylib.IsKeyDown(raylib.KEY_LEFT_CONTROL)) 25 else 5) * time.deltaTime();
|
||||
var movement = v3.new(0, 0, 0);
|
||||
|
||||
if (raylib.IsKeyDown(raylib.KEY_SPACE)) movement.y += 1;
|
||||
|
|
@ -104,7 +153,7 @@ pub fn main() !void {
|
|||
if (raylib.IsKeyDown(raylib.KEY_D)) movement = v3.add(movement, right);
|
||||
if (raylib.IsKeyDown(raylib.KEY_A)) movement = v3.sub(movement, right);
|
||||
|
||||
moveCamera(&camera, v3.scl(v3.nor(movement), speed));
|
||||
moveCamera(&camera, v3.scl(v3.nor(movement), @floatCast(speed)));
|
||||
|
||||
const delta = raylib.GetMouseDelta();
|
||||
// on the first mouse movement, for some reason mouse delta is way too large, so we just ignore too large deltas
|
||||
|
|
@ -120,26 +169,48 @@ pub fn main() !void {
|
|||
}
|
||||
}
|
||||
|
||||
raylib.BeginDrawing();
|
||||
defer raylib.EndDrawing();
|
||||
|
||||
{
|
||||
raylib.BeginMode3D(camera);
|
||||
defer raylib.EndMode3D();
|
||||
global_graphics_mutex.lock();
|
||||
defer global_graphics_mutex.unlock();
|
||||
raylib.MakeContextCurrent();
|
||||
defer raylib.DropContextCurrent();
|
||||
|
||||
world_state.chunks_access_mutex.lock();
|
||||
var chunks_iterator = world_state.chunks.valueIterator();
|
||||
while (chunks_iterator.next()) |entry| {
|
||||
if (entry.*.model) |model| {
|
||||
const model_position = v3.new(@floatFromInt(entry.position[0]*16), @floatFromInt(entry.position[1]*16), @floatFromInt(entry.position[2]*16));
|
||||
raylib.DrawChunkModel(model, model_position, 0.5, raylib.WHITE);
|
||||
raylib.ClearBackground(raylib.BLACK);
|
||||
|
||||
{
|
||||
raylib.BeginDrawing();
|
||||
defer raylib.EndDrawing();
|
||||
|
||||
var chunk_count: u32 = 0;
|
||||
var shown_count: u32 = 0;
|
||||
|
||||
{
|
||||
raylib.BeginMode3D(camera);
|
||||
defer raylib.EndMode3D();
|
||||
|
||||
world_state.chunks_access_mutex.lock();
|
||||
defer world_state.chunks_access_mutex.unlock();
|
||||
|
||||
var chunks_iterator = world_state.chunks.valueIterator();
|
||||
while (chunks_iterator.next()) |entry_ptr| {
|
||||
const entry = entry_ptr.*;
|
||||
if (entry.*.model) |model| {
|
||||
const model_position = v3.new(@floatFromInt(entry.position[0]*16), @floatFromInt(entry.position[1]*16), @floatFromInt(entry.position[2]*16));
|
||||
raylib.DrawChunkModel(model, model_position, 0.5, raylib.WHITE);
|
||||
shown_count += 1;
|
||||
}
|
||||
chunk_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
world_state.chunks_access_mutex.unlock();
|
||||
}
|
||||
|
||||
raylib.DrawFPS(10, 10);
|
||||
try drawCameraPosition(camera, 10, 30);
|
||||
try drawFmtText("fps: {d:.0}", .{time.fps()}, 10, 10);
|
||||
try drawCameraPosition(camera, 10, 30);
|
||||
try drawFmtText("chunks shown: {}, chunks total: {}", .{shown_count, chunk_count}, 10, 50);
|
||||
}
|
||||
|
||||
raylib.SwapScreenBuffer();
|
||||
}
|
||||
time.update();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -387,10 +387,7 @@ pub const Chunk = struct {
|
|||
var raw_quads = try scanForRawQuads(chunk);
|
||||
defer raw_quads.deinit();
|
||||
|
||||
var mesh = packMeshFromRawQuads(raw_quads, tile_columns, tile_rows);
|
||||
|
||||
raylib.UploadChunkMesh(@ptrCast(&mesh), false);
|
||||
|
||||
const mesh = packMeshFromRawQuads(raw_quads, tile_columns, tile_rows);
|
||||
return mesh;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ const std = @import("std");
|
|||
const Thread = std.Thread;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const AutoHashMap = std.AutoHashMap;
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
const Chunk = @import("chunk.zig").Chunk;
|
||||
const chunk_generators = @import("chunk_generators.zig");
|
||||
|
|
@ -10,28 +11,29 @@ const raylib_helper = @import("../lib_helpers/raylib_helper.zig");
|
|||
const raylib = raylib_helper.raylib;
|
||||
|
||||
pub const WorldStateError = error{
|
||||
ChunkNotGeneratedError,
|
||||
ChunkNotGeneratedError,
|
||||
};
|
||||
|
||||
pub const ChunkEntry = struct {
|
||||
mutex: Thread.Mutex = .{},
|
||||
chunk: ?Chunk = null,
|
||||
model: ?raylib.ChunkModel = null,
|
||||
position: @Vector(3, i64),
|
||||
};
|
||||
|
||||
pub const WorldState = struct {
|
||||
pool: Thread.Pool = undefined,
|
||||
pool: *Thread.Pool = undefined,
|
||||
allocator: Allocator = undefined,
|
||||
|
||||
// TODO: we can do better than a hashmap and a mutex
|
||||
chunks: AutoHashMap(@Vector(3, i64), ChunkEntry) = undefined,
|
||||
chunks: AutoHashMap(@Vector(3, i64), *ChunkEntry) = undefined,
|
||||
chunks_access_mutex: Thread.Mutex = .{},
|
||||
|
||||
pub fn init(global_pool: Thread.Pool, allocator: Allocator) WorldState {
|
||||
pub fn init(global_pool: *Thread.Pool, allocator: Allocator) WorldState {
|
||||
var world_state: WorldState = undefined;
|
||||
world_state.pool = global_pool;
|
||||
world_state.allocator = allocator;
|
||||
world_state.chunks = AutoHashMap(@Vector(3, i64), ChunkEntry).init(allocator);
|
||||
world_state.chunks = AutoHashMap(@Vector(3, i64), *ChunkEntry).init(allocator);
|
||||
return world_state;
|
||||
}
|
||||
|
||||
|
|
@ -44,33 +46,65 @@ pub const WorldState = struct {
|
|||
if(entry.*.model) |model| {
|
||||
raylib.UnloadChunkModel(model);
|
||||
}
|
||||
self.allocator.destroy(entry.*);
|
||||
}
|
||||
self.chunks.deinit();
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn queueLoadChunk(self: *WorldState, pos: @Vector(3, i64), tile_rows: u32, tile_columns: u32, texture: raylib.Texture, ambient_occlusion_texture: raylib.Texture, shader: raylib.Shader, global_graphics_mutex: *Thread.Mutex) !void {
|
||||
try self.pool.spawn(completeChunk, .{self, pos, tile_rows, tile_columns, texture, ambient_occlusion_texture, shader, global_graphics_mutex});
|
||||
}
|
||||
|
||||
pub fn completeChunk(self: *WorldState, pos: @Vector(3, i64), tile_rows: u32, tile_columns: u32, texture: raylib.Texture, ambient_occlusion_texture: raylib.Texture, shader: raylib.Shader, global_graphics_mutex: *Thread.Mutex) void {
|
||||
_ = generateChunk(self, pos) catch |err| std.debug.print("error while generating chunk: {}", .{err});
|
||||
_ = generateChunkModel(self, pos, tile_rows, tile_columns, texture, ambient_occlusion_texture, shader, global_graphics_mutex) catch |err| std.debug.print("error while generating chunk model: {}", .{err});
|
||||
}
|
||||
|
||||
pub fn generateChunk(self: *WorldState, pos: @Vector(3, i64)) !Chunk {
|
||||
const chunk = try chunk_generators.createChunk(self.allocator, pos);
|
||||
const entry_ptr = try self.allocator.create(ChunkEntry);
|
||||
entry_ptr.* = .{.chunk = chunk, .position = pos};
|
||||
self.chunks_access_mutex.lock();
|
||||
try self.chunks.put(pos, .{.chunk = chunk, .position = pos});
|
||||
try self.chunks.put(pos, entry_ptr);
|
||||
self.chunks_access_mutex.unlock();
|
||||
return chunk;
|
||||
}
|
||||
|
||||
pub fn generateChunkModel(self: *WorldState, pos: @Vector(3, i64), tile_rows: u32, tile_columns: u32, texture: raylib.Texture, ambient_occlusion_texture: raylib.Texture, shader: raylib.Shader) !raylib.ChunkModel {
|
||||
pub fn generateChunkModel(self: *WorldState, pos: @Vector(3, i64), tile_rows: u32, tile_columns: u32, texture: raylib.Texture, ambient_occlusion_texture: raylib.Texture, shader: raylib.Shader, global_graphics_mutex: *Thread.Mutex) !raylib.ChunkModel {
|
||||
self.chunks_access_mutex.lock();
|
||||
const chunk_entry = (try self.chunks.getOrPutValue(pos, .{.position = pos})).value_ptr;
|
||||
const chunk_entry = blk: {
|
||||
const result = try self.chunks.getOrPut(pos);
|
||||
if(!result.found_existing) {
|
||||
const entry_ptr = try self.allocator.create(ChunkEntry);
|
||||
entry_ptr.* = .{.position = pos};
|
||||
result.value_ptr.* = entry_ptr;
|
||||
}
|
||||
break :blk result.value_ptr.*;
|
||||
};
|
||||
self.chunks_access_mutex.unlock();
|
||||
|
||||
const chunk = chunk_entry.chunk;
|
||||
if(chunk == null) return WorldStateError.ChunkNotGeneratedError;
|
||||
|
||||
const model = raylib.LoadChunkModelFromMesh(try chunk.?.createMesh(tile_rows, tile_columns));
|
||||
model.materials[0].maps[raylib.MATERIAL_MAP_DIFFUSE].texture = texture;
|
||||
model.materials[0].shader = shader;
|
||||
var mesh = try chunk.?.createMesh(tile_rows, tile_columns);
|
||||
const model = blk: {
|
||||
global_graphics_mutex.lock();
|
||||
defer global_graphics_mutex.unlock();
|
||||
raylib.MakeContextCurrent();
|
||||
defer raylib.DropContextCurrent();
|
||||
|
||||
model.materials[0].shader.locs[raylib.SHADER_LOC_MAP_DIFFUSE+1] = raylib.GetShaderLocation(shader, "occlusionMap");
|
||||
raylib.SetShaderValueTexture(shader, model.materials[0].shader.locs[raylib.SHADER_LOC_MAP_DIFFUSE+1], ambient_occlusion_texture); model.materials[0].maps[raylib.MATERIAL_MAP_DIFFUSE+1].texture = ambient_occlusion_texture;
|
||||
raylib.UploadChunkMesh(@ptrCast(&mesh), false);
|
||||
|
||||
const model = raylib.LoadChunkModelFromMesh(mesh);
|
||||
model.materials[0].maps[raylib.MATERIAL_MAP_DIFFUSE].texture = texture;
|
||||
model.materials[0].shader = shader;
|
||||
|
||||
model.materials[0].shader.locs[raylib.SHADER_LOC_MAP_DIFFUSE+1] = raylib.GetShaderLocation(shader, "occlusionMap");
|
||||
raylib.SetShaderValueTexture(shader, model.materials[0].shader.locs[raylib.SHADER_LOC_MAP_DIFFUSE+1], ambient_occlusion_texture);
|
||||
model.materials[0].maps[raylib.MATERIAL_MAP_DIFFUSE+1].texture = ambient_occlusion_texture;
|
||||
break :blk model;
|
||||
};
|
||||
chunk_entry.model = model;
|
||||
return model;
|
||||
}
|
||||
|
|
|
|||
2
znoise
2
znoise
|
|
@ -1 +1 @@
|
|||
Subproject commit 96f9458c2da975a8bf1cdf95e819c7b070965198
|
||||
Subproject commit 76724581c99be0b2f6aa43eb8b63d6f27bada27e
|
||||
Loading…
Add table
Add a link
Reference in a new issue