we’re improving URP internals by moving some shader globals into persistent constant buffers to improve consistency/performance.
As part of this, we want to understand real-world usage where projects set URP internal global shader variables from script (for example via Shader.SetGlobal*, CommandBuffer.SetGlobal*, Material.Set*, MaterialPropertyBlock, etc.).
If you rely on this pattern, could you briefly share your use case?
Which variable(s) you set (if known)
Which API you use
What feature/effect it enables in your project
Here is a list of variables we want to put in persistent Constant Buffers. We are also considering supported URP helpers to override some of them when needed.
We’re collecting this to help with this transition and document the right paths or find alternatives for your use cases.
The impact is only related if someone would override these internal shader constants. Reading these Unity provided values in your shaders is fine and is not changed.
Would this impact how we write custom shaders when using them ? I use a lot of those variables in a fair few custom shaders, though i never change their values.
The impact is only related if someone would override these internal shader constants. Reading these Unity provided values in your shaders is fine and is not changed.
(I added this sentence now also to the original message, so that no one misses this important detail. Thanks for asking )
I use them a lot for value that should apply to everything in the scene.
Mainly via Shader.SetGlobalFloat / Integer / Vector / Texture / Color.
I use them to set ambiant lightning (color) for custom Stylized Lightning , Player position (vector4) for things like water or invisible walls that appear when going near them. I use them for impact like shock waves via a global position, radius and intensity. I also use global texture to set tinting color to my shader to set a mood that affect all objects.
Some of thoses need to update every frame, while other just once.
When a system needs to set multiple value at once I try to package everything into arrays of vectors4 and use SetGlobalVectorArray, to reduce the number of set/get calls.
But to clarify, you set your own global variables, and not the Unity / URP internal variables from the list above, right? Because that is the thing, that we basically change them to “read-only”. But the functionality itself to set other (global) variables would be unchanged.
Exactly. I’d say the only one that I WOULD have though to modify was the _Time. I had a case where I was playing with slowMotion and I needed a unscaledTime value in my shader. But I just defaulted to set my own custom variable.
We use this code to modify the view matrix for our VR binoculars implementation, not sure whether this modifies any of the listed variables under the hood.
Of course developers need to override these variables.
Do you even realize that, in many cases, overriding URP/global shader variables is the only way to fix bugs that exist in Unity 6 LTS / Supported releases?
A recent issue we encountered was related to urp_TileData access errors. The root cause was that when rendering directly to a TargetTexture without BlitToBackBuffer, _ScaledScreenParams did not match the actual RenderTexture size.
The only viable workaround was overriding _ScaledScreenParams manually in the middle of the RenderPass pipeline.
You can investigate the exact version details yourselves.
From my experience, nearly every situation where we were forced to modify URP/global shader variables was caused by URP bugs. Not because developers wanted to rely on internal implementation details.
If Unity blocks these access paths while continuing to ship LTS / Supported releases with insufficient QA validation, a lot of capable developers are going to suffer because of it.
If Unity genuinely wants to improve engine performance, then the team seriously needs actual game production experience before making decisions like this.
People without real production experience cannot realistically understand what kinds of performance problems and engine limitations developers are actually dealing with in shipped games.
while Ive not had these issues, there are other engines who provide the code and if you need you can go customise it.. I respect that its your code unity, but, sometimes we need the tweaks for our stuff.. as not all of us should need to pay you more than i earn a year to get the code out of you to do so. Now sure, many of the packages we can make copies and tweaks, but then need to remake those tweaks each time
Maybe it’s relevant how much performance etc this saves. If it’s 1% already for all games, that’s massive. If it’s just 0.001%, I am not too sure how much that helps with reduced flexibility.
Of course people can always modify URP to have control over this
And for my asset to function it is essential to be able to override some of these global variables to do custom rendering commands.
What my plugin does: it makes snapshots of scene objects and then renders these snapshots when objects are far away from the camera. Snapshot creation happens at RUNTIME, it is not baked in the editor.
What variables are overridden using CommandBuffer:
_MainLightShadowParams
_AdditionalLightsCount
_MainLightPosition
_MainLightColor
_MainLightLayerMask
unity_LightData
_WorldSpaceCameraPos
_ProjectionParams
Used in combination with CommandBuffer.DrawRenderer
I am the author of Dynamic Lighting a modernized Lighting System of Unreal Gold / Tournament (1996-1999) for Unity. It’s running on the built-in render pipeline.
When I tried to port my system to URP, I ran into the issue that there’s a presumption that a render feature only wants to keep a global texture assigned:
using (var builder = renderGraph.AddRasterRenderPass<PassData>("MyPass", out var passData))
{
// Set a texture to the global texture
builder.SetGlobalTextureAfterPass(texture, globalTextureID);
}
But my lighting system does not rely on textures, it’s running on Compute Buffers. I need to assign a global compute buffer and render the scene normally with forward rendering:
// Set a buffer to the global buffer
builder.SetGlobalComputeBufferAfterPass(buffer, globalBufferID);
This function does not exist. In fact, it appears that even Shader.SetGlobalComputeBuffer (and their URP equivalents) have no effect during the built-in forward rendering that occurs.
There are also many presumptions that people wish to use the default light sources. There’s a lot of work done behind the scenes binning them, work that my lighting system wouldn’t need performed whatsoever. I consistently run into these issues that BiRP never had. But the lack of buffer support is most important.
Hey all, thanks a lot for providing feedback! This is super useful to us.
As I lead this initiative on engineering side, I can provide additional context.
By moving these global variables into persistent constant buffers, we will reduce main CPU cost in some key areas. For instance, we will speed up shader setup that happens before every draw call or SRP batch. We currently make constant buffers on the fly by gathering these global variables during the setup of the shaders that use them. By having persistent constant buffers, we will fill them once on C# side, and then only bind them during shader setup. This optimization will benefit every URP users.
As mentioned, by setting these variables into persistent constant buffers, you cannot modify them individually through the usual APIs (Shader.SetGlobal*, CommandBuffer.SetGlobal*, Material.Set*, MaterialPropertyBlock, etc.). But we are considering providing new public URP helpers so you can 1/ modify some of these variables (URP will push a custom version of the constant buffer for your pass behind the scenes) and 2/ reset to the default URP constant buffers when needed (at the end of your pass for instance, cheap switch of bindings behind the scenes). We are also considering a transition period with persistent constant buffers as an opt-in feature.
The current discussion helps us prioritizing things and make sure we are on the right track so again, thanks a lot for the feedback. Keep it up!
Hey @MUGIK, that’s a cool asset, well done. Thanks for providing the list of vars that you modify. So you set these vars with custom values using Material APIs, then call cmd.DrawRenderer with your material? I assume within a Render Graph pass? So you are probably using RasterCommandBuffer or UnsafeCommandBuffer?
Hey @PatHightreeXVR, this is fine. XRBuiltinShaderConstants modify the global vars behind the scenes using cmd.SetGlobal APIs, but I think we can switch it to a constant buffer workflow without any impact on your side.
Hi @Henry00IS, thanks for your feedback. Global resources are not optimal with Render Graph where we aim at optimizing memory usage by doing intra-frame memory aliasing, pass culling based on resource usage and so on. Global texture API was brought to facilitate users transition to Render Graph, but this is not a recommended workflow if you can do without, hence the current absence of SetGlobalComputeBufferAfterPass().
Could you have a look at this sample and let me know if it would work for you? The sample is for textures, but the idea would be to add a custom contextItem in frameData where you would store your computeBuffer handle bufferHandle in pass A and then you could call builder.UseBuffer(bufferHandle) in pass B. No global involved.
Shouldn’t this be added already in the main post? It is pretty important to me (and i think to us) to already know than there will also be a way to modify them even with this change, even if it would not be as easy as before. The way the post talk about it seemed like there were no ways of working on it from the outside.