Repository navigation
Conversation
BasisVideoTextureOutput copies the player's video into a RenderTexture each frame the player presents one, for anything that samples a texture rather than drawing a screen: a CustomRenderTexture's input, a lighting system's colour source, a material many renderers share. BasisVideoMaterialOutput only binds to a renderer's material, and BasisVideoDisplay to a RawImage. It runs in the player's tick at the Output stage, after the poll has queued the frame's present, and copies only when the presented frame count moves, so a paused player costs nothing. The copy fills the target and restores the active RenderTexture; the platform's frame origin is handled, and the material output's flips apply through BasisVideoOutputMath.ApplyFlips, which both outputs now share. While there is no video the target holds ClearColor, converted to linear in a Linear project since GL.Clear takes it as stored. The copy and the clear both set GL.sRGBWrite from the target's sRGB flag and restore it after, so state left by earlier rendering doesn't change what is written. A target assigned, re-created or released while the output runs is filled at once, with the current frame or ClearColor. The component is on the scene and prop content police lists and the Cilbox prop whitelist, beside BasisVideoMaterialOutput, so worlds and props can use it. The player's class doc, the README and TESTING.md cover it; a CustomRenderTexture target must not have a material, since its update would draw over the copy. Tested in the Editor on Direct3D 11 and Direct3D 12 with a public on-demand video into a 320x180 sRGB target in a Linear project: a grey ClearColor reads back exactly before the first frame, the picture is upright while playing, a pause holds the same frame, a new RenderTexture assigned while paused holds that frame at once, and the clear colour returns after Stop. Also wired into a world as a VRSL-URP light manager's sampling texture, where the colour-sampled fixtures follow the video. Not yet run on Android.
dooly123
merged commit Oct 31, 2026
d508874
into
BasisVR:experimental-branch-major-changes
13 of 15 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This adds
BasisVideoTextureOutput, which copies a media player's video into a RenderTexture for anything that samples a texture rather than drawing a screen.BasisVideoMaterialOutputbinds the video to a renderer's material andBasisVideoDisplayto a RawImage, but neither gives you a RenderTexture asset you can hand to something else, such as a CustomRenderTexture's input, a lighting system's colour source or a material shared by many renderers. This component fills that gap:BasisVideoOutputMath.ApplyFlips).ClearColor(black by default). A target assigned, re-created or released while the output runs is filled straight away, with the current frame orClearColor.It's added to the scene and prop content police lists and the Cilbox prop whitelist, beside the material output, so worlds and props can use it. The README,
TESTING.mdand the player's class doc cover it.Required checks
All boxes below must be ticked before this PR can merge. If a check is genuinely N/A, tick it anyway and explain under Notes.
TransformAccessArrayor are otherwise batched. I have not added per-frametransform.position/transform.rotation/transform.localPositioncalls inside loops. Whenever I need both position and rotation, I use the combined APIs —SetPositionAndRotation/SetLocalPositionAndRotationfor writes,GetPositionAndRotation/GetLocalPositionAndRotationfor reads — instead of two separate property accesses; the combined call does one local-to-world matrix traversal instead of two.Resources.Load, no direct asset references that pull large content into memory on scene load.GetComponent/AddComponentwhere avoidable — Where unavoidable, the result is cached on a field, and anyGetComponent<T>is replaced withTryGetComponent<T>(out var x)— bareGetComponentwill be denied.TryGetComponentis the modern API (Unity 2019.2+) and skips the Editor-only GC allocationGetComponentcauses when a component is missing: Unity wraps thenullreturn in a managed "fake null" object so its overloaded==operator can still detect destroyed C++ objects, and constructing that wrapper allocates;TryGetComponentreturns aboolplusoutparameter and never builds the wrapper. None of these calls run insideUpdate,LateUpdate,FixedUpdate, jobs, or other per-frame code paths.BasisEventDriver— Any new per-frame work hooks intoBasisEventDriverrather than adding standaloneUpdate/LateUpdate/FixedUpdatecallbacks on a MonoBehaviour.BasisEventDriveris bulletproof, or guarded bytry/catch—BasisEventDriverruns the single per-frame tick that drives the whole framework (network apply, local player sim, blendshapes, JigglePhysics, nameplates, and more) as one sequential chain. An unhandled exception anywhere in that chain aborts the rest of the tick, so every step after the throwing one is silently skipped for that frame. New work added to the driver must either be guaranteed not to throw, or be wrapped in atry/catchthat contains the failure and surfaces it throughBasisDebug— logged once / rate-limited, never every frame (see the existingHVRBasisBuiltInAddresses.Simulate()guard for the pattern). Expect this to be scrutinized closely in review.{ get; set; }properties or access lockdowns — Public fields are fine; Basis is meant to be read and modified freely, so don't wall things offprivate/internalwithout a real reason. Don't wrap a field in{ get; set; }when the accessors do nothing — property accessors have a real performance cost vs direct field access, and the lead maintainer prefers plain fields (or a method / setter-only property when only the setter needs logic) over a noop-getter pair. For.Instancesingletons, callers reassigningType.Instanceis allowed; if that would break your code, log a warning or throw — don't block the assignment. Locking down access is not your call.BasisLocalCameraDriver— Code that needs the local camera (transform, projection, rig data, etc.) pulls it fromBasisLocalCameraDriverrather than looking one up itself. Don't roll a separate camera discovery path.BasisDebug— All new logging calls go throughBasisDebug.Log/BasisDebug.LogWarning/BasisDebug.LogError(with an appropriateLogTag) instead ofUnityEngine.Debug.Log/Debug.LogWarning/Debug.LogError.BasisDebugroutes through Basis's tagged, color-coded logger and respects the project-wideLoggingDisabledtoggle so logging can be killed at runtime; bareDebug.Logcalls bypass that and will be denied.FindObjectOfType/FindObjectsOfType/GameObject.Find/FindGameObjectsWithTagto locate what it depends on. References are wired in — registered through an existing manager/driver, injected at init, or passed in by the caller — rather than discovered by scanning the scene at runtime. If a scene scan is genuinely unavoidable, justify it under Notes.newon reference types, no LINQ, nostringconcatenation/interpolation, no boxing, noforeachover interface-typed collections. Allocate once at init and reuse the buffer.BasisDebug. Hot-path logging floods the console and incurs cost on every frame regardless of whether the message is filtered out downstream. If a hot-path log is needed while iterating, gate it behind#if UNITY_EDITORand remove (or leave gated) before merge..Count(lists) /.Length(arrays) into a localintbefore the loop instead of re-reading the property each iteration. PreferT[](with a separate length int when the array is over-sized) overList<T>where the data is hot — Unity's mono BCL doesn't exposeCollectionsMarshal.AsSpan(List<T>), so a list can't be fed intoSpan<T>/ unsafe paths cleanly. Where the perf justifies it, drop intoSpan<T>/reflocals /Unsafe.As/unsafepointer code to skip bounds checks and copies, and call out the invariants you're relying on under Notes so reviewers can sanity-check them.Testing details
Tick the platforms you actually tested on. Leave the rest unticked — these are informational and do not block merge.
Input / control mode coverage:
Where applicable, confirm these flows still work after your changes:
Notes
Managed C# plus the two content police assets and docs, with no plugin restage.
The per-frame work runs in the player's existing tick as an
IBasisMediaTickConsumerat the Output stage, after the poll has queued the frame's present, rather than in its ownUpdate. It doesn't allocate or log there.GetComponentInParentruns only inResetandOnEnable, when thePlayerfield is empty, as the material output already does.In a Linear project the copy and the clear set
GL.sRGBWritefrom the target's sRGB flag and restore it after, asGraphics.Blit's docs recommend, andClearColoris converted to linear sinceGL.Cleartakes it as stored.Tested in the Editor on 6000.7.0b6, on Direct3D 11 and Direct3D 12, with an on-demand H.264 MP4 into a 320x180 sRGB target in a Linear project: a grey
ClearColorreads back exactly before the first frame, the picture is upright while playing, a pause holds the same frame, a new RenderTexture assigned while paused holds that frame at once, and the clear colour returns after Stop. I've also wired it into a world as a VRSL-URP light manager's sampling texture, and the colour-sampled fixtures follow the video.