#if UNITY_2023_3_OR_NEWER using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering.Universal; using UnityEngine.XR; namespace JeffGrawAssets.FlexibleUI { public partial class FlexibleBlurPass { class BlurPassData { public PooledListDictionary<(BlurSettings blurSettings, float alpha), List, IBlur> batchedBlurs; public TextureHandle source; public TextureHandle temporaryUIBlurFullScreenHandle; public List instanceHandles = new(); public List textureHandles = new(); public List<(int width, int height)> textureDimensions = new(); public Queue textureIndices = new(); public List blurAreas, blurredImageAreas; public Camera camera; public int featureIdx; public float renderScale, originalWidth, originalHeight; public bool singlePassVR, multiPassVR, rightEye, haveUIBlurAreas, haveBlurredImageAreas, blurredImageLayersSeeLower, blurredImageSeeUIBlurs, uiBlurLayersSeeLower, useComputeShaders, overlayCompatibilityFix; } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { if (!blurLayerAdddedThisFrame && (IndividuallyPaused || FlexibleBlurFeature.GloballyPaused)) return; blurLayerAdddedThisFrame = false; var cameraData = frameData.Get(); if (cameraData.isPreviewCamera || cameraData.isSceneViewCamera) return; var camera = cameraData.camera; var key = (camera, featureIdx); SharedSetup(camera.pixelWidth, camera.pixelHeight, cameraData.renderScale); UIBlur.BlurDict.TryGetValue(key, out var blurAreas); FlexibleBlurFeature.ImageBasedBlurDict.TryGetValue(key, out var blurredImageAreas); var haveUIBlurAreas = blurAreas is { Count: > 0 }; var haveBlurredImageAreas = blurredImageAreas is { Count: > 0 }; Setup(cameraData.camera, cameraData.cameraTargetDescriptor); if (!haveUIBlurAreas && !haveBlurredImageAreas) return; var textureDescriptor = cameraData.cameraTargetDescriptor; textureDescriptor.enableRandomWrite = useComputeShaders; textureDescriptor.msaaSamples = 1; textureDescriptor.depthStencilFormat = GraphicsFormat.None; textureDescriptor.graphicsFormat = blurGraphicsFormat; var (originalWidth, originalHeight) = (textureDescriptor.width, textureDescriptor.height); var renderScale = cameraData.renderScale; #if XR_MANAGEMENT_INSTALLED var singlePassVR = XRSettings.enabled && XRSettings.stereoRenderingMode >= XRSettings.StereoRenderingMode.SinglePassInstanced; var rightEye = XRSettings.enabled && cameraData.xr.multipassId == 1; #else var (rightEye, singlePassVR) = (false, false); #endif using var builder = renderGraph.AddUnsafePass(ProfilerTag, out var passData); (passData.camera, passData.featureIdx, passData.renderScale, passData.originalWidth, passData.originalHeight, passData.singlePassVR, passData.haveUIBlurAreas, passData.haveBlurredImageAreas, passData.blurAreas, passData.blurredImageAreas, passData.batchedBlurs, passData.blurredImageSeeUIBlurs, passData.blurredImageLayersSeeLower, passData.uiBlurLayersSeeLower, passData.useComputeShaders, passData.overlayCompatibilityFix) = ( camera, featureIdx, renderScale, originalWidth, originalHeight, singlePassVR, haveUIBlurAreas, haveBlurredImageAreas, blurAreas, blurredImageAreas, batchedBlurs, blurredImageSeeUIBlurs, blurredImageLayersSeeLower, uiBlurLayersSeeLower, useComputeShaders, overlayCompatibilityFix); #if XR_MANAGEMENT_INSTALLED passData.multiPassVR = XRSettings.enabled && XRSettings.stereoRenderingMode == XRSettings.StereoRenderingMode.MultiPass; #else passData.multiPassVR = false; #endif passData.textureIndices.Clear(); passData.textureHandles.Clear(); passData.textureDimensions.Clear(); passData.instanceHandles.Clear(); passData.source = frameData.Get().activeColorTexture; builder.UseTexture(passData.source, AccessFlags.ReadWrite); foreach (var rtHandle in currentRTHandleList) { var texture = renderGraph.ImportTexture(rtHandle); passData.instanceHandles.Add(texture); builder.UseTexture(texture); } if (blurredImageSeeUIBlurs) { HandleUIBlurs(); HandleBlurredImages(); } else { HandleBlurredImages(); HandleUIBlurs(haveBlurredImageAreas); } builder.SetRenderFunc(static(BlurPassData data, UnsafeGraphContext context) => Pass(data, context)); void HandleUIBlurs(bool useTempRT = false) { if (!haveUIBlurAreas) return; if (useTempRT) { textureDescriptor.width = originalWidth; textureDescriptor.height = originalHeight; passData.temporaryUIBlurFullScreenHandle = UniversalRenderer.CreateRenderGraphTexture(renderGraph, textureDescriptor, nameof(BlurPassData.temporaryUIBlurFullScreenHandle), false, FilterMode.Bilinear); builder.UseTexture(passData.temporaryUIBlurFullScreenHandle); } foreach (var blurArea in blurAreas) AddTemporaryTextures(blurArea.Settings, rightEye ? blurArea.Common.BlurRegionRight : blurArea.Common.BlurRegion, blurArea.Alpha); } void HandleBlurredImages() { if (!haveBlurredImageAreas) return; int currentLayer = blurredImageAreas[0].Layer; int currentPriority = blurredImageAreas[0].Priority; foreach (var blurImage in blurredImageAreas) { if (blurImage.Layer > currentLayer || blurImage.Priority > currentPriority) { TryApplyBatchedBlurs(); currentLayer = blurImage.Layer; currentPriority = blurImage.Priority; } if (blurImage.CanBatch) batchedBlurs.Add((blurImage.Settings, blurImage.Alpha), blurImage); else AddTemporaryTextures(blurImage.Settings, rightEye ? blurImage.Common.BlurRegionRight : blurImage.Common.BlurRegion, blurImage.Alpha); } TryApplyBatchedBlurs(); void TryApplyBatchedBlurs() { foreach (var kvp in batchedBlurs) { var fillEntireRenderTexture = false; foreach (var blur in kvp.Value) { if (!blur.FillEntireRenderTexture) continue; fillEntireRenderTexture = true; break; } if (!fillEntireRenderTexture && kvp.Value.Count == 1) { var blurImage = kvp.Value[0]; AddTemporaryTextures(blurImage.Settings, rightEye ? blurImage.Common.BlurRegionRight : blurImage.Common.BlurRegion, blurImage.Alpha); continue; } float minX, minY, maxX, maxY; if (fillEntireRenderTexture) { (minX, minY, maxX, maxY) = (0, 0, originalWidth, originalHeight); } else { (minX, minY, maxX, maxY) = (float.PositiveInfinity, float.PositiveInfinity, float.NegativeInfinity, float.NegativeInfinity); foreach (var batchedBlur in kvp.Value) { minX = Math.Min(minX, batchedBlur.MinX(rightEye)); minY = Math.Min(minY, batchedBlur.MinY(rightEye)); maxX = Math.Max(maxX, batchedBlur.MaxX(rightEye)); maxY = Math.Max(maxY, batchedBlur.MaxY(rightEye)); if (!singlePassVR) continue; minX = Math.Min(minX, batchedBlur.MinX(true)); minY = Math.Min(minY, batchedBlur.MinY(true)); maxX = Math.Max(maxX, batchedBlur.MaxX(true)); maxY = Math.Max(maxY, batchedBlur.MaxY(true)); } } var blurRegion = UIBlurCommon.ComputeBlurRegion(minX, minY, maxX, maxY); var settings = kvp.Key.blurSettings; AddTemporaryTextures(settings, blurRegion, kvp.Key.alpha); } batchedBlurs.Clear(); } } void AddTemporaryTextures(BlurSettings settings, Vector4 blurRegion, float alpha) { blurRegion *= renderScale; var handles = passData.textureHandles; var dimensions = passData.textureDimensions; var indices = passData.textureIndices; var currentTextureIdx = 0; var aspect = blurRegion.z / blurRegion.w; var scale = renderScale * (settings.referenceResolution > 0 ? (float)settings.referenceResolution / originalHeight : 1f); textureDescriptor.height = Mathf.Max(1, Mathf.RoundToInt(scale * blurRegion.w)); textureDescriptor.width = Mathf.Max(1, Mathf.RoundToInt(textureDescriptor.height * aspect)); var referenceHeightForDownScale = textureDescriptor.height; FindExistingHandleOrAddNew(); bool temp2NeedsInit = true; if (alpha <= 0) return; int totalIterations = 0; foreach (var section in settings.downscaleSections) { var iterations = section.iterations; var (isSeparable, _, _, _, _) = section.GetSectionBehaviour(); for (int i = 0; i < iterations; i++, totalIterations++) { textureDescriptor.height = Mathf.Max(1, Mathf.RoundToInt(referenceHeightForDownScale / Mathf.Pow(1 + alpha, totalIterations + 1))); textureDescriptor.width = Mathf.Max(1, Mathf.RoundToInt(textureDescriptor.height * aspect)); FindExistingHandleOrAddNew(); if (!isSeparable) { temp2NeedsInit = true; continue; } temp2NeedsInit = false; FindExistingHandleOrAddNew(); } } if (temp2NeedsInit) FindExistingHandleOrAddNew(); void FindExistingHandleOrAddNew() { const string textureName = "blurTexture"; while (true) { if (currentTextureIdx >= handles.Count) { var newHandle = UniversalRenderer.CreateRenderGraphTexture(renderGraph, textureDescriptor, textureName, false, FilterMode.Bilinear); dimensions.Add((textureDescriptor.width, textureDescriptor.height)); handles.Add(newHandle); builder.UseTexture(newHandle, AccessFlags.ReadWrite); indices.Enqueue(currentTextureIdx++); return; } var existingDimensions = dimensions[currentTextureIdx]; if (existingDimensions.width == textureDescriptor.width && existingDimensions.height == textureDescriptor.height) { indices.Enqueue(currentTextureIdx++); return; } currentTextureIdx++; } } } } private static void Pass(BlurPassData data, UnsafeGraphContext context) { var cmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd); var (textureHandle, textureDimensions, textureIndices, camera, featureIdx, renderScale, originalWidth, originalHeight, singlePassVR, multiPassVR, rightEye, haveUIBlurAreas, haveBlurredImageAreas, blurAreas, blurredImageAreas, batchedBlurs, blurredImageLayersSeeLower, blurredImageSeeUIBlurs, uiBlurLayersSeeLower, useComputeShaders, overlayCompatibilityFix) = (data.textureHandles, data.textureDimensions, data.textureIndices, data.camera, data.featureIdx, data.renderScale, data.originalWidth, data.originalHeight, data.singlePassVR, data.multiPassVR, data.rightEye, data.haveUIBlurAreas, data.haveBlurredImageAreas, data.blurAreas, data.blurredImageAreas,data.batchedBlurs, data.blurredImageLayersSeeLower, data.blurredImageSeeUIBlurs, data.uiBlurLayersSeeLower, data.useComputeShaders, data.overlayCompatibilityFix); var destinationRequiresClear = haveUIBlurAreas && (!haveBlurredImageAreas || blurredImageSeeUIBlurs); var key = (camera, featureIdx); if (blurredImageSeeUIBlurs) { HandleUIBlurs(); HandleBlurredImages(); } else { HandleBlurredImages(); HandleUIBlurs(haveBlurredImageAreas); } void HandleUIBlurs(bool useTempRT = false) { if (!haveUIBlurAreas) return; TextureHandle layerHandle; if (useTempRT) { destinationRequiresClear = !blurredImageSeeUIBlurs; layerHandle = data.temporaryUIBlurFullScreenHandle; } else { layerHandle = data.instanceHandles[0]; } if (uiBlurLayersSeeLower) { int currentLayer = blurAreas[0].Layer; foreach (var blur in blurAreas) { if (blur.Layer > currentLayer) { currentLayer = blur.Layer; FullScreenBlit(cmd, layerHandle, data.source, fullScreenBlitsMat, 1); } ApplyBlurRenderGraph(blur, data.source, layerHandle); } } else { foreach (var x in blurAreas) ApplyBlurRenderGraph(x, data.source, layerHandle); } if (!destinationRequiresClear) FullScreenBlit(cmd, layerHandle, data.source, fullScreenBlitsMat, 1); } void HandleBlurredImages() { if (!haveBlurredImageAreas) return; var numImageLayers = FlexibleBlurFeature.ImageBasedLayersPerCameraDict[key]; var source = data.source; var destinationIdx = 0; var destination = data.instanceHandles[destinationIdx]; if (blurredImageLayersSeeLower && destinationIdx < numImageLayers - 1) { cmd.SetRenderTarget(destination); cmd.ClearRenderTarget(false, true, Color.clear); cmd.SetRenderTarget(source); FullScreenBlit(cmd, source, destination, fullScreenBlitsMat); } var currentLayer = blurredImageAreas[0].Layer; var currentPriority = blurredImageAreas[0].Priority; foreach (var blurImage in blurredImageAreas) { if (blurImage.Layer > currentLayer || blurImage.Priority > currentPriority) { TryApplyBatchedBlurs(); if (blurImage.Layer > currentLayer) { destination = data.instanceHandles[++destinationIdx]; if (blurredImageLayersSeeLower) { source = data.instanceHandles[destinationIdx - 1]; cmd.SetRenderTarget(source); if (destinationIdx < numImageLayers - 1) { FullScreenBlit(cmd, source, destination, fullScreenBlitsMat); } } } currentLayer = blurImage.Layer; currentPriority = blurImage.Priority; } if (blurImage.CanBatch) batchedBlurs.Add((blurImage.Settings, blurImage.Alpha), blurImage); else ApplyBlurRenderGraph(blurImage, source, destination); } TryApplyBatchedBlurs(); if (blurredImageLayersSeeLower) cmd.SetRenderTarget(data.source); void TryApplyBatchedBlurs() { foreach (var kvp in batchedBlurs) { var fillEntireRenderTexture = false; foreach (var blur in kvp.Value) { if (!blur.FillEntireRenderTexture) continue; fillEntireRenderTexture = true; break; } if (!fillEntireRenderTexture && kvp.Value.Count == 1) { ApplyBlurRenderGraph(kvp.Value[0], source, destination); continue; } float minX, minY, maxX, maxY; if (fillEntireRenderTexture) { (minX, minY, maxX, maxY) = (0, 0, originalWidth, originalHeight); } else { (minX, minY, maxX, maxY) = (float.PositiveInfinity, float.PositiveInfinity, float.NegativeInfinity, float.NegativeInfinity); foreach (var batchedBlur in kvp.Value) { minX = Math.Min(minX, batchedBlur.MinX(rightEye)); minY = Math.Min(minY, batchedBlur.MinY(rightEye)); maxX = Math.Max(maxX, batchedBlur.MaxX(rightEye)); maxY = Math.Max(maxY, batchedBlur.MaxY(rightEye)); } } if (destinationRequiresClear) { cmd.SetRenderTarget(destination); cmd.ClearRenderTarget(false, true, Color.clear); cmd.SetRenderTarget(source); destinationRequiresClear = false; } var settings = kvp.Key.blurSettings; if (singlePassVR) { // Left eye extents already calculated. Min/Max that with the right eye values to get a region that covers both eyes. foreach (var batchedBlur in kvp.Value) { minX = Math.Min(minX, batchedBlur.MinX(true)); minY = Math.Min(minY, batchedBlur.MinY(true)); maxX = Math.Max(maxX, batchedBlur.MaxX(true)); maxY = Math.Max(maxY, batchedBlur.MaxY(true)); } } var blurRegion = UIBlurCommon.ComputeBlurRegion(minX, minY, maxX, maxY); ApplyBlurUnifiedRenderGraph(source, destination, settings, blurRegion, singlePassVR ? blurRegion : null, null, null, kvp.Key.alpha, false, Matrix4x4.identity); } batchedBlurs.Clear(); } } void ApplyBlurRenderGraph(IBlur iBlur, RenderTargetIdentifier source, RenderTargetIdentifier destination) { if (!iBlur.HasVisiblePixels(rightEye)) return; if (destinationRequiresClear) { cmd.SetRenderTarget(new RenderTargetIdentifier(destination, 0, CubemapFace.Unknown, -1)); cmd.ClearRenderTarget(false, true, Color.clear); cmd.SetRenderTarget(source); destinationRequiresClear = false; } if (singlePassVR) { var (regionRight, cornersRight, region, corners) = (iBlur.Common.BlurRegionRight, iBlur.Common.ScreenCornersRight, iBlur.Common.BlurRegion, iBlur.Common.ScreenCorners); ApplyBlurUnifiedRenderGraph(source, destination, iBlur.Settings, region, regionRight, corners, cornersRight, iBlur.Alpha, iBlur.IsAngled, iBlur.Matrix, iBlur.Common.WorldCamera); } else { var (region, corners) = rightEye ? (iBlur.Common.BlurRegionRight, iBlur.Common.ScreenCornersRight) : (iBlur.Common.BlurRegion, iBlur.Common.ScreenCorners); ApplyBlurUnifiedRenderGraph(source, destination, iBlur.Settings, region, null, corners, null, iBlur.Alpha, iBlur.IsAngled, iBlur.Matrix, iBlur.Common.WorldCamera); } } void ApplyBlurUnifiedRenderGraph(RenderTargetIdentifier source, RenderTargetIdentifier destination, BlurSettings settings, Vector4 blurRegion, Vector4? blurRegionRight, Vector4[] blurCorners, Vector4[] blurCornersRight, float alpha, bool isAngled, Matrix4x4 transformationMatrix, Camera uiCamera = null) { blurRegion *= renderScale; var hasRightEye = blurRegionRight.HasValue; if (hasRightEye) { blurRegionRight *= renderScale; var scaleFactorRight = new Vector2(originalWidth / blurRegionRight.Value.z, originalHeight / blurRegionRight.Value.w); cmd.SetGlobalVector(ScaleFactorRightID, scaleFactorRight); var offsetRight = scaleFactorRight * new Vector2(blurRegionRight.Value.x / originalWidth, blurRegionRight.Value.y / originalHeight); cmd.SetGlobalVector(SourceOffsetRightID, offsetRight); } var handleIdx = textureIndices.Dequeue(); var temp1 = textureHandle[handleIdx]; var temp2 = temp1; var temp1Dimensions = textureDimensions[handleIdx]; var temp2Dimensions = temp1Dimensions; var scaleFactor = new Vector2(originalWidth / blurRegion.z, originalHeight / blurRegion.w); var offset = scaleFactor * new Vector2(blurRegion.x / originalWidth, blurRegion.y / originalHeight); cmd.SetGlobalVector(ScaleFactorID, scaleFactor); cmd.SetGlobalVector(SourceOffsetID, offset); FullScreenBlit(cmd, source, temp1, regionalBlitsMat, Convert.ToInt32(settings.hqResample)); if (alpha > 0) { if (useComputeShaders) ComputeBlur(); else TraditionalBlur(); } FinalBlitToDestination(); void ComputeBlur() { int totalIterations = 0; var threadGroupsX = (temp1Dimensions.width + ThreadGroupSizeX - 1) / ThreadGroupSizeX; var threadGroupsY = (temp1Dimensions.height + ThreadGroupSizeY - 1) / ThreadGroupSizeY; var computeShader = hasRightEye ? vrComputeBlurs : computeBlurs; bool temp2NeedsInit = true; if (alpha > 0) { cmd.SetComputeIntParam(computeShader, ComputeOffsetCenterID, 0); foreach (var section in settings.downscaleSections) { var (isSeparable, setSamplesPerSide, skip, firstKernelIdx, secondKernelIdx) = section.GetSectionBehaviour(); if (skip) continue; if (setSamplesPerSide) { cmd.SetComputeIntParam(computeShader, TapsPerSideHorComputeID, section.horizontalSamplesPerSide); cmd.SetComputeIntParam(computeShader, TapsPerSideVertComputeID, section.verticalSamplesPerSide); } var iterations = section.iterations; var baseSampleDistance = section.sampleDistance; var sampleOffset = 1f; for (int i = 0; i < iterations; i++, totalIterations++) { cmd.SetComputeIntParam(computeShader, ComputeBlurIterationID, i); sampleOffset *= 0.5f; var evenIter = i % 2 == 0; var lastPass = i == iterations - 1; cmd.SetComputeFloatParam(computeShader, ComputeSampleOffsetID, !evenIter ? -sampleOffset : lastPass ? 0 : sampleOffset); handleIdx = textureIndices.Dequeue(); temp2 = textureHandle[handleIdx]; temp2Dimensions = textureDimensions[handleIdx]; cmd.SetComputeVectorParam(computeShader, ComputeResultDimensionsID, new Vector2(temp2Dimensions.width, temp2Dimensions.height)); cmd.SetComputeFloatParam(computeShader, ComputeSampleDistID, baseSampleDistance * renderScale); cmd.SetComputeTextureParam(computeShader, firstKernelIdx, ComputeSourceID, temp1); cmd.SetComputeTextureParam(computeShader, firstKernelIdx, ComputeResultID, temp2); threadGroupsX = (temp1Dimensions.width + ThreadGroupSizeX - 1) / ThreadGroupSizeX; threadGroupsY = (temp1Dimensions.height + ThreadGroupSizeY - 1) / ThreadGroupSizeY; cmd.DispatchCompute(computeShader, firstKernelIdx, threadGroupsX, threadGroupsY, 1); if (!isSeparable) { temp2NeedsInit = true; (temp1, temp1Dimensions, temp2, temp2Dimensions) = (temp2, temp2Dimensions, temp1, temp1Dimensions); continue; } temp2NeedsInit = false; handleIdx = textureIndices.Dequeue(); temp1 = textureHandle[handleIdx]; temp1Dimensions = textureDimensions[handleIdx]; cmd.SetComputeTextureParam(computeShader, secondKernelIdx, ComputeSourceID, temp2); cmd.SetComputeTextureParam(computeShader, secondKernelIdx, ComputeResultID, temp1); cmd.DispatchCompute(computeShader, secondKernelIdx, threadGroupsX, threadGroupsY, 1); } } } if (temp2NeedsInit) { handleIdx = textureIndices.Dequeue(); temp2 = textureHandle[handleIdx]; temp2Dimensions = textureDimensions[handleIdx]; cmd.SetComputeVectorParam(computeShader, ComputeResultDimensionsID, new Vector2(temp2Dimensions.width, temp2Dimensions.height)); } totalIterations = 0; cmd.SetComputeIntParam(computeShader, ComputeOffsetCenterID, 1); foreach (var section in settings.blurSections) { var (isSeparable, setSamplesPerSide, skip, firstKernelIdx, secondKernelIdx) = section.GetSectionBehaviour(); if (skip) continue; if (setSamplesPerSide) { cmd.SetComputeIntParam(computeShader, TapsPerSideHorComputeID, section.horizontalSamplesPerSide); cmd.SetComputeIntParam(computeShader, TapsPerSideVertComputeID, section.verticalSamplesPerSide); } var iterations = section.iterations; var baseSampleDistance = section.sampleDistance; if (!isSeparable) { for (int i = 0; i < iterations; i++, totalIterations++) { var evenIter = i % 2 == 0; if (i == iterations - 1 && evenIter) cmd.SetComputeFloatParam(computeShader, ComputeSampleOffsetID, 0); else cmd.SetComputeFloatParam(computeShader, ComputeSampleOffsetID, evenIter ? 0.5f : -0.5f); cmd.SetComputeIntParam(computeShader, ComputeBlurIterationID, i); var sampleDistance = alpha * (baseSampleDistance + settings.blurAdditionalDistancePerIteration * totalIterations); cmd.SetComputeFloatParam(computeShader, ComputeSampleDistID, sampleDistance * renderScale); cmd.SetComputeTextureParam(computeShader, firstKernelIdx, ComputeSourceID, temp1); cmd.SetComputeTextureParam(computeShader, firstKernelIdx, ComputeResultID, temp2); cmd.DispatchCompute(computeShader, firstKernelIdx, threadGroupsX, threadGroupsY, 1); (temp1, temp1Dimensions, temp2, temp2Dimensions) = (temp2, temp2Dimensions, temp1, temp1Dimensions); } } else { cmd.SetComputeTextureParam(computeShader, firstKernelIdx, ComputeSourceID, temp1); cmd.SetComputeTextureParam(computeShader, firstKernelIdx, ComputeResultID, temp2); cmd.SetComputeTextureParam(computeShader, secondKernelIdx, ComputeSourceID, temp2); cmd.SetComputeTextureParam(computeShader, secondKernelIdx, ComputeResultID, temp1); for (int i = 0; i < iterations; i++, totalIterations++) { var evenIter = i % 2 == 0; if (i == iterations - 1 && evenIter) cmd.SetComputeFloatParam(computeShader, ComputeSampleOffsetID, 0); else cmd.SetComputeFloatParam(computeShader, ComputeSampleOffsetID, evenIter ? 0.5f : -0.5f); var sampleDistance = alpha * (baseSampleDistance + settings.blurAdditionalDistancePerIteration * totalIterations); cmd.SetComputeFloatParam(computeShader, ComputeSampleDistID, sampleDistance * renderScale); cmd.DispatchCompute(computeShader, firstKernelIdx, threadGroupsX, threadGroupsY, 1); cmd.DispatchCompute(computeShader, secondKernelIdx, threadGroupsX, threadGroupsY, 1); } } } } void TraditionalBlur() { int totalIterations = 0; bool temp2NeedsInit = true; if (alpha > 0) { cmd.SetGlobalInt(OffsetCenterID, 0); foreach (var section in settings.downscaleSections) { var (isSeparable, setSamplesPerSide, _, firstKernelIdx, secondKernelIdx) = section.GetSectionBehaviour(); if (setSamplesPerSide) { cmd.SetGlobalInt(TapsPerSideHorID, section.horizontalSamplesPerSide); cmd.SetGlobalInt(TapsPerSideVertID, section.verticalSamplesPerSide); } var iterations = section.iterations; var baseSampleDistance = section.sampleDistance; var sampleOffset = 1f; for (int i = 0; i < iterations; i++, totalIterations++) { cmd.SetGlobalInt(BlurIterationID, i); sampleOffset *= 0.5f; var evenIter = i % 2 == 0; if (i == iterations - 1 && evenIter) cmd.SetGlobalFloat(SampleOffsetID, 0); else cmd.SetGlobalFloat(SampleOffsetID, evenIter ? sampleOffset : -sampleOffset); handleIdx = textureIndices.Dequeue(); temp2 = textureHandle[handleIdx]; temp2Dimensions = textureDimensions[handleIdx]; cmd.SetGlobalFloat(BlurSampleDistID, baseSampleDistance * renderScale); FullScreenBlit(cmd, temp1, temp2, blursMat, firstKernelIdx); if (!isSeparable) { temp2NeedsInit = true; (temp1, temp1Dimensions, temp2, temp2Dimensions) = (temp2, temp2Dimensions, temp1, temp1Dimensions); continue; } temp2NeedsInit = false; handleIdx = textureIndices.Dequeue(); temp1 = textureHandle[handleIdx]; temp1Dimensions = textureDimensions[handleIdx]; FullScreenBlit(cmd, temp2, temp1, blursMat, secondKernelIdx); } } } if (temp2NeedsInit) temp2 = textureHandle[textureIndices.Dequeue()]; totalIterations = 0; cmd.SetGlobalInt(OffsetCenterID, 1); foreach (var section in settings.blurSections) { var (isSeparable, setSamplesPerSide, skip, firstKernelIdx, secondKernelIdx) = section.GetSectionBehaviour(); if (skip) continue; if (setSamplesPerSide) { cmd.SetGlobalInt(TapsPerSideHorID, section.horizontalSamplesPerSide); cmd.SetGlobalInt(TapsPerSideVertID, section.verticalSamplesPerSide); } var baseSampleDistance = section.sampleDistance; var iterations = section.iterations; for (int i = 0; i < iterations; i++, totalIterations++) { cmd.SetGlobalInt(BlurIterationID, i); var evenIter = i % 2 == 0; if (i == iterations - 1 && evenIter) cmd.SetGlobalFloat(SampleOffsetID, 0); else cmd.SetGlobalFloat(SampleOffsetID, evenIter ? 0.5f : -0.5f); var sampleDistance = alpha * (baseSampleDistance + settings.blurAdditionalDistancePerIteration * totalIterations); cmd.SetGlobalFloat(BlurSampleDistID, sampleDistance * renderScale); FullScreenBlit(cmd, temp1, temp2, blursMat, firstKernelIdx); if (!isSeparable) { (temp1, temp2) = (temp2, temp1); continue; } FullScreenBlit(cmd, temp2, temp1, blursMat, secondKernelIdx); } } } void FinalBlitToDestination() { cmd.SetGlobalFloat(DitherStrengthID, alpha * settings.ditherStrength); cmd.SetGlobalVector(DestinationRegionSizeID, blurRegion); if (hasRightEye) cmd.SetGlobalVector(DestinationRegionSizeRightID, blurRegionRight.Value); cmd.SetGlobalFloat(VibrancyID, (alpha * settings.vibrancy + 1) * 0.5f); cmd.SetGlobalFloat(BrightnessID, alpha * settings.brightness); cmd.SetGlobalFloat(ContrastID, alpha * settings.contrast + 1); cmd.SetGlobalVector(TintID, alpha * settings.tint); var useQuadBlit = transformationMatrix != Matrix4x4.identity && (!overlayCompatibilityFix || uiCamera != null); if (useQuadBlit) { if (multiPassVR) { BlitToQuad(cmd, temp1, destination, quadBlitsMat, transformationMatrix, blurRegion, blurRegionRight, originalWidth, originalHeight, 0); } else { cmd.SetProjectionMatrix(uiCamera?.projectionMatrix ?? OverlayUIProjectionMatrix); cmd.SetViewMatrix(uiCamera?.worldToCameraMatrix ?? Matrix4x4.identity); BlitToQuad(cmd, temp1, destination, quadBlitsMat, transformationMatrix, blurRegion, blurRegionRight, originalWidth, originalHeight, 0); cmd.SetProjectionMatrix(camera.projectionMatrix); cmd.SetViewMatrix(camera.worldToCameraMatrix); } } else { if (isAngled) { cmd.SetGlobalFloat(RenderScaleID, renderScale); cmd.SetGlobalVectorArray(CornersID, blurCorners); if (hasRightEye) cmd.SetGlobalVectorArray(CornersRightID, blurCornersRight); } BlitToRegion(cmd, temp1, destination, regionalBlitsMat, blurRegion, originalWidth, originalHeight, isAngled ? 3 : 2); } cmd.SetRenderTarget(data.source); } } } } } #endif