diff --git a/Source/Editor/Windows/GraphicsQualityWindow.cs b/Source/Editor/Windows/GraphicsQualityWindow.cs index 957d1af1a..932d8e6d7 100644 --- a/Source/Editor/Windows/GraphicsQualityWindow.cs +++ b/Source/Editor/Windows/GraphicsQualityWindow.cs @@ -113,6 +113,17 @@ namespace FlaxEditor.Windows set => MainRenderTask.Instance.RenderingPercentage = value; } + [NoSerialize, DefaultValue(RenderingUpscaleLocation.AfterAntiAliasingPass), VisibleIf(nameof(UpscaleLocation_Visible))] + [EditorOrder(1401), EditorDisplay("Quality")] + [Tooltip("The image resolution upscale location within rendering pipeline.")] + public RenderingUpscaleLocation UpscaleLocation + { + get => MainRenderTask.Instance.UpscaleLocation; + set => MainRenderTask.Instance.UpscaleLocation = value; + } + + private bool UpscaleLocation_Visible => MainRenderTask.Instance.RenderingPercentage < 1.0f; + [NoSerialize, DefaultValue(1.0f), Limit(0, 1)] [EditorOrder(1500), EditorDisplay("Quality"), Tooltip("The global density scale for all foliage instances. The default value is 1. Use values from range 0-1. Lower values decrease amount of foliage instances in-game. Use it to tweak game performance for slower devices.")] public float FoliageDensityScale diff --git a/Source/Engine/Graphics/RenderTask.h b/Source/Engine/Graphics/RenderTask.h index 92a48cf76..4df61c9d0 100644 --- a/Source/Engine/Graphics/RenderTask.h +++ b/Source/Engine/Graphics/RenderTask.h @@ -182,6 +182,22 @@ API_ENUM(Attributes="Flags") enum class ActorsSources DECLARE_ENUM_OPERATORS(ActorsSources); +/// +/// The Post Process effect rendering location within the rendering pipeline. +/// +API_ENUM() enum class RenderingUpscaleLocation +{ + /// + /// The up-scaling happens directly to the output buffer (backbuffer) after post processing and anti-aliasing. + /// + AfterAntiAliasingPass = 0, + + /// + /// The up-scaling happens before the post processing after scene rendering (after geometry, lighting, volumetrics, transparency and SSR/SSAO). + /// + BeforePostProcessingPass = 1, +}; + /// /// Render task which draws scene actors into the output buffer. /// @@ -244,6 +260,11 @@ public: /// API_FIELD() float RenderingPercentage = 1.0f; + /// + /// The image resolution upscale location within rendering pipeline. Unused if RenderingPercentage is 1. + /// + API_FIELD() RenderingUpscaleLocation UpscaleLocation = RenderingUpscaleLocation::AfterAntiAliasingPass; + public: /// /// The custom set of actors to render. diff --git a/Source/Engine/Renderer/ColorGradingPass.cpp b/Source/Engine/Renderer/ColorGradingPass.cpp index d9fb645de..0bbc4cce3 100644 --- a/Source/Engine/Renderer/ColorGradingPass.cpp +++ b/Source/Engine/Renderer/ColorGradingPass.cpp @@ -205,11 +205,6 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext) context->SetRenderTarget(lut->View()); context->DrawFullscreenTriangle(); } - - // TODO: this could run in async during scene rendering or sth - - const Viewport viewport = renderContext.Task->GetViewport(); - context->SetViewportAndScissors(viewport); context->UnBindSR(0); return lut; diff --git a/Source/Engine/Renderer/MotionBlurPass.cpp b/Source/Engine/Renderer/MotionBlurPass.cpp index 301d0a3eb..f47132794 100644 --- a/Source/Engine/Renderer/MotionBlurPass.cpp +++ b/Source/Engine/Renderer/MotionBlurPass.cpp @@ -265,8 +265,8 @@ void MotionBlurPass::Render(RenderContext& renderContext, GPUTexture*& input, GP ASSERT(motionVectors); auto context = GPUDevice::Instance->GetMainContext(); MotionBlurSettings& settings = renderContext.List->Settings.MotionBlur; - const int32 screenWidth = renderContext.Buffers->GetWidth(); - const int32 screenHeight = renderContext.Buffers->GetHeight(); + const int32 screenWidth = input->Width(); + const int32 screenHeight = input->Height(); const int32 motionVectorsWidth = screenWidth / static_cast(settings.MotionVectorsResolution); const int32 motionVectorsHeight = screenHeight / static_cast(settings.MotionVectorsResolution); if ((renderContext.View.Flags & ViewFlags::MotionBlur) == 0 || diff --git a/Source/Engine/Renderer/PostProcessingPass.cpp b/Source/Engine/Renderer/PostProcessingPass.cpp index 4e75e21fd..7a31c53bb 100644 --- a/Source/Engine/Renderer/PostProcessingPass.cpp +++ b/Source/Engine/Renderer/PostProcessingPass.cpp @@ -482,8 +482,7 @@ void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input, context->BindSR(7, colorGradingLutView); // Composite final frame during single pass (done in full resolution) - auto viewport = renderContext.Task->GetViewport(); - context->SetViewportAndScissors(viewport); + context->SetViewportAndScissors((float)output->Width(), (float)output->Height()); context->SetRenderTarget(*output); context->SetState(_psComposite.Get(compositePermutationIndex)); context->DrawFullscreenTriangle(); diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 1eb2c3a10..9396069f7 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -122,12 +122,10 @@ void RendererService::Dispose() SAFE_DELETE_GPU_RESOURCE(IMaterial::BindParameters::PerViewConstants); } -void RenderAntiAliasingPass(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output) +void RenderAntiAliasingPass(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output, const Viewport& outputViewport) { auto context = GPUDevice::Instance->GetMainContext(); - auto screenSize = renderContext.View.ScreenSize; - context->SetViewportAndScissors(screenSize.X, screenSize.Y); - + context->SetViewportAndScissors(outputViewport); const auto aaMode = renderContext.List->Settings.AntiAliasing.Mode; if (aaMode == AntialiasingMode::FastApproximateAntialiasing) { @@ -554,6 +552,27 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont Swap(frameBuffer, tempBuffer); } + // Upscaling after scene rendering but before post processing + bool useUpscaling = task->RenderingPercentage < 1.0f; + const Viewport outputViewport = task->GetOutputViewport(); + if (useUpscaling && task->UpscaleLocation == RenderingUpscaleLocation::BeforePostProcessingPass) + { + useUpscaling = false; + RenderTargetPool::Release(tempBuffer); + tempDesc.Width = (int32)outputViewport.Width; + tempDesc.Height = (int32)outputViewport.Height; + tempBuffer = RenderTargetPool::Get(tempDesc); + context->ResetSR(); + if (renderContext.List->HasAnyPostFx(renderContext, PostProcessEffectLocation::CustomUpscale)) + renderContext.List->RunCustomPostFxPass(context, renderContext, PostProcessEffectLocation::CustomUpscale, frameBuffer, tempBuffer); + else + MultiScaler::Instance()->Upscale(context, outputViewport, frameBuffer, tempBuffer->View()); + if (tempBuffer->Width() == tempDesc.Width) + Swap(frameBuffer, tempBuffer); + RenderTargetPool::Release(tempBuffer); + tempBuffer = RenderTargetPool::Get(tempDesc); + } + // Depth of Field auto dofTemporary = DepthOfFieldPass::Instance()->Render(renderContext, frameBuffer); frameBuffer = dofTemporary ? dofTemporary : frameBuffer; @@ -591,7 +610,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont { context->ResetRenderTarget(); context->SetRenderTarget(task->GetOutputView()); - context->SetViewportAndScissors(task->GetOutputViewport()); + context->SetViewportAndScissors(outputViewport); MotionBlurPass::Instance()->RenderDebug(renderContext, frameBuffer->View()); RenderTargetPool::Release(tempBuffer); RenderTargetPool::Release(frameBuffer); @@ -599,35 +618,58 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont } // Anti Aliasing - if (!renderContext.List->HasAnyPostFx(renderContext, PostProcessEffectLocation::AfterAntiAliasingPass, MaterialPostFxLocation::AfterAntiAliasingPass) && Math::IsOne(task->RenderingPercentage)) + GPUTextureView* outputView = task->GetOutputView(); + if (!renderContext.List->HasAnyPostFx(renderContext, PostProcessEffectLocation::AfterAntiAliasingPass, MaterialPostFxLocation::AfterAntiAliasingPass) && !useUpscaling) { // AA -> Back Buffer - RenderAntiAliasingPass(renderContext, frameBuffer, task->GetOutputView()); + RenderAntiAliasingPass(renderContext, frameBuffer, outputView, outputViewport); } else { // AA -> PostFx - RenderAntiAliasingPass(renderContext, frameBuffer, *tempBuffer); + RenderAntiAliasingPass(renderContext, frameBuffer, *tempBuffer, Viewport(Float2(renderContext.View.ScreenSize))); context->ResetRenderTarget(); Swap(frameBuffer, tempBuffer); renderContext.List->RunCustomPostFxPass(context, renderContext, PostProcessEffectLocation::AfterAntiAliasingPass, frameBuffer, tempBuffer); renderContext.List->RunMaterialPostFxPass(context, renderContext, MaterialPostFxLocation::AfterAntiAliasingPass, frameBuffer, tempBuffer); // PostFx -> (up-scaling) -> Back Buffer - if (task->RenderingPercentage >= 1.0f) + if (!useUpscaling) { PROFILE_GPU("Copy frame"); - context->SetRenderTarget(task->GetOutputView()); - context->SetViewportAndScissors(task->GetOutputViewport()); + context->SetRenderTarget(outputView); + context->SetViewportAndScissors(outputViewport); context->Draw(frameBuffer); } - else if (renderContext.List->HasAnyPostFx(renderContext, PostProcessEffectLocation::CustomUpscale, MaterialPostFxLocation::MAX)) + else if (renderContext.List->HasAnyPostFx(renderContext, PostProcessEffectLocation::CustomUpscale)) { - renderContext.List->RunCustomPostFxPass(context, renderContext, PostProcessEffectLocation::CustomUpscale, frameBuffer, frameBuffer); + if (outputView->GetParent()->Is()) + { + // Upscale directly to the output texture + auto outputTexture = (GPUTexture*)outputView->GetParent(); + renderContext.List->RunCustomPostFxPass(context, renderContext, PostProcessEffectLocation::CustomUpscale, frameBuffer, outputTexture); + if (frameBuffer == (GPUTexture*)outputView->GetParent()) + Swap(frameBuffer, outputTexture); + } + else + { + // Use temporary buffer for upscaled frame if GetOutputView is owned by GPUSwapChain + RenderTargetPool::Release(tempBuffer); + tempDesc.Width = (int32)outputViewport.Width; + tempDesc.Height = (int32)outputViewport.Height; + tempBuffer = RenderTargetPool::Get(tempDesc); + renderContext.List->RunCustomPostFxPass(context, renderContext, PostProcessEffectLocation::CustomUpscale, frameBuffer, tempBuffer); + { + PROFILE_GPU("Copy frame"); + context->SetRenderTarget(outputView); + context->SetViewportAndScissors(outputViewport); + context->Draw(frameBuffer); + } + } } else { - MultiScaler::Instance()->Upscale(context, task->GetOutputViewport(), frameBuffer, task->GetOutputView()); + MultiScaler::Instance()->Upscale(context, outputViewport, frameBuffer, outputView); } }