Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ zigcraft-minidumps/
*.spv
!assets/shaders/vulkan/lpv_inject.comp.spv
!assets/shaders/vulkan/lpv_propagate.comp.spv
!assets/shaders/vulkan/lod_compact_terrain.frag.spv
!assets/shaders/vulkan/lod_compact_water.frag.spv
!assets/shaders/vulkan/water.frag.spv
!assets/shaders/vulkan/water.vert.spv
wiki/
*.exr
*.hdr
Expand Down
19 changes: 15 additions & 4 deletions assets/shaders/vulkan/g_pass.frag
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,22 @@ layout(set = 0, binding = 0) uniform GlobalUniforms {
vec4 lpv_origin;
} global;

const float LOD_CHUNK_SIZE = 16.0;

bool shouldDiscardLODFragment(float encodedMaskRadius, vec2 cameraRelativeXZ) {
float maskRadius = abs(encodedMaskRadius);
if (maskRadius < 1.0) return false;

bool readyDiskMask = encodedMaskRadius < 0.0;
vec2 cameraChunkLocal = mod(global.cam_pos.xz, LOD_CHUNK_SIZE);
vec2 chunkDelta = floor((cameraRelativeXZ + cameraChunkLocal) / LOD_CHUNK_SIZE);
float detailRadiusChunks = floor(maskRadius / LOD_CHUNK_SIZE) + (readyDiskMask ? 0.0 : 2.0);
return dot(chunkDelta, chunkDelta) <= detailRadiusChunks * detailRadiusChunks;
}

void main() {
bool isLOD = vTileID < 0 || vMaskRadius > 0.0;
if (vMaskRadius >= 1.0) {
if (length(vFragPosWorld.xz) < vMaskRadius) discard;
}
bool isLOD = vTileID < 0 || abs(vMaskRadius) > 0.0;
if (shouldDiscardLODFragment(vMaskRadius, vFragPosWorld.xz)) discard;

vec3 N = normalize(vNormal);
if (!isLOD) {
Expand Down
19 changes: 16 additions & 3 deletions assets/shaders/vulkan/lod_compact_terrain.frag
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,30 @@ layout(set = 0, binding = 0) uniform Global {
vec4 lpv_origin;
} global;

const float LOD_CHUNK_SIZE = 16.0;

bool shouldDiscardLODFragment(float encodedMaskRadius, vec2 cameraRelativeXZ) {
float maskRadius = abs(encodedMaskRadius);
if (maskRadius < 1.0) return false;

bool readyDiskMask = encodedMaskRadius < 0.0;
vec2 cameraChunkLocal = mod(global.cam_pos.xz, LOD_CHUNK_SIZE);
vec2 chunkDelta = floor((cameraRelativeXZ + cameraChunkLocal) / LOD_CHUNK_SIZE);
float detailRadiusChunks = floor(maskRadius / LOD_CHUNK_SIZE) + (readyDiskMask ? 0.0 : 2.0);
return dot(chunkDelta, chunkDelta) <= detailRadiusChunks * detailRadiusChunks;
}

void main() {
if (vMaskRadius >= 1.0 && length(vFragPosWorld.xz) < vMaskRadius) discard;
if (shouldDiscardLODFragment(vMaskRadius, vFragPosWorld.xz)) discard;
vec3 normal = normalize(vNormal);
vec3 light_dir = normalize(global.sun_dir.xyz);
float diffuse = max(dot(normal, light_dir), 0.0);
float block_light = max(vBlockLight.r, max(vBlockLight.g, vBlockLight.b));
float illumination = clamp(max(vSkyLight * global.lighting.x, block_light) + diffuse * global.params.w * 0.45, 0.18, 1.15);
vec3 color = vColor * illumination * mix(0.72, 1.0, clamp(vAO, 0.0, 1.0));
if (global.params.z > 0.5) {
float fog = clamp(1.0 - exp(-vDistance * global.params.y), 0.0, 1.0);
fog = max(fog, smoothstep(300.0, 1200.0, vDistance) * 0.62);
float rawFog = clamp(1.0 - exp(-vDistance * global.params.y), 0.0, 1.0);
float fog = rawFog * rawFog * 0.72;
color = mix(color, global.fog_color.rgb, fog);
}
outColor = vec4(color, 1.0);
Expand Down
Binary file not shown.
23 changes: 19 additions & 4 deletions assets/shaders/vulkan/lod_compact_water.frag
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ layout(location = 11) in float vLODFade;

layout(location = 0) out vec4 outColor;

// Matches the two-chunk overlap reserved by LODConfig.calculateMaskRadius().
const float LOD_MASK_BLEND_WIDTH = 32.0;

layout(set = 0, binding = 0) uniform Global {
mat4 view_proj;
mat4 view_proj_prev;
Expand All @@ -35,7 +38,19 @@ layout(set = 0, binding = 0) uniform Global {
} global;

void main() {
if (vMaskRadius >= 1.0 && length(vFragPosWorld.xz) < vMaskRadius) discard;
float lodMaskAlpha = 1.0;
if (abs(vMaskRadius) >= 1.0) {
// A negative radius carries the outer edge of the ready detail disk;
// begin water's translucent handoff two chunks inside that edge.
bool readyDiskMask = vMaskRadius < 0.0;
float maskRadius = abs(vMaskRadius);
if (readyDiskMask) maskRadius = max(maskRadius - LOD_MASK_BLEND_WIDTH, 0.0);
float maskDistance = length(vFragPosWorld.xz);
if (maskDistance < maskRadius) discard;
// Fade the translucent LOD underlay in across the detailed-water
// overlap instead of changing its contribution at a hard circle.
lodMaskAlpha = smoothstep(maskRadius, maskRadius + LOD_MASK_BLEND_WIDTH, maskDistance);
}
// Far water deliberately avoids scene-depth, reflection, SSR, atlas, and
// thickness reads. It uses stable low-frequency waves and atmospheric fog.
float wave = sin(vFragPosWorld.x * 0.012 + global.params.x * 0.55) *
Expand All @@ -49,10 +64,10 @@ void main() {
base += global.sun_color.rgb * diffuse * global.params.w * 0.06;

if (global.params.z > 0.5) {
float fog = clamp(1.0 - exp(-vDistance * global.params.y), 0.0, 1.0);
fog = max(fog, smoothstep(280.0, 1100.0, vDistance) * 0.62);
float rawFog = clamp(1.0 - exp(-vDistance * global.params.y), 0.0, 1.0);
float fog = rawFog * rawFog * 0.65;
base = mix(base, global.fog_color.rgb, fog);
}

outColor = vec4(base, 0.78 * clamp(vLODFade, 0.0, 1.0));
outColor = vec4(base, 0.78 * clamp(vLODFade, 0.0, 1.0) * lodMaskAlpha);
}
Binary file added assets/shaders/vulkan/lod_compact_water.frag.spv
Binary file not shown.
31 changes: 19 additions & 12 deletions assets/shaders/vulkan/terrain.frag
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ layout(set = 0, binding = 0) uniform GlobalUniforms {

// Constants
const float PI = 3.14159265359;
const float LOD_CHUNK_SIZE = 16.0;

bool shouldDiscardLODFragment(float encodedMaskRadius, vec2 cameraRelativeXZ) {
float maskRadius = abs(encodedMaskRadius);
if (maskRadius < 1.0) return false;

bool readyDiskMask = encodedMaskRadius < 0.0;
vec2 cameraChunkLocal = mod(global.cam_pos.xz, LOD_CHUNK_SIZE);
vec2 chunkDelta = floor((cameraRelativeXZ + cameraChunkLocal) / LOD_CHUNK_SIZE);
// Streaming gives the contiguous ready disk to detail and the outer
// annulus to LOD. Legacy integral masks retain the two-chunk overlap.
float detailRadiusChunks = floor(maskRadius / LOD_CHUNK_SIZE) + (readyDiskMask ? 0.0 : 2.0);
return dot(chunkDelta, chunkDelta) <= detailRadiusChunks * detailRadiusChunks;
}

float saturate(float v) {
return clamp(v, 0.0, 1.0);
Expand Down Expand Up @@ -264,7 +278,7 @@ float computeShadowFactor(vec3 fragPosWorld, vec3 N, vec3 L, int layer) {
// receiver reference moves slightly closer to the light (higher depth) to
// avoid self-shadowing on coplanar surfaces.
float biasTexels = 0.35 + 0.2 * min(tanTheta, 5.0);
if (vTileID < 0 || vMaskRadius > 0.0) biasTexels = max(biasTexels, 0.45);
if (vTileID < 0 || abs(vMaskRadius) > 0.0) biasTexels = max(biasTexels, 0.45);
float bias = worldTexelSize * biasTexels / depthSpan;
float compareDepth = min(currentDepth + bias, 1.0);

Expand Down Expand Up @@ -509,17 +523,15 @@ void main() {
const float TEXTURE_FADE_START = 32.0;
const float TEXTURE_FADE_END = 128.0;
float viewDistance = length(vFragPosWorld);
bool isLOD = vTileID < 0 || vMaskRadius > 0.0;
bool isLOD = vTileID < 0 || abs(vMaskRadius) > 0.0;
float textureDetail = 1.0 - smoothstep(TEXTURE_FADE_START, TEXTURE_FADE_END, viewDistance);
if (isLOD) {
textureDetail = 0.0;
}

if (vMaskRadius >= 1.0) {
// Full-detail chunks own this area. Dithering the handoff creates a
// camera-following grid of holes at the chunk/LOD boundary.
if (length(vFragPosWorld.xz) < vMaskRadius) discard;
}
// Full-detail chunks own this area. Dithering the handoff creates a
// camera-following grid of holes at the chunk/LOD boundary.
if (shouldDiscardLODFragment(vMaskRadius, vFragPosWorld.xz)) discard;

vec2 tileBase = vec2(mod(float(vTileID), 16.0), floor(float(vTileID) / 16.0)) * (1.0 / 16.0);
vec2 tiledUV = fract(vTexCoord);
Expand Down Expand Up @@ -585,11 +597,6 @@ void main() {
if (global.params.z > 0.5) {
float rawFog = clamp(1.0 - exp(-viewDistance * global.params.y), 0.0, 1.0);
float fogFactor = rawFog * rawFog * 0.72 * atmosphericVisibility;
if (isLOD) {
float lodEdgeFog = smoothstep(0.65, 1.0, vLODFade) * rawFog * atmosphericVisibility;
float lodHorizonFog = smoothstep(420.0, 1400.0, viewDistance) * atmosphericVisibility;
fogFactor = max(fogFactor, max(lodEdgeFog * 0.9, lodHorizonFog * 0.82));
}
color = mix(color, global.fog_color.rgb, fogFactor);
}

Expand Down
Binary file modified assets/shaders/vulkan/terrain.frag.spv
Binary file not shown.
4 changes: 3 additions & 1 deletion assets/shaders/vulkan/terrain.vert
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ void main() {
float lod_fade;
vec3 color_override;

if (model_data.mask_radius < 0.0) {
// Color alpha is reserved as the indirect-draw sentinel. Signed mask
// radii encode the dynamic ready-detail disk and are valid direct values.
if (model_data.color_override.w < 0.0) {
InstanceData inst = instance_buf.instances[gl_InstanceIndex];
model = inst.model;
mask_radius = inst.mask_radius;
Expand Down
Binary file modified assets/shaders/vulkan/terrain.vert.spv
Binary file not shown.
21 changes: 16 additions & 5 deletions assets/shaders/vulkan/water.frag
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ const vec3 WATER_SHALLOW = vec3(0.20, 0.58, 0.86);
const vec3 WATER_MID = vec3(0.08, 0.34, 0.70);
const vec3 WATER_DEEP = vec3(0.02, 0.12, 0.42);
const float WATER_MAX_DEPTH = 14.0;
// Matches the two-chunk overlap reserved by LODConfig.calculateMaskRadius().
const float LOD_MASK_BLEND_WIDTH = 32.0;

const float WAVE_AMPLITUDE = 0.5;
const float WAVE_FREQUENCY = 1.5;
Expand Down Expand Up @@ -129,9 +131,19 @@ vec2 atlasUV(int tileID, vec2 texCoord) {
}

void main() {
bool isLOD = vTileID < 0 || vMaskRadius > 0.0;
if (vMaskRadius >= 1.0) {
if (length(vFragPosWorld.xz) < vMaskRadius) discard;
bool isLOD = vTileID < 0 || abs(vMaskRadius) > 0.0;
float lodMaskAlpha = 1.0;
if (abs(vMaskRadius) >= 1.0) {
// A negative radius carries the outer edge of the ready detail disk;
// begin water's translucent handoff two chunks inside that edge.
bool readyDiskMask = vMaskRadius < 0.0;
float maskRadius = abs(vMaskRadius);
if (readyDiskMask) maskRadius = max(maskRadius - LOD_MASK_BLEND_WIDTH, 0.0);
float maskDistance = length(vFragPosWorld.xz);
if (maskDistance < maskRadius) discard;
// Fade the translucent LOD underlay in across the detailed-water
// overlap instead of changing its contribution at a hard circle.
lodMaskAlpha = smoothstep(maskRadius, maskRadius + LOD_MASK_BLEND_WIDTH, maskDistance);
}
float time = global.params.x;

Expand Down Expand Up @@ -216,7 +228,6 @@ void main() {
if (global.params.z > 0.5) {
float rawFog = clamp(1.0 - exp(-vDistance * global.params.y), 0.0, 1.0);
float fogBlend = max(rawFog * rawFog * 0.65, water_mass * 0.28);
if (isLOD) fogBlend = max(fogBlend, smoothstep(260.0, 1000.0, vDistance) * 0.56);
waterColor = mix(waterColor, global.fog_color.rgb, fogBlend);
}

Expand All @@ -226,5 +237,5 @@ void main() {
if (isLOD) alpha = max(alpha, 0.93);
alpha = clamp(alpha, 0.56, 0.96);

FragColor = vec4(waterColor, alpha);
FragColor = vec4(waterColor, alpha * lodMaskAlpha);
}
Binary file added assets/shaders/vulkan/water.frag.spv
Binary file not shown.
4 changes: 3 additions & 1 deletion assets/shaders/vulkan/water.vert
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ void main() {
float lod_fade;
vec3 color_override;

if (model_data.mask_radius < 0.0) {
// Color alpha is reserved as the indirect-draw sentinel. Signed mask
// radii encode the dynamic ready-detail disk and are valid direct values.
if (model_data.color_override.w < 0.0) {
InstanceData inst = instance_buf.instances[gl_InstanceIndex];
model = inst.model;
mask_radius = inst.mask_radius;
Expand Down
Binary file added assets/shaders/vulkan/water.vert.spv
Binary file not shown.
2 changes: 1 addition & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1071,7 +1071,7 @@ fn defineBuildOptions(b: *std.Build, optimize: std.builtin.OptimizeMode) BuildOp
const screenshot_delay_seconds = b.option(u32, "screenshot-delay-seconds", "Seconds to wait after screenshot target is ready before capture") orelse 0;
options.addOption(u32, "screenshot_delay_seconds", screenshot_delay_seconds);

const phase5_visual_scene = b.option([]const u8, "phase5-visual-scene", "Deterministic production-world fixture/camera for the Phase 5 visual gate (seam, water, lod-handoff, lod-handoff-traversal, fog-rapid-turn, teleport-handoff, saved-world-create, saved-world-reload)") orelse "";
const phase5_visual_scene = b.option([]const u8, "phase5-visual-scene", "Deterministic production-world fixture/camera for the Phase 5 visual gate (seam, water, lod-handoff, lod-aerial, lod-handoff-traversal, fog-rapid-turn, teleport-handoff, saved-world-create, saved-world-reload)") orelse "";
options.addOption([]const u8, "phase5_visual_scene", phase5_visual_scene);
const phase5_visual_run_id = b.option([]const u8, "phase5-visual-run-id", "Fresh evidence scope identifier for a Phase 5 visual-gate invocation") orelse "";
options.addOption([]const u8, "phase5_visual_run_id", phase5_visual_run_id);
Expand Down
31 changes: 24 additions & 7 deletions docs/lod-quality-controls.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
# LOD Quality Controls

The render distance preset is the supported user-facing control for distant LOD quality. Presets intentionally expose a small set of stable knobs:
The render distance preset seeds distant LOD quality. The World settings also
expose `Render Distance` for full-detail chunks and `Distant LOD Limit` as the
outer terrain radius. Lowering the latter reduces generation pressure;
coarse regions fill concentrically so the engine does not render isolated
outer-horizon islands before nearby fallback terrain.

The production `Distant LOD Limit` currently supports 256 or 512 chunks.
Larger radii remain benchmark/diagnostic-only: the five-level hierarchy has no
coarser level beyond LOD4, so a contiguous 1,024-chunk disk exceeds the normal
logical-memory and compact-pool qualification budgets.

Presets intentionally expose a small set of stable knobs:

- `lod_radii`: chunk radii for LOD0 through LOD4.
- `horizon_radius`: the supported far-terrain horizon in chunks.
- `lod_store_size_cap_mb`: an aggregate cap across all persistent `.zlod`
containers. The cache worker evicts the oldest containers after atomic
writes and compacts live entries when sector growth reaches the cap.
- `horizontal_detail`: target horizontal detail per LOD. This is used as a floor for QEM triangle targets when the experimental QEM mesh path is enabled.
- `sample_density`: source-grid density per LOD. Medium uses half density for
LOD4 so its initial 512-chunk horizon has 33x33 source grids instead of
65x65 grids; finer LODs replace those 16-block cells as they stream in.
- `sample_density`: source-grid density per LOD. Every 512-chunk production
horizon uses half density for LOD4, giving the fallback 33x33 source grids
instead of 65x65 grids; finer LODs replace those 16-block cells as they
stream in. The 256-chunk Low horizon retains its denser LOD4 source grid.
- `vertical_span_budget`: enables rich column/span source data when nonzero.
The numeric values are reserved preset policy; current source allocation is
bounded by the engine-wide `MAX_LOD_VERTICAL_SPANS` limit.
- `mesh_path`: selects the rich `column_spans` path for near and mid-distance
LODs. LOD3/LOD4 deliberately fall back to heightfields to bound far-horizon
geometry and memory; `qem` remains available for controlled testing.
- `fog_start_percent`: controls the fade band for each LOD level.
- `fog_start_percent`: records the intended per-level fade-band policy. Terrain
and water currently use the shared atmospheric distance-fog curve so loaded
LODs do not turn into an opaque horizon-colored shelf near the player.
- `memory_budget_mb` and `max_uploads_per_frame`: bound cache pressure and per-frame GPU upload work.

## Supported presets
Expand All @@ -27,8 +41,8 @@ The render distance preset is the supported user-facing control for distant LOD
| Low | 256 chunks | 33/33/33/65/65 | 2 | 128 MB | 512 MB | 4 |
| Medium | 512 chunks | 33/49/49/65/33 | 2 | 256 MB | 1,024 MB | 8 |
| High | 512 chunks | 33/65/65/97/97 | 3 | 384 MB | 1,536 MB | 8 |
| Ultra | 1,024 chunks | 33/65/65/129/129 | 4 | 512 MB | 3,072 MB | 12 |
| Extreme | 2,048 chunks | 33/65/65/129/129 | 4 | 1,024 MB | 4,096 MB | 16 |
| Ultra | 512 chunks | 33/65/65/129/129 | 4 | 512 MB | 3,072 MB | 12 |
| Extreme | 512 chunks | 33/65/65/129/129 | 4 | 1,024 MB | 4,096 MB | 16 |

These values are policy inputs, not a promise that all hardware sustains the
full horizon. The memory governor shrinks refinement radii under pressure but
Expand All @@ -48,6 +62,9 @@ The benchmark SLOs and regression thresholds are maintained in
- Parent regions remain visible until all four finer children are renderable
and the transition window completes. Streaming delay therefore degrades to
coarser terrain rather than a hierarchy hole.
- Expanded and compact LOD terrain and water use the same atmospheric fog
progression as their full-detail counterparts. LOD representation changes
must not introduce an additional fixed-distance fog floor.
- Pause and large traversal changes invalidate queued and in-flight worker
tokens. Per-region cancellation prevents a stale generation result from
publishing after unpause or teleport.
Expand Down
4 changes: 2 additions & 2 deletions docs/shaders/spirv-sizes.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
"assets/shaders/vulkan/fxaa.frag": 5916,
"assets/shaders/vulkan/fxaa.vert": 1160,
"assets/shaders/vulkan/g_pass.frag": 6912,
"assets/shaders/vulkan/lod_compact_terrain.frag": 4296,
"assets/shaders/vulkan/lod_compact_terrain.frag": 5660,
"assets/shaders/vulkan/lod_compact_terrain.vert": 20656,
"assets/shaders/vulkan/lod_compact_water.frag": 5364,
"assets/shaders/vulkan/lod_compact_water.frag": 5912,
"assets/shaders/vulkan/lod_compact_water.vert": 13332,
"assets/shaders/vulkan/lod_culling.comp": 14044,
"assets/shaders/vulkan/lpv_inject.comp": 4844,
Expand Down
5 changes: 5 additions & 0 deletions modules/engine-graphics/src/render_system.zig
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,11 @@ pub const RenderSystem = struct {
self.rhi.renderContext().endFrame();
}

/// Discards the active frame without submitting it to the GPU.
pub fn abortFrame(self: *RenderSystem) void {
self.rhi.renderContext().abortFrame();
}

pub fn waitIdle(self: *RenderSystem) void {
self.rhi.query().waitIdle();
}
Expand Down
7 changes: 7 additions & 0 deletions modules/engine-graphics/src/rhi_tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,13 @@ test "IRenderContext getEncoder" {
try testing.expectEqual(&MockContext.MOCK_STATE_VTABLE, state.vtable);
}

test "indirect model uniforms use alpha sentinel without consuming mask sign" {
const uniforms = @import("vulkan/rhi_draw_submission.zig").indirectModelUniforms();

try testing.expect(uniforms.color[3] < 0.0);
try testing.expectEqual(@as(f32, 0.0), uniforms.mask_radius);
}

test "AtmosphereSystem.renderSky with null handles" {
var mock = MockContext{};
const rhi_instance = rhi.RHI{ .ptr = &mock, .vtable = &MockContext.MOCK_VULKAN_RHI_VTABLE, .device = null };
Expand Down
16 changes: 12 additions & 4 deletions modules/engine-graphics/src/rhi_vulkan.zig
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,12 @@ fn abortFrame(ctx_ptr: *anyopaque) void {
const ctx: *VulkanContext = @ptrCast(@alignCast(ctx_ptr));
if (!ctx.frames.frame_in_progress) return;

if (ctx.runtime.main_pass_active) endMainPass(ctx_ptr);
if (ctx.shadow_system.pass_active) endShadowPass(ctx_ptr);
if (ctx.runtime.g_pass_active) endGPass(ctx_ptr);

// Reset both recording command buffers before any screen/world teardown.
// vkDeviceWaitIdle only covers submitted work and cannot make references in
// an unsubmitted recording command buffer safe to destroy.
ctx.resources.abortCurrentFrame();
ctx.frames.abortFrame();
if (ctx.screenshot_capture.staging != null) screenshot.discardCapture(ctx);

// Recreate semaphores
const device = ctx.vulkan_device.vk_device;
Expand All @@ -161,6 +162,13 @@ fn abortFrame(ctx_ptr: *anyopaque) void {
ctx.shadow_system.pass_active = false;
ctx.runtime.g_pass_active = false;
ctx.runtime.ssao_pass_active = false;
ctx.water_system.pass_active = false;
ctx.post_process.pass_active = false;
ctx.fxaa.pass_active = false;
ctx.ui.ui_swapchain_pass_active = false;
ctx.ui.ui_using_swapchain = false;
ctx.ui.ui_swapchain_clears_output = false;
ctx.runtime.final_composed.clear();
ctx.draw.descriptors_updated = false;
ctx.draw.bound_texture = 0;
}
Expand Down
Loading
Loading