diff --git a/modules/engine-graphics/src/vulkan/lpv_system.zig b/modules/engine-graphics/src/vulkan/lpv_system.zig index 09288d5c..e350d312 100644 --- a/modules/engine-graphics/src/vulkan/lpv_system.zig +++ b/modules/engine-graphics/src/vulkan/lpv_system.zig @@ -39,6 +39,7 @@ pub const LPVSystem = struct { propagation_factor: f32, center_retention: f32, enabled: bool, + resources_initialized: bool = false, update_interval_frames: u32 = 6, origin: Vec3 = Vec3.zero, @@ -102,12 +103,20 @@ pub const LPVSystem = struct { }, }; + if (enabled) try self.initResources(); + + return self; + } + + fn initResources(self: *LPVSystem) !void { + std.debug.assert(!self.resources_initialized); + try self.createGridTextures(); errdefer self.destroyGridTextures(); const light_buffer_size = MAX_LIGHTS_PER_UPDATE * @sizeOf(GpuLight); self.light_buffer = try Utils.createVulkanBuffer( - &vk_ctx.vulkan_device, + &self.vk_ctx.vulkan_device, light_buffer_size, c.VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, c.VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | c.VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, @@ -115,9 +124,9 @@ pub const LPVSystem = struct { errdefer self.destroyLightBuffer(); // Occlusion grid buffer: one u32 per cell (1 = opaque, 0 = transparent) - const occlusion_buffer_size = @as(usize, clamped_grid) * @as(usize, clamped_grid) * @as(usize, clamped_grid) * @sizeOf(u32); + const occlusion_buffer_size = @as(usize, self.grid_size) * @as(usize, self.grid_size) * @as(usize, self.grid_size) * @sizeOf(u32); self.occlusion_buffer = try Utils.createVulkanBuffer( - &vk_ctx.vulkan_device, + &self.vk_ctx.vulkan_device, occlusion_buffer_size, c.VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, c.VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | c.VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, @@ -130,19 +139,25 @@ pub const LPVSystem = struct { errdefer self.deinitComputeResources(); try self.initComputeResources(); - return self; + self.resources_initialized = true; } - pub fn deinit(self: *LPVSystem) void { + fn deinitResources(self: *LPVSystem) void { + if (!self.resources_initialized) return; + self.deinitComputeResources(); self.destroyOcclusionBuffer(); self.destroyLightBuffer(); self.destroyGridTextures(); + self.resources_initialized = false; + } + + pub fn deinit(self: *LPVSystem) void { + self.deinitResources(); self.allocator.destroy(self); } pub fn setSettings(self: *LPVSystem, enabled: bool, intensity: f32, cell_size: f32, propagation_iterations: u32, grid_size: u32, update_interval_frames: u32) !void { - self.enabled = enabled; self.intensity = std.math.clamp(intensity, 0.0, 4.0); self.cell_size = @max(cell_size, 0.5); self.propagation_iterations = std.math.clamp(propagation_iterations, 1, 8); @@ -151,6 +166,21 @@ pub const LPVSystem = struct { self.stats.update_interval_frames = self.update_interval_frames; const clamped_grid = std.math.clamp(grid_size, 16, 64); + if (!enabled) { + self.enabled = false; + self.deinitResources(); + self.stats.light_count = 0; + self.stats.cpu_update_ms = 0.0; + return; + } + + if (!self.resources_initialized) { + self.grid_size = clamped_grid; + self.stats.grid_size = clamped_grid; + try self.initResources(); + } + self.enabled = true; + if (clamped_grid == self.grid_size) return; const old_resources = GridResources{ @@ -229,6 +259,10 @@ pub const LPVSystem = struct { return self.cell_size; } + pub fn isEnabled(self: *const LPVSystem) bool { + return self.enabled and self.resources_initialized; + } + pub fn update(self: *LPVSystem, world: ILPVWorld, camera_pos: Vec3, debug_overlay_enabled: bool) !void { self.current_frame +%= 1; const timer_start = std.Io.Clock.awake.now(std.Options.debug_io); @@ -237,12 +271,7 @@ pub const LPVSystem = struct { self.stats.propagation_iterations = self.propagation_iterations; self.stats.update_interval_frames = self.update_interval_frames; - if (!self.enabled) { - self.active_grid_textures = self.grid_textures_a; - if (self.was_enabled_last_frame and debug_overlay_enabled) { - self.buildDebugOverlay(&.{}, 0); - try self.uploadDebugOverlay(); - } + if (!self.isEnabled()) { self.was_enabled_last_frame = false; self.debug_overlay_was_enabled = debug_overlay_enabled; self.stats.light_count = 0; @@ -550,6 +579,7 @@ pub const LPVSystem = struct { fn createGridTextures(self: *LPVSystem) !void { const resources = try self.createGridResources(self.grid_size); self.applyGridResources(resources); + errdefer self.destroyGridTextures(); self.buildDebugOverlay(&.{}, 0); try self.uploadDebugOverlay(); diff --git a/modules/game-core/src/settings/json_presets.zig b/modules/game-core/src/settings/json_presets.zig index cc7764a2..a73c36f3 100644 --- a/modules/game-core/src/settings/json_presets.zig +++ b/modules/game-core/src/settings/json_presets.zig @@ -34,7 +34,7 @@ pub const PresetConfig = struct { volumetric_scattering: f32, ssao_enabled: bool, lpv_quality_preset: u32 = 1, - lpv_enabled: bool = true, + lpv_enabled: bool = false, lpv_intensity: f32 = 0.5, lpv_cell_size: f32 = 2.0, lpv_grid_size: u32 = 32, diff --git a/modules/game-core/src/settings/tests.zig b/modules/game-core/src/settings/tests.zig index 0b707a31..f501f215 100644 --- a/modules/game-core/src/settings/tests.zig +++ b/modules/game-core/src/settings/tests.zig @@ -25,6 +25,7 @@ test "Preset Application" { try std.testing.expectEqual(@as(i32, 6), settings.render_distance); try std.testing.expectEqual(RenderDistancePreset.low, settings.render_distance_preset); try std.testing.expectEqual(true, settings.lod_enabled); + try std.testing.expectEqual(false, settings.lpv_enabled); presets.apply(&settings, 3); try std.testing.expectEqual(@as(u32, 3), settings.shadow_quality); diff --git a/modules/game-ui/src/screens/world.zig b/modules/game-ui/src/screens/world.zig index 868b4ad6..54f7c807 100644 --- a/modules/game-ui/src/screens/world.zig +++ b/modules/game-ui/src/screens/world.zig @@ -362,7 +362,7 @@ pub const WorldScreen = struct { lpv_quality.grid_size, lpv_quality.update_interval_frames, ); - if (!safe_mode and !startup_light_render) { + if (lpv_system.isEnabled()) { rhi.timing().beginPassTiming("LPVPass"); try lpv_system.update(world_render_view.lpvWorld(), camera.position, ctx.settings.debug_lpv_overlay_active); rhi.timing().endPassTiming("LPVPass"); @@ -392,6 +392,7 @@ pub const WorldScreen = struct { .lpv_cell_size = lpv_system.getCellSize(), .lpv_grid_size = lpv_system.getGridSize(), .lpv_origin = lpv_origin, + .lpv_available = lpv_system.isEnabled(), }); const aspect = built_params.aspect; const taa_enabled = built_params.taa_enabled; diff --git a/modules/game-ui/src/screens/world_frame_params.zig b/modules/game-ui/src/screens/world_frame_params.zig index 24930c51..1adafc24 100644 --- a/modules/game-ui/src/screens/world_frame_params.zig +++ b/modules/game-ui/src/screens/world_frame_params.zig @@ -27,6 +27,7 @@ pub const BuildInput = struct { lpv_cell_size: f32, lpv_grid_size: u32, lpv_origin: Vec3, + lpv_available: bool, }; pub const BuiltParams = struct { @@ -89,7 +90,7 @@ pub fn build(input: BuildInput) BuiltParams { .volumetric_steps = input.settings.volumetric_steps, .volumetric_scattering = input.settings.volumetric_scattering, .ssao_enabled = ssao_enabled, - .lpv_enabled = input.settings.lpv_enabled and !input.safe_mode and !input.startup_light_render, + .lpv_enabled = input.lpv_available, .lpv_intensity = input.settings.lpv_intensity, .lpv_cell_size = input.lpv_cell_size, .lpv_grid_size = input.lpv_grid_size,