diff --git a/Content/Shaders/MultiScaler.flax b/Content/Shaders/MultiScaler.flax index 1e35e8206..01f7bac4f 100644 --- a/Content/Shaders/MultiScaler.flax +++ b/Content/Shaders/MultiScaler.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:941d8e300f269bd23a6b3f7e6016af650e96d353bf1f29657553ee7dcb38c5bb -size 3929 +oid sha256:38bed53b2fae0679ebdfafe9e44bac551249c285fa929f657a9e6e0b5febc1ef +size 7206 diff --git a/Source/Editor/Windows/GraphicsQualityWindow.cs b/Source/Editor/Windows/GraphicsQualityWindow.cs index 65452d5f6..94b3bd44e 100644 --- a/Source/Editor/Windows/GraphicsQualityWindow.cs +++ b/Source/Editor/Windows/GraphicsQualityWindow.cs @@ -88,6 +88,15 @@ namespace FlaxEditor.Windows set => Graphics.AllowCSMBlending = value; } + [NoSerialize, DefaultValue(1.0f), Limit(0.05f, 5, 0)] + [EditorOrder(1400), EditorDisplay("Quality")] + [Tooltip("The scale of the rendering resolution relative to the output dimensions. If lower than 1 the scene and postprocessing will be rendered at a lower resolution and upscaled to the output backbuffer.")] + public float RenderingPercentage + { + get => MainRenderTask.Instance.RenderingPercentage; + set => MainRenderTask.Instance.RenderingPercentage = value; + } + [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/RenderBuffers.cpp b/Source/Engine/Graphics/RenderBuffers.cpp index 42ad32486..0788738c6 100644 --- a/Source/Engine/Graphics/RenderBuffers.cpp +++ b/Source/Engine/Graphics/RenderBuffers.cpp @@ -90,7 +90,7 @@ GPUTexture* RenderBuffers::RequestHalfResDepth(GPUContext* context) } // Generate depth - MultiScaler::Instance()->DownscaleDepth(context, halfDepthWidth, halfDepthHeight, DepthBuffer->View(), HalfResDepth->View()); + MultiScaler::Instance()->DownscaleDepth(context, halfDepthWidth, halfDepthHeight, DepthBuffer, HalfResDepth->View()); return HalfResDepth; } diff --git a/Source/Engine/Graphics/RenderTask.cpp b/Source/Engine/Graphics/RenderTask.cpp index c5cd370cf..05b261728 100644 --- a/Source/Engine/Graphics/RenderTask.cpp +++ b/Source/Engine/Graphics/RenderTask.cpp @@ -296,13 +296,27 @@ void SceneRenderTask::OnPostRender(GPUContext* context, RenderContext& renderCon Viewport SceneRenderTask::GetViewport() const { + Viewport viewport; if (Output) + viewport = Viewport(0, 0, static_cast(Output->Width()), static_cast(Output->Height())); + else if (SwapChain) + viewport = Viewport(0, 0, static_cast(SwapChain->GetWidth()), static_cast(SwapChain->GetHeight())); + else if (Buffers != nullptr) + viewport = Buffers->GetViewport(); + else + viewport = Viewport(0, 0, 1280, 720); + viewport.Width *= RenderingPercentage; + viewport.Height *= RenderingPercentage; + return viewport; +} + +Viewport SceneRenderTask::GetOutputViewport() const +{ + if (Output && Output->IsAllocated()) return Viewport(0, 0, static_cast(Output->Width()), static_cast(Output->Height())); if (SwapChain) return Viewport(0, 0, static_cast(SwapChain->GetWidth()), static_cast(SwapChain->GetHeight())); - if (Buffers != nullptr) - return Buffers->GetViewport(); - return Viewport(0, 0, 1280, 720); + return GetViewport(); } GPUTextureView* SceneRenderTask::GetOutputView() const @@ -352,11 +366,11 @@ void SceneRenderTask::OnBegin(GPUContext* context) // Setup render buffers for the output rendering resolution if (Output) { - Buffers->Init(Output->Width(), Output->Height()); + Buffers->Init((int32)((float)Output->Width() * RenderingPercentage), (int32)((float)Output->Height() * RenderingPercentage)); } else if (SwapChain) { - Buffers->Init(SwapChain->GetWidth(), SwapChain->GetHeight()); + Buffers->Init((int32)((float)SwapChain->GetWidth() * RenderingPercentage), (int32)((float)SwapChain->GetHeight() * RenderingPercentage)); } } @@ -385,7 +399,7 @@ bool SceneRenderTask::Resize(int32 width, int32 height) { if (Output && Output->Resize(width, height)) return true; - if (Buffers && Buffers->Init(width, height)) + if (Buffers && Buffers->Init((int32)((float)width * RenderingPercentage), (int32)((float)height * RenderingPercentage))) return true; return false; } @@ -423,7 +437,7 @@ void MainRenderTask::OnBegin(GPUContext* context) #if !USE_EDITOR // Sync render buffers size with the backbuffer const auto size = Screen::GetSize(); - Buffers->Init((int32)size.X, (int32)size.Y); + Buffers->Init((int32)(size.X * RenderingPercentage), (int32)(size.Y * RenderingPercentage)); #endif SceneRenderTask::OnBegin(context); diff --git a/Source/Engine/Graphics/RenderTask.h b/Source/Engine/Graphics/RenderTask.h index 47a43363e..63c5894c2 100644 --- a/Source/Engine/Graphics/RenderTask.h +++ b/Source/Engine/Graphics/RenderTask.h @@ -263,6 +263,11 @@ public: /// API_FIELD() ActorsSources ActorsSource = ActorsSources::Scenes; + /// + /// The scale of the rendering resolution relative to the output dimensions. If lower than 1 the scene and postprocessing will be rendered at a lower resolution and upscaled to the output backbuffer. + /// + API_FIELD() float RenderingPercentage = 1.0f; + /// /// The custom set of actors to render. /// @@ -336,10 +341,15 @@ public: public: /// - /// Gets the rendering render task viewport. + /// Gets the rendering render task viewport (before upsampling). /// API_PROPERTY() Viewport GetViewport() const; + /// + /// Gets the rendering output viewport (after upsampling). + /// + API_PROPERTY() Viewport GetOutputViewport() const; + /// /// Gets the rendering output view. /// diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index 369dcdd73..fe5126c6d 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -326,12 +326,12 @@ void RenderList::RunCustomPostFxPass(GPUContext* context, RenderContext& renderC } } -bool RenderList::HasAnyPostAA(RenderContext& renderContext) const +bool RenderList::HasAnyPostFx(RenderContext& renderContext, PostProcessEffectLocation postProcess, MaterialPostFxLocation materialPostFx) const { for (int32 i = 0; i < Settings.PostFxMaterials.Materials.Count(); i++) { auto material = Settings.PostFxMaterials.Materials[i].Get(); - if (material && material->IsReady() && material->IsPostFx() && material->GetInfo().PostFxLocation == MaterialPostFxLocation::AfterAntiAliasingPass) + if (material && material->IsReady() && material->IsPostFx() && material->GetInfo().PostFxLocation == materialPostFx) { return true; } @@ -341,7 +341,7 @@ bool RenderList::HasAnyPostAA(RenderContext& renderContext) const for (int32 i = 0; i < renderContext.List->PostFx.Count(); i++) { auto fx = renderContext.List->PostFx[i]; - if (fx->IsReady() && fx->GetLocation() == PostProcessEffectLocation::AfterAntiAliasingPass) + if (fx->IsReady() && fx->GetLocation() == postProcess) { return true; } diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index 97f369999..3dfc7eb23 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -489,11 +489,13 @@ public: void RunCustomPostFxPass(GPUContext* context, RenderContext& renderContext, PostProcessEffectLocation location, GPUTexture*& input, GPUTexture*& output); /// - /// Determines whether any Custom PostFx or Material PostFx has to be rendered after AA pass. Used to pick a faster rendering path by the frame rendering module. + /// Determines whether any Custom PostFx or Material PostFx specified by given type. Used to pick a faster rendering path by the frame rendering module. /// /// The rendering context. - /// True if render any postFx after AA, otherwise false. - bool HasAnyPostAA(RenderContext& renderContext) const; + /// The PostFx location to check (for scripts). + /// The PostFx location to check (for materials). + /// True if render any postFx of the given type, otherwise false. + bool HasAnyPostFx(RenderContext& renderContext, PostProcessEffectLocation postProcess, MaterialPostFxLocation materialPostFx) const; public: diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 8c82f9792..7e7a2908c 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -332,7 +332,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext) // Render reflections debug view context->ResetRenderTarget(); context->SetRenderTarget(task->GetOutputView()); - context->SetViewportAndScissors((float)renderContext.Buffers->GetWidth(), (float)renderContext.Buffers->GetHeight()); + context->SetViewportAndScissors(task->GetOutputViewport()); context->Draw(lightBuffer->View()); RenderTargetPool::Release(lightBuffer); return; @@ -349,7 +349,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext) { context->ResetRenderTarget(); context->SetRenderTarget(task->GetOutputView()); - context->SetViewportAndScissors((float)renderContext.Buffers->GetWidth(), (float)renderContext.Buffers->GetHeight()); + context->SetViewportAndScissors(task->GetOutputViewport()); GBufferPass::Instance()->RenderDebug(renderContext); RenderTargetPool::Release(lightBuffer); return; @@ -367,7 +367,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext) RenderTargetPool::Release(lightBuffer); context->ResetRenderTarget(); context->SetRenderTarget(task->GetOutputView()); - context->SetViewportAndScissors((float)renderContext.Buffers->GetWidth(), (float)renderContext.Buffers->GetHeight()); + context->SetViewportAndScissors(task->GetOutputViewport()); context->Draw(tempBuffer); return; } @@ -381,7 +381,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext) { context->ResetRenderTarget(); context->SetRenderTarget(task->GetOutputView()); - context->SetViewportAndScissors((float)renderContext.Buffers->GetWidth(), (float)renderContext.Buffers->GetHeight()); + context->SetViewportAndScissors(task->GetOutputViewport()); context->Draw(lightBuffer); RenderTargetPool::Release(lightBuffer); return; @@ -421,7 +421,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext) if (renderContext.View.Mode == ViewMode::NoPostFx || renderContext.View.Mode == ViewMode::Wireframe) { context->SetRenderTarget(task->GetOutputView()); - context->SetViewportAndScissors((float)renderContext.Buffers->GetWidth(), (float)renderContext.Buffers->GetHeight()); + context->SetViewportAndScissors(task->GetOutputViewport()); context->Draw(forwardPassResult); return; } @@ -475,13 +475,13 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext) { context->ResetRenderTarget(); context->SetRenderTarget(task->GetOutputView()); - context->SetViewportAndScissors((float)renderContext.Buffers->GetWidth(), (float)renderContext.Buffers->GetHeight()); + context->SetViewportAndScissors(task->GetOutputViewport()); MotionBlurPass::Instance()->RenderDebug(renderContext, frameBuffer->View()); return; } // Anti Aliasing - if (!renderContext.List->HasAnyPostAA(renderContext)) + if (!renderContext.List->HasAnyPostFx(renderContext, PostProcessEffectLocation::AfterAntiAliasingPass, MaterialPostFxLocation::AfterAntiAliasingPass) && Math::IsOne(task->RenderingPercentage)) { // AA -> Back Buffer RenderAntiAliasingPass(renderContext, frameBuffer, task->GetOutputView()); @@ -495,12 +495,17 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext) renderContext.List->RunCustomPostFxPass(context, renderContext, PostProcessEffectLocation::AfterAntiAliasingPass, frameBuffer, tempBuffer); renderContext.List->RunMaterialPostFxPass(context, renderContext, MaterialPostFxLocation::AfterAntiAliasingPass, frameBuffer, tempBuffer); - // PostFx -> Back Buffer + // PostFx -> (up-scaling) -> Back Buffer + if (Math::IsOne(task->RenderingPercentage)) { PROFILE_GPU("Copy frame"); context->SetRenderTarget(task->GetOutputView()); - context->SetViewportAndScissors((float)renderContext.Buffers->GetWidth(), (float)renderContext.Buffers->GetHeight()); + context->SetViewportAndScissors(task->GetViewport()); context->Draw(frameBuffer); } + else + { + MultiScaler::Instance()->Upscale(context, task->GetOutputViewport(), frameBuffer, task->GetOutputView()); + } } } diff --git a/Source/Engine/Renderer/ScreenSpaceReflectionsPass.cpp b/Source/Engine/Renderer/ScreenSpaceReflectionsPass.cpp index bd1662773..a936291df 100644 --- a/Source/Engine/Renderer/ScreenSpaceReflectionsPass.cpp +++ b/Source/Engine/Renderer/ScreenSpaceReflectionsPass.cpp @@ -187,8 +187,8 @@ void ScreenSpaceReflectionsPass::Render(RenderContext& renderContext, GPUTexture const bool useTemporal = settings.TemporalEffect && !renderContext.Task->IsCameraCut; // Prepare resolutions for passes - const int32 width = renderContext.Buffers->GetWidth(); - const int32 height = renderContext.Buffers->GetHeight(); + const int32 width = buffers->GetWidth(); + const int32 height = buffers->GetHeight(); const int32 traceWidth = width / static_cast(settings.RayTracePassResolution); const int32 traceHeight = height / static_cast(settings.RayTracePassResolution); const int32 resolveWidth = width / static_cast(settings.RayTracePassResolution); @@ -262,7 +262,7 @@ void ScreenSpaceReflectionsPass::Render(RenderContext& renderContext, GPUTexture const double integral = round(time / scale) * scale; data.TemporalTime = static_cast(time - integral); - renderContext.Buffers->LastFrameTemporalSSR = Engine::FrameCount; + buffers->LastFrameTemporalSSR = Engine::FrameCount; if (buffers->TemporalSSR == nullptr) { // Missing temporal buffer diff --git a/Source/Engine/Renderer/Utils/MultiScaler.cpp b/Source/Engine/Renderer/Utils/MultiScaler.cpp index 54995a9d4..93ddf6572 100644 --- a/Source/Engine/Renderer/Utils/MultiScaler.cpp +++ b/Source/Engine/Renderer/Utils/MultiScaler.cpp @@ -4,10 +4,10 @@ #include "Engine/Graphics/Textures/GPUTexture.h" #include "Engine/Content/Content.h" -MultiScaler::MultiScaler() - : _psHalfDepth(nullptr) -{ -} +PACK_STRUCT(struct Data { + Vector2 TexelSize; + Vector2 Padding; + }); String MultiScaler::ToString() const { @@ -21,6 +21,7 @@ bool MultiScaler::Init() _psBlur5.CreatePipelineStates(); _psBlur9.CreatePipelineStates(); _psBlur13.CreatePipelineStates(); + _psUpscale = GPUDevice::Instance->CreatePipelineState(); // Load asset _shader = Content::LoadAsyncInternal(TEXT("Shaders/MultiScaler")); @@ -64,6 +65,12 @@ bool MultiScaler::setupResources() if (_psBlur13.Create(psDesc, shader, "PS_Blur13")) return true; } + if (!_psUpscale->IsValid()) + { + psDesc.PS = shader->GetPS("PS_Upscale"); + if (_psUpscale->Init(psDesc)) + return true; + } if (!_psHalfDepth->IsValid()) { psDesc.PS = shader->GetPS("PS_HalfDepth"); @@ -84,6 +91,7 @@ void MultiScaler::Dispose() // Cleanup SAFE_DELETE_GPU_RESOURCE(_psHalfDepth); + SAFE_DELETE_GPU_RESOURCE(_psUpscale); _psBlur5.Delete(); _psBlur9.Delete(); _psBlur13.Delete(); @@ -120,14 +128,14 @@ void MultiScaler::Filter(const FilterMode mode, GPUContext* context, const int32 ps = &_psBlur13; break; default: - CRASH; + CRASH; return; } // Prepare Data data; - data.TexelSize.X = 1.0f / width; - data.TexelSize.Y = 1.0f / height; + data.TexelSize.X = 1.0f / (float)width; + data.TexelSize.Y = 1.0f / (float)height; auto cb = _shader->GetShader()->GetCB(0); context->UpdateCB(cb, &data); context->BindCB(0, cb); @@ -176,7 +184,7 @@ void MultiScaler::Filter(const FilterMode mode, GPUContext* context, const int32 ps = &_psBlur13; break; default: - CRASH; + CRASH; return; } @@ -205,7 +213,7 @@ void MultiScaler::Filter(const FilterMode mode, GPUContext* context, const int32 context->ResetRenderTarget(); } -void MultiScaler::DownscaleDepth(GPUContext* context, int32 dstWidth, int32 dstHeight, GPUTextureView* src, GPUTextureView* dst) +void MultiScaler::DownscaleDepth(GPUContext* context, int32 dstWidth, int32 dstHeight, GPUTexture* src, GPUTextureView* dst) { PROFILE_GPU_CPU("Downscale Depth"); @@ -219,8 +227,8 @@ void MultiScaler::DownscaleDepth(GPUContext* context, int32 dstWidth, int32 dstH // Prepare Data data; - data.TexelSize.X = 2.0f / (float)dstWidth; - data.TexelSize.Y = 2.0f / (float)dstHeight; + data.TexelSize.X = 1.0f / (float)src->Width(); + data.TexelSize.Y = 1.0f / (float)src->Height(); auto cb = _shader->GetShader()->GetCB(0); context->UpdateCB(cb, &data); context->BindCB(0, cb); @@ -236,3 +244,31 @@ void MultiScaler::DownscaleDepth(GPUContext* context, int32 dstWidth, int32 dstH context->ResetRenderTarget(); context->UnBindCB(0); } + +void MultiScaler::Upscale(GPUContext* context, const Viewport& viewport, GPUTexture* src, GPUTextureView* dst) +{ + PROFILE_GPU_CPU("Upscale"); + + context->SetViewportAndScissors(viewport); + context->SetRenderTarget(dst); + + if (checkIfSkipPass()) + { + context->Draw(src); + } + else + { + Data data; + data.TexelSize.X = 1.0f / (float)src->Width(); + data.TexelSize.Y = 1.0f / (float)src->Height(); + auto cb = _shader->GetShader()->GetCB(0); + context->UpdateCB(cb, &data); + context->BindCB(0, cb); + context->BindSR(0, src); + context->SetState(_psUpscale); + context->DrawFullscreenTriangle(); + context->UnBindCB(0); + } + + context->ResetRenderTarget(); +} diff --git a/Source/Engine/Renderer/Utils/MultiScaler.h b/Source/Engine/Renderer/Utils/MultiScaler.h index a00d3e58d..0e459a0aa 100644 --- a/Source/Engine/Renderer/Utils/MultiScaler.h +++ b/Source/Engine/Renderer/Utils/MultiScaler.h @@ -12,23 +12,12 @@ class MultiScaler : public RendererPass { private: - PACK_STRUCT(struct Data { - Vector2 TexelSize; - Vector2 Padding; - }); - AssetReference _shader; - GPUPipelineState* _psHalfDepth; + GPUPipelineState* _psHalfDepth = nullptr; GPUPipelineStatePermutationsPs<2> _psBlur5; GPUPipelineStatePermutationsPs<2> _psBlur9; GPUPipelineStatePermutationsPs<2> _psBlur13; - -public: - - /// - /// Init - /// - MultiScaler(); + GPUPipelineState* _psUpscale = nullptr; public: @@ -84,7 +73,16 @@ public: /// The height of the destination texture (in pixels). /// The source texture. /// The destination texture. - void DownscaleDepth(GPUContext* context, int32 dstWidth, int32 dstHeight, GPUTextureView* src, GPUTextureView* dst); + void DownscaleDepth(GPUContext* context, int32 dstWidth, int32 dstHeight, GPUTexture* src, GPUTextureView* dst); + + /// + /// Upscales the texture. + /// + /// The context. + /// The viewport of the destination texture. + /// The source texture. + /// The destination texture. + void Upscale(GPUContext* context, const Viewport& viewport, GPUTexture* src, GPUTextureView* dst); public: @@ -96,6 +94,7 @@ public: void OnShaderReloading(Asset* obj) { _psHalfDepth->ReleaseGPU(); + _psUpscale->ReleaseGPU(); _psBlur5.Release(); _psBlur9.Release(); _psBlur13.Release();