Skip to content

Expose viewport bg color in editor preferences#10295

Closed
boxrocket6803 wants to merge 862 commits intoFacepunch:masterfrom
boxrocket6803:viewport-color-preference
Closed

Expose viewport bg color in editor preferences#10295
boxrocket6803 wants to merge 862 commits intoFacepunch:masterfrom
boxrocket6803:viewport-color-preference

Conversation

@boxrocket6803
Copy link
Copy Markdown
Contributor

Summary

Allows the background color for the editor camera to be set via editor preferences, as opposed to being hard coded. By default it uses the previously hard coded value.

Motivation & Context

I personally prefer dark gray in my viewport backgrounds.

Video

2026-03-27.18-29-32.mp4

Checklist

  • Code follows existing style and conventions
  • No unnecessary formatting or unrelated changes
  • Public APIs are documented (if applicable)
  • Unit tests added where applicable and all passing
  • I’m okay with this PR being rejected or requested to change 🙂

lolleko and others added 30 commits February 23, 2026 21:19
Attempt to fix animation perf regression introduced by Facepunch#4128

Parallel.ForEach(IList<T>) uses static range partitioning which may cause load imbalance on hybrid P/E-core CPUs. Use Partitioner.Create(list, loadBalance: true) instead to restore dynamic chunk stealing that aws already used when using Parallel.ForEach(IEnumerable<T>)
…d trackball and view based rotation gizmos

This commit adds a blender style trackball and view space rotation to all rotate gizmos. This is a breaking change to anything that currently uses the rotate method as you now must use the output directly rather than multiply it. Also added a method to get distance vector, not sure why it's only a float.

---

Co-authored-by: William Lemon <William.Lemon2@gmail.com>
It will show as a dropdown in the editor which will allow you to pick an implementation.
* Verify CreateGameResults cookie before use

* don't modify Output directly

---------

Co-authored-by: nixx quality <nixx@is-fantabulo.us>
Can be used to train a dictionary for compression and analysing network packets.
* Fix native resource networking

Native resource networking never properly worked, because it relied on ids that may not exist on a connecting client.

- `Resource.ResourceId` is now Obsolete
- Added `internal Resource.ResourceIdLong`
- Networking now uses the 64 bit id
- Register all Resource Paths during package load, so networking for native packages works even if they are not loaded
- Client automatically loads networked resource if not cached
- Clothing container now serializes path instead of ID but maintains backwards compatibility.
- Drop ResourceId usage in tools
…JSON (Facepunch#4147)

Fixes and issue where clothings referenced via their ResourceID (e.g. free clothings) would not save correctly

Fixes Facepunch#10083
- Make a few Json Diff-related functions/classes public
- Make `GameObject.DiffObjectDefinitions` public
- Add static `Package.MountAsync()`
- Make `Scene.SerializeProperties()` public
* Add Panel.OnDragEnter, OnDragLeave, OnDrop, maintain drop target and clear after drop
Co-authored-by: Mahir Muratovic <73573263+Kaikenny@users.noreply.github.com>
fixes "Error calling event 'hotloaded' on 'Editor.ComponentSheet'
So far we only had a way to get the Color buffer of a render target, this lets us use  the depth target as well in commandlists.
* Use ToArray to avoid collection modified while iterated exception

Not sure this ever actually happened but better to be save.

* Remove ValidatePrefabLookup from SerializePrefabInstance

ValidatePrefabLookup may have modified GUIDs incorrectly on undo/redo.
Also Serialize should be read-only op.
ValidatePrefabLookup  is still called at enough other places.
* Add Json.Deserialize<T>( ref Utf8JsonReader reader )
* Add Json.Serialize<T>( Utf8JsonWriter writer, T target )
May fixes some issues when enabling/disabling nav areas.
Keep temp list so we can sort in place

Cache occlusion sort predicate in static to avoid action allocation
Fixes timing scopes to more accurately represent a per-frame main thread breakdown, and prevents spikes when GC is executed.

- **GcPause**
  - New separate timing scope showing time spent in GC per frame
  - GC pause time is subtracted from all other scopes, so each scope now only tracks its own code execution and no longer includes GC overhead
  - e.g. when GC occurs during the audio scope, the audio scope no longer spikes to 20ms
- **AudioMixingThread** removed from the main scopes
  - Runs on a separate thread, so its timings are effectively meaningless in the main thread view
  - All other scopes are main thread only
  - No longer relevant given the audio optimisation work done over the past months
- **Scene** scope removed
  - Didn't make much sense as it was an aggregate wrapping many other timing scopes
  - Replaced with a finer `Update` scope that tracks `Component.FixedUpdate`/`Update`
- **Editor** scope no longer shows in-game
- Scopes reschuffled
  - e.g. verlet rope physics traces are now tracked under the physics scope
  - Audio occlusion queries are now tracked under the audio scope

https://files.facepunch.com/lolleko/2026/March/02_12-59-QuixoticMarten.png
* Move duplicated ProjectInfo building to extension methods
* Add moviemaker package reference to editor packages
handsomematt and others added 15 commits March 26, 2026 15:58
When serializing and deserializing to/from JSON we need to explicitly write nulls.
Because for development/editing we need to differentiate the following cases:
1. JSON is of an older version (field missing) -> initialize with code defined default
2. JSON is of the current version and field is actually null -> initialize with null

For networking we don't need to differentiate between those cases since client and server have the same version of the code at runtime.

So in networking case we can omit nulls from the JSON and skip all of these:

```
          "OnComponentDestroy": null,
          "OnComponentDisabled": null,
          "OnComponentEnabled": null,
          "OnComponentFixedUpdate": null,
          "OnComponentStart": null,
          "OnComponentUpdate": null,
          "OnPropBreak": null,
          "OnPropTakeDamage": null,
```

However, this wont reduce the amount of data that ends up on the wire, because compression already does a good job at deduplicating those strings.

```
Raw JSON    with nulls :      41Kb
Raw JSON    skip nulls :      27Kb  (13Kb saved, 33.6% smaller, 1.51x ratio)
LZ4 fast    with nulls :       7Kb  (34Kb saved, 81.9% smaller, 5.54x ratio)
LZ4 fast    skip nulls :       7Kb  (34Kb saved, 82.6% smaller, 5.76x ratio)
```

The main benefit instead comes from saving CPU resources as this makes compression/decompression faster, because there is less data to process.

```
LZ4  with nulls          compress :    0.210 ms/op
LZ4  with nulls          decomp   :    0.232 ms/op
 
LZ4  skip nulls          compress :    0.165 ms/op
LZ4  skip nulls          decomp   :    0.074 ms/op
```
* Shadows Rewrite: C#, bindless, flexible, quality options, less VRAM...

Introduces a new shadow mapper written entirely in C#, as well as
rewritten shader code for sampling shadow maps. This fully removes and
replaces Source 2's native shadow mapping giving us greater flexibility
and allowing us to open-source it all.

The main goal for the new shadow mapper is greater flexibility whilst
reducing complexity.

Older shaders are incompatible with the new lighting buffers, and will
need to be recompiled to receive lighting properly.

What's new:

- Bindless per-light shadow maps instead of a shared shadow atlas — this
  means games can avoid the shadow atlas cost if not using many shadows,
  but also allows games with many shadows to not be limited by atlas
  space.

- Directional lights have developer configurable cascade count (1-4) and
  control over split ratio (logarithmic/uniform blend via lambda
  parameter), useful for games where you may not need multiple cascades.
  User quality settings define a maximum cascade count which always
  overrides developer settings, allowing low end hardware to use fewer.

- Directional lights have a dedicated cbuffer and uniform fast path in
  rendering code, and are no longer binned and looped over with local
  lights. Every pixel on screen is always affected by a directional
  light.

- CSM cascade selection uses bounding spheres instead of depth
  comparison, with per-cascade texel snapping to eliminate sub-texel
  shimmer.

- Point lights use a TextureCube for cube shadows for much simpler
  rendering and mapping, along with hardware PCF filtering.

- Local light shadow resolution is derived from each light's screen-space
  size. Shadows below a configurable threshold are not rendered at all.
  Lights are sorted by screen size, and r.shadows.max caps the total
  count, culling least important lights first.

- User settings have been added for shadow quality (Low/Medium/High)
  controlling max resolution, max cascades, and PCF filter quality.

- Local light shadow maps use D16 depth format, halving memory compared
  to D32. CSMs remain D32 for precision at large distances.
  (Although this could be a TODO, I bet we could make it work in D16)

- ShadowHardness: New per-light property controlling shadow sharpness.
  Defaults to soft (0.0) and scales up to 4x sharper. For directional
  lights, hardness is automatically scaled per cascade proportional to
  texel density (wider cascades get softer shadows), and clamped so the
  filter never exceeds a full texel — ensuring consistent softness
  across cascade transitions.

- Shadow debug overlay showing all information about allocated shadow maps,
  their textures, cascades and more.

- Many new convars to control
  - r.shadows.max: Maximum number of shadow-casting local lights, sorted by screen size, least important culled first
  - r.shadows.maxresolution: Max texture size for a projected light shadow map (128–4096)
  - r.shadows.quality: Shadow filter quality (0=Off, 1=Low, 2=Med, 3=High, 4=Experimental Penumbra)
  - r.shadows.csm.maxcascades: Maximum number of cascades for directional light shadows (1–4)
  - r.shadows.csm.maxresolution: Maximum resolution for each cascade shadow map (512–8192)
  - r.shadows.csm.distance: Maximum distance from camera that directional light shadows render (500–50000)
  - r.shadows.debug: Show shadow debug overlay with CSM textures, cascade bounds, and memory budget
  - r.shadows.csm.enabled: Enable or disable directional light (CSM) shadows
  - r.shadows.local.enabled: Enable or disable local light (spot/point) shadows
  - r.shadows.depthbias: Rasterizer constant depth bias during shadow map rendering
  - r.shadows.slopescale: Rasterizer slope-scaled depth bias during shadow map rendering
  - r.shadows.size_cull_threshold: Screen size percentage below which local light shadows are culled

- SceneLight refactored into a base class with ScenePointLight,
  SceneSpotLight, SceneDirectionalLight. SceneOrthoLight removed.

- Simplified Light.hlsl: Light is now a class, DynamicLight merged into
  Light, ProbeLight and LightmappedLight no longer inherit from
  DynamicLight.

- GPULight/BinnedLight struct reorganized and trimmed: explicit typed
  fields instead of packed uint4 Params, shadow data replaced with a
  shadow index into a separate StructuredBuffer, removed embedded shadow
  matrices and atlas bounds.

- ViewLightingConfig cleaned up: removed ViewLightingFlags,
  Shadow3x3PCFConstants, EnvironmentMapSizeConstants,
  LegacyAmbientLightColor.

- Baked light mode flags fixed: BAKED lights (lightmaps only) no longer
  create shadow maps. MIXED_SHADOWS gated to Stationary lights only
  (was unconditionally applied). RENDER_ALL_GEOMETRY flag removed.
  DirectLightMode enum documented across Hammer entity definitions.

- Removed light cookie sheets; cookie UV derived from light transform
  (LightToWorld transpose). Cookie sampling simplified to a single
  bindless texture lookup.
* Shadows Rewrite: C#, bindless, flexible, quality options, less VRAM...

Introduces a new shadow mapper written entirely in C#, as well as
rewritten shader code for sampling shadow maps. This fully removes and
replaces Source 2's native shadow mapping giving us greater flexibility
and allowing us to open-source it all.

The main goal for the new shadow mapper is greater flexibility whilst
reducing complexity.

Older shaders are incompatible with the new lighting buffers, and will
need to be recompiled to receive lighting properly.

What's new:

- Bindless per-light shadow maps instead of a shared shadow atlas — this
  means games can avoid the shadow atlas cost if not using many shadows,
  but also allows games with many shadows to not be limited by atlas
  space.

- Directional lights have developer configurable cascade count (1-4) and
  control over split ratio (logarithmic/uniform blend via lambda
  parameter), useful for games where you may not need multiple cascades.
  User quality settings define a maximum cascade count which always
  overrides developer settings, allowing low end hardware to use fewer.

- Directional lights have a dedicated cbuffer and uniform fast path in
  rendering code, and are no longer binned and looped over with local
  lights. Every pixel on screen is always affected by a directional
  light.

- CSM cascade selection uses bounding spheres instead of depth
  comparison, with per-cascade texel snapping to eliminate sub-texel
  shimmer.

- Point lights use a TextureCube for cube shadows for much simpler
  rendering and mapping, along with hardware PCF filtering.

- Local light shadow resolution is derived from each light's screen-space
  size. Shadows below a configurable threshold are not rendered at all.
  Lights are sorted by screen size, and r.shadows.max caps the total
  count, culling least important lights first.

- User settings have been added for shadow quality (Low/Medium/High)
  controlling max resolution, max cascades, and PCF filter quality.

- Local light shadow maps use D16 depth format, halving memory compared
  to D32. CSMs remain D32 for precision at large distances.
  (Although this could be a TODO, I bet we could make it work in D16)

- ShadowHardness: New per-light property controlling shadow sharpness.
  Defaults to soft (0.0) and scales up to 4x sharper. For directional
  lights, hardness is automatically scaled per cascade proportional to
  texel density (wider cascades get softer shadows), and clamped so the
  filter never exceeds a full texel — ensuring consistent softness
  across cascade transitions.

- Shadow debug overlay showing all information about allocated shadow maps,
  their textures, cascades and more.

- Many new convars to control
  - r.shadows.max: Maximum number of shadow-casting local lights, sorted by screen size, least important culled first
  - r.shadows.maxresolution: Max texture size for a projected light shadow map (128–4096)
  - r.shadows.quality: Shadow filter quality (0=Off, 1=Low, 2=Med, 3=High, 4=Experimental Penumbra)
  - r.shadows.csm.maxcascades: Maximum number of cascades for directional light shadows (1–4)
  - r.shadows.csm.maxresolution: Maximum resolution for each cascade shadow map (512–8192)
  - r.shadows.csm.distance: Maximum distance from camera that directional light shadows render (500–50000)
  - r.shadows.debug: Show shadow debug overlay with CSM textures, cascade bounds, and memory budget
  - r.shadows.csm.enabled: Enable or disable directional light (CSM) shadows
  - r.shadows.local.enabled: Enable or disable local light (spot/point) shadows
  - r.shadows.depthbias: Rasterizer constant depth bias during shadow map rendering
  - r.shadows.slopescale: Rasterizer slope-scaled depth bias during shadow map rendering
  - r.shadows.size_cull_threshold: Screen size percentage below which local light shadows are culled

- SceneLight refactored into a base class with ScenePointLight,
  SceneSpotLight, SceneDirectionalLight. SceneOrthoLight removed.

- Simplified Light.hlsl: Light is now a class, DynamicLight merged into
  Light, ProbeLight and LightmappedLight no longer inherit from
  DynamicLight.

- GPULight/BinnedLight struct reorganized and trimmed: explicit typed
  fields instead of packed uint4 Params, shadow data replaced with a
  shadow index into a separate StructuredBuffer, removed embedded shadow
  matrices and atlas bounds.

- ViewLightingConfig cleaned up: removed ViewLightingFlags,
  Shadow3x3PCFConstants, EnvironmentMapSizeConstants,
  LegacyAmbientLightColor.

- Baked light mode flags fixed: BAKED lights (lightmaps only) no longer
  create shadow maps. MIXED_SHADOWS gated to Stationary lights only
  (was unconditionally applied). RENDER_ALL_GEOMETRY flag removed.
  DirectLightMode enum documented across Hammer entity definitions.

- Removed light cookie sheets; cookie UV derived from light transform
  (LightToWorld transpose). Cookie sampling simplified to a single
  bindless texture lookup.
…acepunch#4409)

* Instead of in a EditorEvent.Hotload, which only happens in the editor
* Fixes exceptions on connected clients after hotload (Facepunch#10246, Facepunch#10210)
…acepunch#4409)

* Instead of in a EditorEvent.Hotload, which only happens in the editor
* Fixes exceptions on connected clients after hotload (Facepunch#10246, Facepunch#10210)
* Add DisableClientRecording config property in MovieRecorderSystem
* Fix handling missing prefabs when creating targets
* Fix AutoBind finding prefab scenes(?)
* Include current game's ident in __references for movie recordings
* Make exceptions when serializing blocks survivable
* Survive trying to compress NaN / infinite transforms / rotations
* MemberProperty: use delegate instead of reflection when getting value
* PropertyBlockWriter: avoid allocating _samples list for constants
* Fix creating targets from prefab with multiple same-type components
* Add TextRendererCapturer, fix recording strings
* Add DisableClientRecording config property in MovieRecorderSystem
* Fix handling missing prefabs when creating targets
* Fix AutoBind finding prefab scenes(?)
* Include current game's ident in __references for movie recordings
* Make exceptions when serializing blocks survivable
* Survive trying to compress NaN / infinite transforms / rotations
* MemberProperty: use delegate instead of reflection when getting value
* PropertyBlockWriter: avoid allocating _samples list for constants
* Fix creating targets from prefab with multiple same-type components
* Add TextRendererCapturer, fix recording strings
Copilot AI review requested due to automatic review settings March 27, 2026 23:36
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Exposes the Scene View (editor camera) viewport background color as an editor preference, preserving the previous hard-coded default.

Changes:

  • Replaced the hard-coded scene viewport clear color with EditorPreferences.CameraBackgroundColor.
  • Added a new EditorPreferences.CameraBackgroundColor preference backed by an editor cookie (defaulting to the old value).
  • Surfaced the new preference on the Scene View preferences page.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
game/addons/tools/Code/Scene/SceneView/SceneViewportWidget.cs Uses the new preference for the editor camera background clear color.
game/addons/tools/Code/Editor/EditorPreferences/PageSceneView.cs Adds the background color field to the Scene View preferences UI.
engine/Sandbox.Tools/EditorPreferences.cs Introduces the new persisted preference (cookie-backed) with the prior default.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread engine/Sandbox.Tools/EditorPreferences.cs
lolleko and others added 4 commits March 29, 2026 23:08
Rewrites Vulkan per-thread resource management to use a pooled PerThreadDataVulkan_t slot system with a TLS fast-path, replacing the previous thread-index/map/array approach and updating Vulkan call sites to pass PerThreadDataVulkan_t* instead of thread indices.

* Remove ThreadIndex, SetAssociatedThreadIndex & MaxDeviceThreads (see what breaks)

* Refactor per-thread vulkan data into a single pooled struct

Replace fixed-array per-thread Vulkan data with a dynamic slot pool.
Threads grab slots on demand, hot path is just a TLS read, completly lock-free

Two mechanisms manage slot lifetime:
- Ref counting: render contexts and the render thread AddRef to keep
  their slot alive as long as they need it.
- Idle reclaim: slots nobody is holding onto get recycled after N frames
  of inactivity. Keeps short-lived threads (async shader compiles, texture
  uploads) from leaking slots, while still letting a thread that calls
  GetPerThreadData() every frame hang onto the same slot and all its
  resources (command pools, descriptor pools, pipeline cache, etc).

Moved VkPipelineCache into the per-thread struct with lazy creation.

* Add CRenderDeviceVulkan::GetPerThreadData() acquires from pool

* Use PerThreadData eveywhere instead of ThreadIndex

And do proper thread data shutdown cleanup

* Main render thread gets it's own permaned PerThreadData instance

* Rework pipelinecache to use PerThreadData instead of VkPipelineCache directly
DevulTj and others added 4 commits March 31, 2026 11:49
* Fix GCN/Polaris crash when averaging clusters, snap positionSs to the top-left pixel of the 2x2 quad to avoid cluster divergence without QuadLaneReadAt

* update shaders
)

**Broadcast**
now encodes the wire payload just once and sends the same bytes to
every recipient, before we did one redundant compression per connection.
This primarily reduces CPU load on the server/host.

**Chunking**
Large messages are now compressed before chunking rather than after.
Resulting in slightly smaller payloads.
The receiver now decompresses a single reassembled payload instead of
decompressing every chunk independently, significantly reducing CPU load
on receiving clients.

**Refactor**
Chunking and compression are now low-level wire concerns handled by
Connection rather than being mixed into the high-level message types.
The old `InternalMessageType.Chunk` enum is removed; chunk framing uses
a dedicated wire flag byte alongside `FlagRaw` and `FlagCompressed`.

**Results (Chunking changes)**

Synthetic data, results on real payloads may differ.

Benchmarks (1000 GOs / 2000 components, ~1MB payload, 500 iterations):

Wire size (chunk-first-then-compress):    275KB
Wire size (compress-first):               259KB  (5.7% smaller)

Send  chunk-first:                        0.85 ms/op  (old)
Send  compress-first:                     0.88 ms/op  (new)

Recv  chunk-first:                        1.16 ms/op  (old)
Recv  compress-first:                     0.34 ms/op  (new, 3.4x faster)
@handsomematt handsomematt added the triaged triaged pull-requests are replicated on the internal sbox repo label Apr 5, 2026
@sboxbot sboxbot closed this Apr 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triaged triaged pull-requests are replicated on the internal sbox repo

Projects

None yet

Development

Successfully merging this pull request may close these issues.