Hi everyone,
I’m trying to implement a Vulkan subpass for color correction as described in the Meta documentation (link to article). My goal is to apply color correction while the render texture is still in tile memory to avoid unnecessary load/store operations between passes. I’m running this on Meta Quest 2 using Unity 2022.3.17f and URP Core RP version 14.0.9.
The Problem
When I enable my custom render pass, I get the following error:
Assertion failed
UnityEngine.Rendering.Blitter:BlitCameraTexture
VulkanColorCorrectionFeature/VulkanColorCorrectionPass:Execute (at Assets/Scripts/Rendering/VulkanColorCorrectionFeature.cs:38)
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
It seems like something is wrong with how I’m setting up the render target or handling the camera color input. The Frame Debugger shows that when I enable my render pass the rendering pipeline fails right at the beginning, and notthing gets executed.
What I’ve Tried
-
Custom Render Pass:
I’ve written aScriptableRendererFeature
with a custom render pass. The pass attempts to useBlitter.BlitCameraTexture
to apply the material with my color correction shader. Here’s the core part of myExecute
method:public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { CommandBuffer cmd = CommandBufferPool.Get("Color Correction Buffer"); using (new ProfilingScope(cmd, new ProfilingSampler("Vulkan Color Correction"))) { RTHandle colorTarget = renderingData.cameraData.renderer.cameraColorTargetHandle; Blitter.BlitCameraTexture(cmd, colorTarget, colorTarget, 0, material); } context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); }
-
Shader:
My shader is a basic color correction pass using_CameraOpaqueTexture
:TEXTURE2D_X(_CameraOpaqueTexture); SAMPLER(sampler_CameraOpaqueTexture); half4 frag(Varyings input) : SV_Target { float4 color = SAMPLE_TEXTURE2D_X(_CameraOpaqueTexture, sampler_CameraOpaqueTexture, input.uv); return color * float4(1, 1, 1, 1); }
-
Render Pass Event:
I’ve tried several injection points likeAfterRenderingPostProcessing
andBeforeRenderingPostProcessing
, but the error persists.
What I Suspect
- I might be incorrectly handling the render target (
cameraColorTargetHandle
). Blitter.BlitCameraTexture
could be the wrong approach for this use case in XR.- I may need to use
DrawProcedural
instead ofBlitCameraTexture
, as the article suggests explicitly avoidingload
operations.
My Questions
- How do I correctly set up the render target for Vulkan subpasses in URP?
I want to ensure the color correction happens in tile memory and avoid load/store to RAM. - Is
Blitter.BlitCameraTexture
compatible with Vulkan subpasses on XR, or should I switch toDrawProcedural
? - Are there specific XR-related considerations I’m missing when handling render targets or texture arrays?
Any guidance, examples, or relevant documentation links would be greatly appreciated!
Environment Details
- Target Platform: Meta Quest 2
- Unity Version: 2022.3.17f
- URP Core RP Version: 14.0.9
Full Code
Custom Render Feature
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class VulkanColorCorrectionFeature : ScriptableRendererFeature {
public Material colorCorrectionMaterial;
public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
private VulkanColorCorrectionPass colorCorrectionPass;
public override void Create() {
colorCorrectionPass = new VulkanColorCorrectionPass(colorCorrectionMaterial, renderPassEvent);
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) {
if (colorCorrectionMaterial == null) {
Debug.LogWarning("VulkanColorCorrectionFeature: Missing material. Pass will not execute.");
return;
}
if (renderingData.cameraData.cameraType == CameraType.Game) {
renderer.EnqueuePass(colorCorrectionPass);
}
}
class VulkanColorCorrectionPass : ScriptableRenderPass {
private Material material;
public VulkanColorCorrectionPass(Material material, RenderPassEvent renderPassEvent) {
this.material = material;
this.renderPassEvent = renderPassEvent;
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) {
CommandBuffer cmd = CommandBufferPool.Get("Color Correction Buffer");
using (new ProfilingScope(cmd, new ProfilingSampler("Vulkan Color Correction"))) {
RTHandle colorTarget = renderingData.cameraData.renderer.cameraColorTargetHandle;
Blitter.BlitCameraTexture(cmd, colorTarget, colorTarget, 0, material);
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
}
}
Shader Code
Shader "ColorBlit" {
SubShader {
Tags { "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline"}
LOD 100
ZWrite Off Cull Off
Pass {
Name "ColorBlitPass"
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct Attributes {
float4 positionHCS : POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings {
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_OUTPUT_STEREO
};
Varyings vert(Attributes input) {
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
output.positionCS = float4(input.positionHCS.xyz, 1.0);
output.uv = input.uv;
return output;
}
TEXTURE2D_X(_CameraOpaqueTexture);
SAMPLER(sampler_CameraOpaqueTexture);
half4 frag (Varyings input) : SV_Target {
float4 color = SAMPLE_TEXTURE2D_X(_CameraOpaqueTexture, sampler_CameraOpaqueTexture, input.uv);
return color * float4(1, 1, 1, 1);
}
ENDHLSL
}
}
}
Thanks in advance for any help!