Files
FlaxEngine/Source/Engine/Graphics/RenderTask.cpp

452 lines
12 KiB
C++

// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "RenderTask.h"
#include "RenderBuffers.h"
#include "GPUDevice.h"
#include "GPUSwapChain.h"
#include "FlaxEngine.Gen.h"
#include "Engine/Core/Collections/Sorting.h"
#include "Engine/Debug/DebugLog.h"
#include "Engine/Level/Level.h"
#include "Engine/Level/Actors/Camera.h"
#include "Engine/Renderer/Renderer.h"
#include "Engine/Render2D/Render2D.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Level/Actors/PostFxVolume.h"
#include "Engine/Profiler/Profiler.h"
#include "Engine/Scripting/ManagedCLR/MAssembly.h"
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/ManagedCLR/MMethod.h"
#include "Engine/Scripting/Script.h"
#include "Engine/Scripting/BinaryModule.h"
#include "Engine/Threading/Threading.h"
#include <ThirdParty/mono-2.0/mono/metadata/appdomain.h>
#if USE_EDITOR
#include "Engine/Renderer/Lightmaps.h"
#else
#include "Engine/Engine/Screen.h"
#endif
// TODO: use API for events and remove this manual wrapper code
class RenderContextInternal
{
public:
MonoObject* Buffers;
MonoObject* List;
RenderView View;
RenderView* LodProxyView;
MonoObject* Task;
};
// TODO: use API for events and remove this manual wrapper code
namespace
{
RenderContextInternal ToManaged(const RenderContext& value)
{
RenderContextInternal result;
result.Buffers = ScriptingObject::ToManaged((ScriptingObject*)value.Buffers);
result.List = ScriptingObject::ToManaged((ScriptingObject*)value.List);
result.View = value.View;
result.LodProxyView = value.LodProxyView;
result.Task = ScriptingObject::ToManaged((ScriptingObject*)value.Task);
return result;
}
}
Array<RenderTask*> RenderTask::Tasks;
CriticalSection RenderTask::TasksLocker;
int32 RenderTask::TasksDoneLastFrame;
MainRenderTask* MainRenderTask::Instance;
void RenderTask::DrawAll()
{
ScopeLock lock(TasksLocker);
// Sort tasks (by Order property)
Sorting::QuickSortObj(Tasks.Get(), Tasks.Count());
// Render all that shit
for (auto task : Tasks)
{
if (task->CanDraw())
{
task->OnDraw();
}
}
}
RenderTask::RenderTask(const SpawnParams& params)
: PersistentScriptingObject(params)
{
// Register
TasksLocker.Lock();
Tasks.Add(this);
TasksLocker.Unlock();
}
RenderTask::~RenderTask()
{
// Unregister
TasksLocker.Lock();
Tasks.Remove(this);
TasksLocker.Unlock();
}
bool RenderTask::CanDraw() const
{
if (SwapChain && SwapChain->GetWindow() && !SwapChain->GetWindow()->IsVisible() && !SwapChain->GetWindow()->GetSettings().ShowAfterFirstPaint)
return false;
return Enabled;
}
void RenderTask::OnDraw()
{
const auto context = GPUDevice::Instance->GetMainContext();
OnBegin(context);
OnRender(context);
OnEnd(context);
}
void RenderTask::OnBegin(GPUContext* context)
{
Begin(this, context);
if (SwapChain)
SwapChain->Begin(this);
_prevTask = GPUDevice::Instance->CurrentTask;
GPUDevice::Instance->CurrentTask = this;
LastUsedFrame = Engine::FrameCount;
FrameCount++;
}
void RenderTask::OnRender(GPUContext* context)
{
Render(this, context);
if (SwapChain)
{
PROFILE_GPU_CPU_NAMED("GUI");
const Viewport viewport(0, 0, static_cast<float>(SwapChain->GetWidth()), static_cast<float>(SwapChain->GetHeight()));
Render2D::Begin(context, SwapChain->GetBackBufferView(), nullptr, viewport);
SwapChain->GetWindow()->OnDraw();
Render2D::End();
}
}
void RenderTask::OnEnd(GPUContext* context)
{
GPUDevice::Instance->CurrentTask = _prevTask;
_prevTask = nullptr;
if (SwapChain)
SwapChain->End(this);
End(this, context);
}
void RenderTask::OnPresent(bool vsync)
{
if (SwapChain)
SwapChain->Present(vsync);
Present(this);
}
bool RenderTask::Resize(int32 width, int32 height)
{
return false;
}
void ManagedPostProcessEffect::FetchInfo()
{
ASSERT(Target);
SetEnabled(Target->GetEnabled());
static MMethod* FetchInfoManaged = nullptr;
if (FetchInfoManaged == nullptr)
{
auto klass = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->GetClass("FlaxEngine.PostProcessEffect");
ASSERT(klass);
FetchInfoManaged = klass->GetMethod("FetchInfo", 3);
ASSERT(FetchInfoManaged);
}
void* args[3];
args[0] = Target->GetOrCreateManagedInstance();
args[1] = &_location;
args[2] = &_useSingleTarget;
MonoObject* exception = nullptr;
FetchInfoManaged->Invoke(nullptr, args, &exception);
if (exception)
DebugLog::LogException(exception);
}
bool ManagedPostProcessEffect::IsLoaded() const
{
return Target != nullptr;
}
void ManagedPostProcessEffect::Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output)
{
const auto context = GPUDevice::Instance->GetMainContext();
auto inputObj = ScriptingObject::ToManaged(input);
auto outputObj = ScriptingObject::ToManaged(output);
// Call rendering method (handle base classes)
ASSERT(Target && Target->GetClass() != nullptr);
auto mclass = Target->GetClass();
auto renderMethod = mclass->FindMethod("Render", 4, true);
if (renderMethod == nullptr)
return;
RenderContextInternal tmp = ::ToManaged(renderContext);
void* params[4];
params[0] = context->GetOrCreateManagedInstance();
params[1] = &tmp;
params[2] = inputObj;
params[3] = outputObj;
MonoObject* exception = nullptr;
renderMethod->InvokeVirtual(Target->GetOrCreateManagedInstance(), params, &exception);
if (exception)
DebugLog::LogException(exception);
}
SceneRenderTask::SceneRenderTask(const SpawnParams& params)
: RenderTask(params)
{
View.Position = Vector3::Zero;
View.Direction = Vector3::Forward;
Buffers = New<RenderBuffers>();
}
SceneRenderTask::~SceneRenderTask()
{
if (Buffers)
Buffers->DeleteObjectNow();
}
void SceneRenderTask::CameraCut()
{
IsCameraCut = true;
}
void SceneRenderTask::AddCustomActor(Actor* actor)
{
CustomActors.Add(actor);
}
void SceneRenderTask::RemoveCustomActor(Actor* actor)
{
CustomActors.Remove(actor);
}
void SceneRenderTask::ClearCustomActors()
{
CustomActors.Clear();
}
void SceneRenderTask::CollectPostFxVolumes(RenderContext& renderContext)
{
if ((ActorsSource & ActorsSources::Scenes) != 0)
{
Level::CollectPostFxVolumes(renderContext);
}
if ((ActorsSource & ActorsSources::CustomActors) != 0)
{
for (auto a : CustomActors)
{
auto* postFxVolume = dynamic_cast<PostFxVolume*>(a);
if (postFxVolume && a->GetIsActive())
{
postFxVolume->Collect(renderContext);
}
}
}
}
void SceneRenderTask::OnCollectDrawCalls(RenderContext& renderContext)
{
// Draw actors (collect draw calls)
if ((ActorsSource & ActorsSources::CustomActors) != 0)
{
for (auto a : CustomActors)
{
if (a && a->GetIsActive())
{
a->DrawHierarchy(renderContext);
}
}
}
if ((ActorsSource & ActorsSources::Scenes) != 0)
{
Level::DrawActors(renderContext);
}
// External drawing event
CollectDrawCalls(renderContext);
}
void SceneRenderTask::OnPreRender(GPUContext* context, RenderContext& renderContext)
{
PreRender(context, renderContext);
}
void SceneRenderTask::OnPostRender(GPUContext* context, RenderContext& renderContext)
{
PostRender(context, renderContext);
}
Viewport SceneRenderTask::GetViewport() const
{
Viewport viewport;
if (Output)
viewport = Viewport(0, 0, static_cast<float>(Output->Width()), static_cast<float>(Output->Height()));
else if (SwapChain)
viewport = Viewport(0, 0, static_cast<float>(SwapChain->GetWidth()), static_cast<float>(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<float>(Output->Width()), static_cast<float>(Output->Height()));
if (SwapChain)
return Viewport(0, 0, static_cast<float>(SwapChain->GetWidth()), static_cast<float>(SwapChain->GetHeight()));
return GetViewport();
}
GPUTextureView* SceneRenderTask::GetOutputView() const
{
if (Output && Output->IsAllocated())
return Output->View();
if (SwapChain)
return SwapChain->GetBackBufferView();
return nullptr;
}
void SceneRenderTask::OnBegin(GPUContext* context)
{
RenderTask::OnBegin(context);
// Copy view info if camera is specified
if (Camera)
{
auto viewport = GetViewport();
View.CopyFrom(Camera, &viewport);
}
// Get custom and global PostFx
// TODO: move postFx in SceneRenderTask from C# to C++
static MMethod* GetPostFxManaged = GetStaticClass()->GetMethod("GetPostFx", 1);
if (GetPostFxManaged)
{
int32 count = 0;
void* params[1];
params[0] = &count;
MonoObject* exception = nullptr;
const auto objects = (MonoArray*)GetPostFxManaged->Invoke(GetOrCreateManagedInstance(), params, &exception);
if (exception)
DebugLog::LogException(exception);
CustomPostFx.Resize(count);
for (int32 i = 0; i < count; i++)
{
auto& postFx = CustomPostFx[i];
postFx.Target = mono_array_get(objects, Script*, i);
if (postFx.Target)
postFx.FetchInfo();
}
}
else
CustomPostFx.Clear();
// Setup render buffers for the output rendering resolution
if (Output)
{
Buffers->Init((int32)((float)Output->Width() * RenderingPercentage), (int32)((float)Output->Height() * RenderingPercentage));
}
else if (SwapChain)
{
Buffers->Init((int32)((float)SwapChain->GetWidth() * RenderingPercentage), (int32)((float)SwapChain->GetHeight() * RenderingPercentage));
}
}
void SceneRenderTask::OnRender(GPUContext* context)
{
if (Buffers && Buffers->GetWidth() > 0)
Renderer::Render(this);
RenderTask::OnRender(context);
}
void SceneRenderTask::OnEnd(GPUContext* context)
{
TasksDoneLastFrame++;
IsCameraCut = false;
RenderTask::OnEnd(context);
// Swap matrices
View.PrevView = View.View;
View.PrevProjection = View.Projection;
Matrix::Multiply(View.PrevView, View.PrevProjection, View.PrevViewProjection);
}
bool SceneRenderTask::Resize(int32 width, int32 height)
{
if (Output && Output->Resize(width, height))
return true;
if (Buffers && Buffers->Init((int32)((float)width * RenderingPercentage), (int32)((float)height * RenderingPercentage)))
return true;
return false;
}
bool SceneRenderTask::CanDraw() const
{
if (Output && !Output->IsAllocated())
return false;
return RenderTask::CanDraw();
}
MainRenderTask::MainRenderTask(const SpawnParams& params)
: SceneRenderTask(params)
{
if (Instance == nullptr)
{
Instance = this;
LOG(Info, "Main render task created");
}
}
MainRenderTask::~MainRenderTask()
{
if (Instance == this)
{
Instance = nullptr;
}
}
void MainRenderTask::OnBegin(GPUContext* context)
{
// Use the main camera for the game (can be later overriden in Begin event by external code)
Camera = Camera::GetMainCamera();
#if !USE_EDITOR
// Sync render buffers size with the backbuffer
const auto size = Screen::GetSize();
Buffers->Init((int32)(size.X * RenderingPercentage), (int32)(size.Y * RenderingPercentage));
#endif
SceneRenderTask::OnBegin(context);
}
RenderContext::RenderContext(SceneRenderTask* task)
{
Buffers = task->Buffers;
View = task->View;
Task = task;
}