TMPro, Unity UI shaders & XR Single Pass Instanced

After being forced to update to 2022.1.16f1 URP 13.1.8 which comes paired with TextMeshPro 3.0.6 & Unity UI 1.0.1 (no other options in the package manager) I've noticed that the default UI's shaders don't correctly support Single Pass Instanced XR rendering in a world space UI.

Although I swear they were functioning normally on Wednesday.

To test I created a new canvas with single Image element, set it to world space, linked the XR camera and pressed play. The UI elements only rendered in the left eye and were transformed weirdly/moved about when I turned my head. I then created a new material, selected Universal Render Pipeline/2D/Sprite-Unlit shader, and applied it to the image element's material slot which instantly fixed the rendering.

  • Is there a hidden update that addresses XR SPI support for the default UI elements that I'm unaware of?
  • If not, where can I find the default UI shader that's used in the editor so I can fix it and avoid having to apply a new material to every one of a few hundred UI elements or have my changes to the package overwritten due to package immutability.

Edit: I tried re-importing the essential assets, to no effect, as well as setting the package manifest to reference 3.2.0-pre.3 then re-importing again. It's odd as I can see several of the appropriate unity macros in the shaders source code.

Side note, the user guide included with the package appears to still reference V1.0.54

1 Like

For future searchers, the thread with an example patched shader ( ) is now rather out of date. To that end, and until @Stephan_B gets the opportunity to port TMPro to URP's HLSL paradigm, the following steps should be performed on the TMPro shaders you use in your URP Single Pass Instanced (SPI) application world space UI's.

NOTE: While I haven't had a chance to verify a full build, the following changes work in the editor, and during play mode.

  • Change all instances of fixed4 to half4
  • Define your render pipeline using SubShader tags. "RenderPipeline" = "UniversalPipeline"
  • Give your passes a name. Name "My Modified TMPro Shader Pass"
  • Change instance of CGPROGRAM to HLSLPROGRAM
  • Change your default includes to URP variants. #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
  • Update your vert struct to an Attributes struct. and add the SPI macros as per the docs.
  • Spoiler A*
  • Update your frag struct to an Varyings struct. and add the SPI macros as per the docs.
  • Spoiler B*

  • Update your vert function to initialize your output, and add the 3 SPI macros as per the docs.

  • Spoiler C*

  • Update the CG Program helpers to the appropriate HLSL Program Helpers

  • Spoiler C*

  • Boost the sharpness value used in the scale calculation. I found that =10 worked for me, your results will vary and you may benefit from a dynamic math value instead.

  • Spoiler C*

  • Add the Instance Id macro to your frag shader

  • Spoiler D*

  • Wrap the uniforms in the TMPro_Properties.cginc file in a CBUFFER block to enable the SPR batcher to do it's job. missed uniforms will be indicated in the inspector when you click on the shader.

  • Spoiler E*


struct Attributes {


struct Varyings {


Varyings vert(Attributes i) {
Varyings o = (Varyings)0;

//Examples of the most common helpers that changed, for more information on other helpers, review the ARM migration guide, or Cyanilux's guide.
UnityObjectToClipPos(vert) became TransformObjectToHClip(
UnityObjectToWorldNormal( became TransformObjectToWorldNormal(
WorldSpaceViewDir(vert) became GetWorldSpaceViewDir(

//You will likely need to improve the sharpness under HLSL math
scale *= abs(input.texcoord1.y) * _GradientScale * (_Sharpness + 10);


half4 frag(Varyings i: SV_Target{


Uniform Block for SRP Batcher:
uniform float _FaceDilate;
//Put the rest of them here, sampler's don't appear to need to e wrapped for per instance batching.

Hopefully this helps others who's installs weren't fixed by just re-importing the essentials.

Edit: fixed a stupid error x2.