Skip to content

[Batch 6] Dynamic resolution rendering #392

@MichaelFisher1997

Description

@MichaelFisher1997

Summary

Implement dynamic resolution rendering that adjusts render resolution based on frame time budget. During fast camera movement or at extreme render distances, render at lower resolution and upscale via TAA. This keeps frame time consistent regardless of scene complexity.

Depends on: #388 (double-buffer upload — needs stable frame pacing)

How It Works

  1. Each frame: measure GPU time of previous frame
  2. If over budget (e.g., 16ms for 60 FPS target): reduce resolution scale (e.g., 0.7x)
  3. If under budget: increase resolution scale (e.g., 1.0x = native)
  4. Render at adjusted resolution into offscreen target
  5. TAA resolve + upscale to native swapchain resolution
  6. Smooth transitions: scale changes ±0.01 per frame (no popping)

Implementation Plan

Step 1: Offscreen render targets

  • Create color + depth attachments at dynamically-sized resolution
  • When scale changes: recreate these targets (or pre-allocate at max size and use view subsets)
  • More efficient: pre-allocate at native resolution, use vkCmdSetViewport/vkCmdSetScissor to render into a sub-region, then upscale

Step 2: Frame time tracking

  • Use existing GPU timestamps from rhi_timing.zig
  • Track rolling average over 8 frames to avoid thrashing
  • Budget: configurable (16ms for 60 FPS, 8ms for 120 FPS)

Step 3: Scale adjustment logic

const target_ms = 1000.0 / target_fps;
const current_ms = rolling_avg_gpu_ms;

if (current_ms > target_ms * 1.1) {
    // Over budget: reduce scale
    scale = @max(scale - 0.02, min_scale);
} else if (current_ms < target_ms * 0.8) {
    // Under budget: increase scale
    scale = @min(scale + 0.01, max_scale);
}

Step 4: TAA upscale integration

  • TAA pass already exists (taa_system.zig)
  • Modify to accept input at lower resolution and output at native
  • TAA's temporal accumulation naturally smooths the upscale
  • Jitter pattern scaled to match render resolution

Step 5: Settings integration

  • Add "Dynamic Resolution" toggle to settings
  • Add min/max scale bounds (default: 0.5x - 1.0x)
  • Add target FPS setting (60, 120, 144, unlimited)
  • Show current scale in timing overlay

Step 6: Quality considerations

  • UI always renders at native resolution (no blur on text/menus)
  • Only world rendering uses dynamic resolution
  • Post-processing (bloom, FXAA) runs at native resolution on upscaled image

Files to Modify

  • src/engine/graphics/vulkan/frame_manager.zig — dynamic render targets
  • src/engine/graphics/vulkan/swapchain_presenter.zig — resolve to native
  • src/engine/graphics/vulkan/taa_system.zig — accept variable input resolution
  • src/engine/graphics/render_graph.zig — pass resolution to sub-passes
  • src/engine/ui/timing_overlay.zig — show current scale
  • src/game/settings/data.zig — dynamic resolution toggle, min/max scale, target FPS

Testing

  • Resolution scale adjusts smoothly under heavy load
  • No visible popping when scale changes
  • UI remains sharp at all scales
  • TAA quality maintained at non-native resolution
  • Frame time stays within budget at 256+ chunks with dynamic resolution
  • Disabling toggle locks to native resolution
  • Extreme preset (512 chunks) remains playable

Roadmap: docs/PERFORMANCE_ROADMAP.md — Batch 6, Issue 4B

Metadata

Metadata

Assignees

No one assigned

    Labels

    batch-6Batch 6: CapstonedocumentationImprovements or additions to documentationengineenhancementNew feature or requestquestionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions