3D resolution: scale, filtering and unchanged UI
Change internal resolution, distinguish it from output texture size and handle unavailable upscalers correctly.
What scale changes
Scaling3DScale changes internal 3D resolution, not window dimensions or canvas UI size. The viewport output texture retains its dimensions. Change SubViewport.Size separately to resize output itself.
| 512×384 output | Internal 3D render | Pixel count relative to Native |
|---|---|---|
| Scale 0.5 | 256×192 | 25% |
| Scale 1 | 512×384 | 100% |
| Scale 2 | 1024×768 | 400% |
Source and a diagnostic spatial shader confirm these dimensions in OpenGL/Vulkan. GetTexture().GetImage() remains 512×384 in all three cases: it reads output, not the raw internal buffer.
Fewer pixels can help a GPU-bound workload. If scripts, physics or CPU frame preparation dominate, lower resolution can barely change FPS. One quarter of internal pixels does not mean four times the speed.
Run the comparison
Use both complete files from the antialiasing guide: QualityPreset.cs and QualityLab3D.cs. Use C# SDK 1.0.0, net10.0, run dotnet build and attach QualityLab3D.cs to an Entity3D-rooted .rscene level.
Before startup, select Preset: Native, HalfBilinear, HalfNearest or DoubleBilinear. Keep output dimensions constant for a reproducible comparison; figures below are 512×384. The blue 80×20 indicator belongs to CanvasLayer and keeps its size when 3D scale changes.
Compare filters
Bilinear blends neighboring samples while enlarging, producing softer output. Nearest retains a hard low-resolution grid. Native checks distinguish their actual pixels while UI stays in output coordinates.
Scale 2 adds partial coverage to diagonal edges after reduction. It renders more internal pixels; performance is not measured here. Use Bilinear for downsampling: source internally falls back from FSR/MetalFX/Nearest to bilinear above scale 1.
Select upscalers without a misleading indicator
Current code applies Fsr and Fsr2 in Forward+. The FsrHalf/Fsr2Half presets use scale 0.5. Their Forward+/Vulkan frames differ from HalfBilinear. Temporal Fsr2 is compared after history accumulation, without verifying motion or ghosting quality.
Compatibility can reject an Fsr2 request while the getter still reports Fsr2. If the renderer previously used Bilinear, it remains bilinear; if it used Nearest, it remains nearest. Both cases are reproduced. Therefore, GetScaling3DMode alone cannot identify the applied algorithm, and not every rejected request necessarily produces bilinear fallback.
QualityPreset.Apply checks the renderer before changing state. On false, preserve the previous selected value in your game menu and indicate that the mode is unavailable. Settings apply to the supplied viewport; changing another viewport does not change the active game output.
MetalFX, Mobile, XR and other GPUs are not exercised here. Verify their support and restrictions separately. FsrSharpness neither enables FSR nor changes resolution; its quality effects are outside this example.
Validate the range and diagnose
For finite Scaling3DScale values, Viewport clamps to 0.1..2. Native checks confirm requests 0.01→0.1 and 9→2. Reject NaN/infinity at your menu input; clamping does not fully validate arbitrary floats.
| Problem | Check |
|---|---|
| GetTexture dimensions never change | Expected: this measures output, not the internal 3D buffer |
| Entire UI shrinks along with 3D | Size/global stretch changed instead of separate 3D scale |
| UI says FSR but output remains nearest | Request may be rejected; check renderer, warnings and previous mode |
| Lower scale does not improve FPS | Profile CPU/GPU, physics, scripts and other costs |
| Thin details disappear | Low internal resolution, filter and temporal method separately |
Dynamic resolution needs a separate controller with measurements and limits. This example switches fixed presets rather than implementing a dynamic-resolution system.