// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "ForwardPass.h" #include "Engine/Core/Collections/Sorting.h" #include "Engine/Content/Assets/Model.h" #include "Engine/Content/Assets/Shader.h" #include "Engine/Content/Content.h" #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/RenderTargetPool.h" ForwardPass::ForwardPass() : _shader(nullptr) , _psApplyDistortion(nullptr) { } String ForwardPass::ToString() const { return TEXT("ForwardPass"); } bool ForwardPass::Init() { // Prepare resources _psApplyDistortion = GPUDevice::Instance->CreatePipelineState(); _shader = Content::LoadAsyncInternal(TEXT("Shaders/Forward")); if (_shader == nullptr) { return true; } #if COMPILE_WITH_DEV_ENV _shader.Get()->OnReloading.Bind(this); #endif return false; } bool ForwardPass::setupResources() { // Check shader if (!_shader->IsLoaded()) { return true; } const auto shader = _shader->GetShader(); // Create pipeline stages GPUPipelineState::Description psDesc; if (!_psApplyDistortion->IsValid()) { psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle; psDesc.PS = shader->GetPS("PS_ApplyDistortion"); if (_psApplyDistortion->Init(psDesc)) return true; } return false; } void ForwardPass::Dispose() { // Base RendererPass::Dispose(); // Cleanup SAFE_DELETE_GPU_RESOURCE(_psApplyDistortion); _shader = nullptr; } void ForwardPass::Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output) { PROFILE_GPU_CPU("Forward"); // Cache data auto device = GPUDevice::Instance; auto context = device->GetMainContext(); auto& view = renderContext.View; auto mainCache = renderContext.List; context->ResetRenderTarget(); context->ResetSR(); // Try to use read-only depth if supported GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer; GPUTextureView* depthBufferHandle = depthBuffer->View(); if (depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView) depthBufferHandle = depthBuffer->ViewReadOnlyDepth(); // Check if there is no objects to render or no resources ready auto& forwardList = mainCache->DrawCallsLists[(int32)DrawCallsListType::Forward]; auto& distortionList = mainCache->DrawCallsLists[(int32)DrawCallsListType::Distortion]; if (distortionList.IsEmpty() || checkIfSkipPass()) { // Copy frame context->SetRenderTarget(output->View()); context->Draw(input); } else { PROFILE_GPU_CPU("Distortion"); // Peek temporary render target for the distortion pass // TODO: render distortion in half-res? const int32 width = renderContext.Buffers->GetWidth(); const int32 height = renderContext.Buffers->GetHeight(); const int32 distortionWidth = width; const int32 distortionHeight = height; const auto tempDesc = GPUTextureDescription::New2D(distortionWidth, distortionHeight, Distortion_Pass_Output_Format); auto distortionRT = RenderTargetPool::Get(tempDesc); // Clear distortion vectors context->Clear(distortionRT->View(), Color::Transparent); context->SetViewportAndScissors((float)distortionWidth, (float)distortionHeight); context->SetRenderTarget(depthBufferHandle, distortionRT->View()); // Render distortion pass view.Pass = DrawPass::Distortion; mainCache->ExecuteDrawCalls(renderContext, distortionList); context->SetViewportAndScissors((float)width, (float)height); context->ResetRenderTarget(); context->ResetSR(); // Bind inputs context->BindSR(0, input); context->BindSR(1, distortionRT); // Copy combined frame with distortion from transparent materials context->SetRenderTarget(output->View()); context->SetState(_psApplyDistortion); context->DrawFullscreenTriangle(); RenderTargetPool::Release(distortionRT); } if (!forwardList.IsEmpty()) { // Run forward pass view.Pass = DrawPass::Forward; context->SetRenderTarget(depthBufferHandle, output->View()); mainCache->ExecuteDrawCalls(renderContext, forwardList); } }