From 6a61b692aae1e15c7f61c08108779e5e6524199b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 2 Nov 2022 20:29:59 +0100 Subject: [PATCH] Refactor `PostProcessEffect` to support C++ scripting --- Source/Editor/Gizmo/EditorPrimitives.cs | 5 +- Source/Editor/Gizmo/SelectionOutline.cs | 5 +- .../Foliage/EditFoliageSelectionOutline.cs | 23 +-- .../Viewport/MainEditorGizmoViewport.cs | 37 ++-- .../Editor/Viewport/PrefabWindowViewport.cs | 14 +- .../Editor/Viewport/Previews/AssetPreview.cs | 4 +- Source/Engine/Engine/PostProcessEffect.cs | 52 ----- Source/Engine/Graphics/PostProcessBase.h | 121 ----------- Source/Engine/Graphics/PostProcessEffect.h | 57 ++++++ Source/Engine/Graphics/RenderTask.cpp | 190 +++++++----------- Source/Engine/Graphics/RenderTask.cs | 70 ------- Source/Engine/Graphics/RenderTask.h | 67 +++--- Source/Engine/Renderer/EyeAdaptationPass.cpp | 1 - Source/Engine/Renderer/HistogramPass.cpp | 1 - Source/Engine/Renderer/PostProcessingPass.cpp | 2 - Source/Engine/Renderer/RenderList.cpp | 41 ++-- Source/Engine/Renderer/RenderList.h | 6 +- Source/Engine/Renderer/Renderer.cpp | 5 - Source/Engine/UI/UICanvas.cs | 35 ++-- 19 files changed, 253 insertions(+), 483 deletions(-) delete mode 100644 Source/Engine/Engine/PostProcessEffect.cs delete mode 100644 Source/Engine/Graphics/PostProcessBase.h create mode 100644 Source/Engine/Graphics/PostProcessEffect.h diff --git a/Source/Editor/Gizmo/EditorPrimitives.cs b/Source/Editor/Gizmo/EditorPrimitives.cs index c427418aa..044d21c71 100644 --- a/Source/Editor/Gizmo/EditorPrimitives.cs +++ b/Source/Editor/Gizmo/EditorPrimitives.cs @@ -33,7 +33,10 @@ namespace FlaxEditor.Gizmo public IEditorPrimitivesOwner Viewport; /// - public override int Order => -100; + public EditorPrimitives() + { + Order = -100; + } /// public override void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output) diff --git a/Source/Editor/Gizmo/SelectionOutline.cs b/Source/Editor/Gizmo/SelectionOutline.cs index 64febc005..51d865a0e 100644 --- a/Source/Editor/Gizmo/SelectionOutline.cs +++ b/Source/Editor/Gizmo/SelectionOutline.cs @@ -114,7 +114,10 @@ namespace FlaxEditor.Gizmo protected bool HasDataReady => _enabled && _material && _outlineMaterial.IsLoaded; /// - public override bool CanRender => _enabled && _material && _outlineMaterial.IsLoaded && SelectionGetter().Count > 0; + public override bool CanRender() + { + return _enabled && _material && _outlineMaterial.IsLoaded && SelectionGetter().Count > 0; + } /// public override void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output) diff --git a/Source/Editor/Tools/Foliage/EditFoliageSelectionOutline.cs b/Source/Editor/Tools/Foliage/EditFoliageSelectionOutline.cs index 42898cb3e..06a4fa4b7 100644 --- a/Source/Editor/Tools/Foliage/EditFoliageSelectionOutline.cs +++ b/Source/Editor/Tools/Foliage/EditFoliageSelectionOutline.cs @@ -20,21 +20,18 @@ namespace FlaxEditor.Tools.Foliage public EditFoliageGizmoMode GizmoMode; /// - public override bool CanRender + public override bool CanRender() { - get - { - if (!HasDataReady) - return false; + if (!HasDataReady) + return false; - var foliage = GizmoMode.SelectedFoliage; - if (!foliage) - return false; - var instanceIndex = GizmoMode.SelectedInstanceIndex; - if (instanceIndex < 0 || instanceIndex >= foliage.InstancesCount) - return false; - return true; - } + var foliage = GizmoMode.SelectedFoliage; + if (!foliage) + return false; + var instanceIndex = GizmoMode.SelectedInstanceIndex; + if (instanceIndex < 0 || instanceIndex >= foliage.InstancesCount) + return false; + return true; } /// diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index c725c3b8b..a79ef1e17 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -71,13 +71,17 @@ namespace FlaxEditor.Viewport public SceneRenderTask Task; /// - public override int Order => -10000000; + public EditorSpritesRenderer() + { + Order = -10000000; + UseSingleTarget = true; + } /// - public override bool UseSingleTarget => true; - - /// - public override bool CanRender => (Task.View.Flags & ViewFlags.EditorSprites) == ViewFlags.EditorSprites && Level.ScenesCount != 0 && base.CanRender; + public override bool CanRender() + { + return (Task.View.Flags & ViewFlags.EditorSprites) == ViewFlags.EditorSprites && Level.ScenesCount != 0 && base.CanRender(); + } /// public override void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output) @@ -202,13 +206,13 @@ namespace FlaxEditor.Viewport // Create post effects SelectionOutline = Object.New(); SelectionOutline.SelectionGetter = () => TransformGizmo.SelectedParents; - Task.CustomPostFx.Add(SelectionOutline); + Task.AddCustomPostFx(SelectionOutline); EditorPrimitives = Object.New(); EditorPrimitives.Viewport = this; - Task.CustomPostFx.Add(EditorPrimitives); + Task.AddCustomPostFx(EditorPrimitives); _editorSpritesRenderer = Object.New(); _editorSpritesRenderer.Task = Task; - Task.CustomPostFx.Add(_editorSpritesRenderer); + Task.AddCustomPostFx(_editorSpritesRenderer); // Add transformation gizmo TransformGizmo = new TransformGizmo(this); @@ -396,16 +400,13 @@ namespace FlaxEditor.Viewport if (_customSelectionOutline != null) { Object.Destroy(ref _customSelectionOutline); - - Task.CustomPostFx.Remove(_customSelectionOutline); - - Task.CustomPostFx.Add(customSelectionOutline ? customSelectionOutline : SelectionOutline); + Task.RemoveCustomPostFx(_customSelectionOutline); + Task.AddCustomPostFx(customSelectionOutline ? customSelectionOutline : SelectionOutline); } else if (customSelectionOutline != null) { - Task.CustomPostFx.Remove(SelectionOutline); - - Task.CustomPostFx.Add(customSelectionOutline); + Task.RemoveCustomPostFx(SelectionOutline); + Task.AddCustomPostFx(customSelectionOutline); } _customSelectionOutline = customSelectionOutline; @@ -509,20 +510,20 @@ namespace FlaxEditor.Viewport // Render editor primitives, gizmo and debug shapes in debug view modes // Note: can use Output buffer as both input and output because EditorPrimitives is using a intermediate buffers - if (EditorPrimitives && EditorPrimitives.CanRender) + if (EditorPrimitives && EditorPrimitives.CanRender()) { EditorPrimitives.Render(context, ref renderContext, task.Output, task.Output); } // Render editor sprites - if (_editorSpritesRenderer && _editorSpritesRenderer.CanRender) + if (_editorSpritesRenderer && _editorSpritesRenderer.CanRender()) { _editorSpritesRenderer.Render(context, ref renderContext, task.Output, task.Output); } // Render selection outline var selectionOutline = _customSelectionOutline ?? SelectionOutline; - if (selectionOutline && selectionOutline.CanRender) + if (selectionOutline && selectionOutline.CanRender()) { // Use temporary intermediate buffer var desc = task.Output.Description; diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 86b247fdc..14644b3ef 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -31,7 +31,11 @@ namespace FlaxEditor.Viewport { public PrefabWindowViewport Viewport; - public override bool CanRender => (Task.View.Flags & ViewFlags.EditorSprites) == ViewFlags.EditorSprites && Enabled; + /// + public override bool CanRender() + { + return (Task.View.Flags & ViewFlags.EditorSprites) == ViewFlags.EditorSprites && Enabled; + } protected override void Draw(ref RenderContext renderContext) { @@ -91,11 +95,11 @@ namespace FlaxEditor.Viewport // Create post effects SelectionOutline = FlaxEngine.Object.New(); SelectionOutline.SelectionGetter = () => TransformGizmo.SelectedParents; - Task.CustomPostFx.Add(SelectionOutline); + Task.AddCustomPostFx(SelectionOutline); _spritesRenderer = FlaxEngine.Object.New(); _spritesRenderer.Task = Task; _spritesRenderer.Viewport = this; - Task.CustomPostFx.Add(_spritesRenderer); + Task.AddCustomPostFx(_spritesRenderer); // Add transformation gizmo TransformGizmo = new TransformGizmo(this); @@ -270,13 +274,13 @@ namespace FlaxEditor.Viewport var task = renderContext.Task; // Render editor sprites - if (_spritesRenderer && _spritesRenderer.CanRender) + if (_spritesRenderer && _spritesRenderer.CanRender()) { _spritesRenderer.Render(context, ref renderContext, task.Output, task.Output); } // Render selection outline - if (SelectionOutline && SelectionOutline.CanRender) + if (SelectionOutline && SelectionOutline.CanRender()) { // Use temporary intermediate buffer var desc = task.Output.Description; diff --git a/Source/Editor/Viewport/Previews/AssetPreview.cs b/Source/Editor/Viewport/Previews/AssetPreview.cs index 973f9b563..f56a84080 100644 --- a/Source/Editor/Viewport/Previews/AssetPreview.cs +++ b/Source/Editor/Viewport/Previews/AssetPreview.cs @@ -114,7 +114,7 @@ namespace FlaxEditor.Viewport.Previews { _editorPrimitives = Object.New(); _editorPrimitives.Viewport = this; - Task.CustomPostFx.Add(_editorPrimitives); + Task.AddCustomPostFx(_editorPrimitives); Task.PostRender += OnPostRender; var view = Task.View; view.Flags |= ViewFlags.CustomPostProcess; @@ -203,7 +203,7 @@ namespace FlaxEditor.Viewport.Previews private void OnPostRender(GPUContext context, ref RenderContext renderContext) { - if (renderContext.View.Mode != ViewMode.Default && _editorPrimitives && _editorPrimitives.CanRender) + if (renderContext.View.Mode != ViewMode.Default && _editorPrimitives && _editorPrimitives.CanRender()) { // Render editor primitives, gizmo and debug shapes in debug view modes // Note: can use Output buffer as both input and output because EditorPrimitives is using a intermediate buffers diff --git a/Source/Engine/Engine/PostProcessEffect.cs b/Source/Engine/Engine/PostProcessEffect.cs deleted file mode 100644 index 5a85f4549..000000000 --- a/Source/Engine/Engine/PostProcessEffect.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. - -namespace FlaxEngine -{ - /// - /// Custom postFx which can modify final image by processing it with material based filters. - /// The base class for all post process effects used by the graphics pipeline. - /// Allows to extend frame rendering logic and apply custom effects such as outline, night vision, contrast etc. - /// - /// - /// Override this class and implement custom post fx logic. - /// Use MainRenderTask.Instance.CustomPostFx.Add(myPostFx) to attach your script to rendering. - /// Or add script to camera. - /// - public abstract class PostProcessEffect : Script - { - /// - /// Gets a value indicating whether this effect can be rendered. - /// - public virtual bool CanRender => Enabled; - - /// - /// Gets a value indicating whether use a single render target as both input and output. Use this if your effect doesn't need to copy the input buffer to the output but can render directly to the single texture. Can be used to optimize game performance. - /// - public virtual bool UseSingleTarget => false; - - /// - /// Gets the effect rendering location within rendering pipeline. - /// - public virtual PostProcessEffectLocation Location => PostProcessEffectLocation.Default; - - /// - /// Gets the effect rendering order. Registered post effects are sorted before rendering (from the lowest order to the highest order). - /// - public virtual int Order => 0; - - /// - /// Performs custom postFx rendering. - /// - /// The GPU commands context. - /// The rendering context. - /// The input texture. - /// The output texture. - public abstract void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output); - - internal static void FetchInfo(PostProcessEffect obj, out PostProcessEffectLocation location, out bool useSingleTarget) - { - location = obj.Location; - useSingleTarget = obj.UseSingleTarget; - } - } -} diff --git a/Source/Engine/Graphics/PostProcessBase.h b/Source/Engine/Graphics/PostProcessBase.h deleted file mode 100644 index d903b1278..000000000 --- a/Source/Engine/Graphics/PostProcessBase.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. - -#pragma once - -#include "Enums.h" - -class RenderTask; -class GPUTexture; -class GPUTextureView; -struct RenderContext; - -/// -/// Post process effects base class. -/// -class PostProcessBase -{ -protected: - bool _isEnabled; - bool _useSingleTarget; - PostProcessEffectLocation _location; - - PostProcessBase() - : _isEnabled(true) - , _useSingleTarget(false) - , _location(PostProcessEffectLocation::Default) - { - } - -public: - /// - /// Destructor - /// - virtual ~PostProcessBase() - { - } - -public: - /// - /// Returns true if effect is enabled - /// - /// True if is enabled, otherwise false - bool IsEnabled() const - { - return _isEnabled; - } - - /// - /// Set enabled state - /// - /// True if enable, otherwise disable effect - virtual void SetEnabled(bool enabled) - { - // Check if value will change - if (_isEnabled != enabled) - { - // Change value - _isEnabled = enabled; - - // Fire events - if (_isEnabled) - onEnable(); - else - onDisable(); - onEnabledChanged(); - } - } - - /// - /// Returns true if effect is loaded and can be rendered - /// - /// True if is loaded - virtual bool IsLoaded() const = 0; - - /// - /// Returns true if effect is ready for rendering - /// - /// True if is ready - bool IsReady() const - { - return _isEnabled && IsLoaded(); - } - - /// - /// Gets a value indicating whether use a single render target as both input and output. Use this if your effect doesn't need to copy the input buffer to the output but can render directly to the single texture. Can be used to optimize game performance. - /// - bool GetUseSingleTarget() const - { - return _useSingleTarget; - } - - /// - /// Gets the effect rendering location within rendering pipeline. - /// - /// Render location. - PostProcessEffectLocation GetLocation() const - { - return _location; - } - -public: - /// - /// Perform rendering - /// - /// The rendering context. - /// The input texture. - /// The output texture. - virtual void Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output) = 0; - -protected: - virtual void onEnable() - { - } - - virtual void onDisable() - { - } - - virtual void onEnabledChanged() - { - } -}; diff --git a/Source/Engine/Graphics/PostProcessEffect.h b/Source/Engine/Graphics/PostProcessEffect.h new file mode 100644 index 000000000..c02e428f6 --- /dev/null +++ b/Source/Engine/Graphics/PostProcessEffect.h @@ -0,0 +1,57 @@ +// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Scripting/Script.h" +#include "Enums.h" + +class GPUTexture; +class GPUContext; +struct RenderContext; + +/// +/// Custom PostFx which can modify final image by processing it with material based filters. The base class for all post process effects used by the graphics pipeline. Allows to extend frame rendering logic and apply custom effects such as outline, night vision, contrast etc. +/// +/// +/// Override this class and implement custom post fx logic. Use MainRenderTask.Instance.CustomPostFx.Add(myPostFx) to attach your script to rendering or add script to camera actor. +/// +API_CLASS(Abstract) class FLAXENGINE_API PostProcessEffect : public Script +{ + DECLARE_SCRIPTING_TYPE(PostProcessEffect); + +public: + /// + /// Effect rendering location within rendering pipeline. + /// + API_FIELD() PostProcessEffectLocation Location = PostProcessEffectLocation::Default; + + /// + /// True whether use a single render target as both input and output. Use this if your effect doesn't need to copy the input buffer to the output but can render directly to the single texture. Can be used to optimize game performance. + /// + API_FIELD() bool UseSingleTarget = false; + + /// + /// Effect rendering order. Post effects are sorted before rendering (from the lowest order to the highest order). + /// + API_FIELD() int32 Order = 0; + +public: + /// + /// Gets a value indicating whether this effect can be rendered. + /// + API_FUNCTION() virtual bool CanRender() const + { + return GetEnabled(); + } + + /// + /// Performs custom postFx rendering. + /// + /// The GPU commands context. + /// The rendering context. + /// The input texture. + /// The output texture. + API_FUNCTION() virtual void Render(GPUContext* context, API_PARAM(Ref) RenderContext& renderContext, GPUTexture* input, GPUTexture* output) + { + } +}; diff --git a/Source/Engine/Graphics/RenderTask.cpp b/Source/Engine/Graphics/RenderTask.cpp index 6428a3504..d1b6b1e24 100644 --- a/Source/Engine/Graphics/RenderTask.cpp +++ b/Source/Engine/Graphics/RenderTask.cpp @@ -4,7 +4,7 @@ #include "RenderBuffers.h" #include "GPUDevice.h" #include "GPUSwapChain.h" -#include "FlaxEngine.Gen.h" +#include "PostProcessEffect.h" #include "Engine/Core/Collections/Sorting.h" #include "Engine/Debug/DebugLog.h" #include "Engine/Level/Level.h" @@ -14,55 +14,25 @@ #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/Renderer/RenderList.h" #include "Engine/Threading/Threading.h" -#if USE_MONO -#include -#endif #if USE_EDITOR #include "Engine/Renderer/Lightmaps.h" #else #include "Engine/Engine/Screen.h" #endif -#if USE_MONO - -// TODO: use API for events and remove this manual wrapper code -struct RenderContextManaged -{ - MonoObject* Buffers; - MonoObject* List; - MonoObject* Task; - RenderView* LodProxyView; - RenderView View; -}; - -// TODO: use API for events and remove this manual wrapper code -namespace -{ - RenderContextManaged ToManaged(const RenderContext& value) - { - RenderContextManaged result; - result.Buffers = ScriptingObject::ToManaged((ScriptingObject*)value.Buffers); - result.List = ScriptingObject::ToManaged((ScriptingObject*)value.List); - result.Task = ScriptingObject::ToManaged((ScriptingObject*)value.Task); - result.LodProxyView = value.LodProxyView; - result.View = value.View; - return result; - } -} - -#endif - Array RenderTask::Tasks; CriticalSection RenderTask::TasksLocker; int32 RenderTask::TasksDoneLastFrame; +Array SceneRenderTask::GlobalCustomPostFx; MainRenderTask* MainRenderTask::Instance; +PostProcessEffect::PostProcessEffect(const SpawnParams& params) + : Script(params) +{ +} + void RenderTask::DrawAll() { ScopeLock lock(TasksLocker); @@ -160,62 +130,6 @@ 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; - MObject* 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) -{ -#if USE_MONO - 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; - RenderContextManaged tmp = ::ToManaged(renderContext); - void* params[4]; - params[0] = context->GetOrCreateManagedInstance(); - params[1] = &tmp; - params[2] = inputObj; - params[3] = outputObj; - MObject* exception = nullptr; - renderMethod->InvokeVirtual(Target->GetOrCreateManagedInstance(), params, &exception); - if (exception) - DebugLog::LogException(exception); -#endif -} - SceneRenderTask::SceneRenderTask(const SpawnParams& params) : RenderTask(params) { @@ -252,6 +166,26 @@ void SceneRenderTask::ClearCustomActors() CustomActors.Clear(); } +void SceneRenderTask::AddCustomPostFx(PostProcessEffect* fx) +{ + CustomPostFx.Add(fx); +} + +void SceneRenderTask::RemoveCustomPostFx(PostProcessEffect* fx) +{ + CustomPostFx.Remove(fx); +} + +void SceneRenderTask::AddGlobalCustomPostFx(PostProcessEffect* fx) +{ + GlobalCustomPostFx.Add(fx); +} + +void SceneRenderTask::RemoveGlobalCustomPostFx(PostProcessEffect* fx) +{ + GlobalCustomPostFx.Remove(fx); +} + void SceneRenderTask::CollectPostFxVolumes(RenderContext& renderContext) { // Cache WorldPosition used for PostFx volumes blending (RenderView caches it later on) @@ -263,7 +197,7 @@ void SceneRenderTask::CollectPostFxVolumes(RenderContext& renderContext) } if ((ActorsSource & ActorsSources::CustomActors) != 0) { - for (auto a : CustomActors) + for (Actor* a : CustomActors) { auto* postFxVolume = dynamic_cast(a); if (postFxVolume && a->GetIsActive()) @@ -285,8 +219,47 @@ void AddActorToSceneRendering(SceneRendering* s, Actor* a) } } +bool SortPostFx(PostProcessEffect* const& a, PostProcessEffect* const& b) +{ + return a->Order < b->Order; +} + void SceneRenderTask::OnCollectDrawCalls(RenderContextBatch& renderContextBatch, byte category) { + // Setup PostFx in the render list + if (category == SceneRendering::DrawCategory::PreRender) + { + const RenderContext& renderContext = renderContextBatch.GetMainContext(); + auto& postFx = renderContext.List->PostFx; + + // Collect all post effects + if (AllowGlobalCustomPostFx) + { + for (PostProcessEffect* fx : GlobalCustomPostFx) + { + if (fx && fx->CanRender()) + postFx.Add(fx); + } + } + for (PostProcessEffect* fx : CustomPostFx) + { + if (fx && fx->CanRender()) + postFx.Add(fx); + } + if (const auto* camera = Camera.Get()) + { + for (Script* script : camera->Scripts) + { + auto* fx = Cast(script); + if (fx && fx->CanRender()) + postFx.Add(fx); + } + } + + // Sort by order + Sorting::QuickSort(postFx.Get(), postFx.Count(), &SortPostFx); + } + // Draw actors (collect draw calls) if ((ActorsSource & ActorsSources::CustomActors) != 0) { @@ -373,33 +346,6 @@ void SceneRenderTask::OnBegin(GPUContext* context) View.CopyFrom(Camera, &viewport); } - // Get custom and global PostFx - CustomPostFx.Clear(); -#if !COMPILE_WITHOUT_CSHARP - // 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; - MObject* exception = nullptr; -#if USE_MONO - 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(); - } -#endif - } -#endif - // Setup render buffers for the output rendering resolution if (Output) { diff --git a/Source/Engine/Graphics/RenderTask.cs b/Source/Engine/Graphics/RenderTask.cs index 7d85263e8..5bb45d890 100644 --- a/Source/Engine/Graphics/RenderTask.cs +++ b/Source/Engine/Graphics/RenderTask.cs @@ -1,8 +1,5 @@ // Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. -using System; -using System.Collections.Generic; - namespace FlaxEngine { partial class RenderTask @@ -42,72 +39,5 @@ namespace FlaxEngine View = view; } } - - /// - /// The global custom post processing effects applied to all (applied to tasks that have turned on). - /// - public static readonly HashSet GlobalCustomPostFx = new HashSet(); - - /// - /// The custom post processing effects. - /// - public readonly HashSet CustomPostFx = new HashSet(); - - /// - /// True if allow using global custom PostFx when rendering this task. - /// - public bool AllowGlobalCustomPostFx = true; - - private static List _postFx; - private static IntPtr[] _postFxPtr; - private static Comparison _postFxComparison = (x, y) => x.Order - y.Order; - - internal IntPtr[] GetPostFx(out int count) - { - if (_postFx == null) - _postFx = new List(); - - // Get custom post effects to render (registered ones and from the current camera) - - foreach (var postFx in CustomPostFx) - { - if (postFx.CanRender) - _postFx.Add(postFx); - } - if (AllowGlobalCustomPostFx) - { - foreach (var postFx in GlobalCustomPostFx) - { - if (postFx.CanRender) - _postFx.Add(postFx); - } - } - var camera = Camera; - if (camera != null) - { - var perCameraPostFx = camera.GetScripts(); - for (int i = 0; i < perCameraPostFx.Length; i++) - { - var postFx = perCameraPostFx[i]; - if (postFx.CanRender) - _postFx.Add(postFx); - } - } - - // Sort postFx - _postFx.Sort(_postFxComparison); - - // Convert into unmanaged objects - count = _postFx.Count; - if (_postFxPtr == null || _postFxPtr.Length < count) - _postFxPtr = new IntPtr[_postFx.Capacity]; - for (int i = 0; i < count; i++) - _postFxPtr[i] = GetUnmanagedPtr(_postFx[i]); - - // Release references to managed objects - _postFx.Clear(); - - return _postFxPtr; - } } } diff --git a/Source/Engine/Graphics/RenderTask.h b/Source/Engine/Graphics/RenderTask.h index 30260e66e..c9a7aa7eb 100644 --- a/Source/Engine/Graphics/RenderTask.h +++ b/Source/Engine/Graphics/RenderTask.h @@ -9,7 +9,6 @@ #include "Engine/Scripting/ScriptingObjectReference.h" #include "Engine/Scripting/ScriptingType.h" #include "Engine/Renderer/RendererAllocation.h" -#include "PostProcessBase.h" #include "RenderView.h" class GPUDevice; @@ -18,6 +17,7 @@ class GPUTexture; class GPUTextureView; class GPUSwapChain; class RenderBuffers; +class PostProcessEffect; struct RenderContext; class Camera; class Actor; @@ -182,31 +182,6 @@ API_ENUM(Attributes="Flags") enum class ActorsSources DECLARE_ENUM_OPERATORS(ActorsSources); -/// -/// Wrapper for PostProcessBase that allows to render to customized managed postFx. -/// TODO: add support for managed inheritance of custom native types with proper spawn handling (like for ManagedScript) -/// -/// -class ManagedPostProcessEffect : public PostProcessBase -{ -public: - /// - /// The script to use. Inherits from C# class 'PostProcessEffect'. - /// - Script* Target = nullptr; - -public: - /// - /// Fetches the information about the PostFx location from the managed object. - /// - void FetchInfo(); - -public: - // [PostProcessBase] - bool IsLoaded() const override; - void Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output) override; -}; - /// /// Render task which draws scene actors into the output buffer. /// @@ -269,6 +244,7 @@ public: /// API_FIELD() float RenderingPercentage = 1.0f; +public: /// /// The custom set of actors to render. /// @@ -291,10 +267,45 @@ public: /// API_FUNCTION() void ClearCustomActors(); +public: /// - /// The custom post fx to render (managed). + /// The custom set of postfx to render. /// - Array CustomPostFx; + Array CustomPostFx; + + /// + /// Adds the custom postfx to the rendering. + /// + /// The postfx script. + API_FUNCTION() void AddCustomPostFx(PostProcessEffect* fx); + + /// + /// Removes the custom postfx from the rendering. + /// + /// The postfx script. + API_FUNCTION() void RemoveCustomPostFx(PostProcessEffect* fx); + + /// + /// True if allow using global custom PostFx when rendering this task. + /// + API_FIELD() bool AllowGlobalCustomPostFx = true; + + /// + /// The custom set of global postfx to render for all (applied to tasks that have turned on). + /// + static Array GlobalCustomPostFx; + + /// + /// Adds the custom global postfx to the rendering. + /// + /// The postfx script. + API_FUNCTION() static void AddGlobalCustomPostFx(PostProcessEffect* fx); + + /// + /// Removes the custom global postfx from the rendering. + /// + /// The postfx script. + API_FUNCTION() static void RemoveGlobalCustomPostFx(PostProcessEffect* fx); public: /// diff --git a/Source/Engine/Renderer/EyeAdaptationPass.cpp b/Source/Engine/Renderer/EyeAdaptationPass.cpp index 00b15192f..ea5013a3d 100644 --- a/Source/Engine/Renderer/EyeAdaptationPass.cpp +++ b/Source/Engine/Renderer/EyeAdaptationPass.cpp @@ -9,7 +9,6 @@ #include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUContext.h" #include "Engine/Graphics/GPULimits.h" -#include "Engine/Graphics/PostProcessBase.h" #include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderTargetPool.h" diff --git a/Source/Engine/Renderer/HistogramPass.cpp b/Source/Engine/Renderer/HistogramPass.cpp index acff2adf9..8d5c3b492 100644 --- a/Source/Engine/Renderer/HistogramPass.cpp +++ b/Source/Engine/Renderer/HistogramPass.cpp @@ -4,7 +4,6 @@ #include "RenderList.h" #include "Engine/Content/Assets/Shader.h" #include "Engine/Content/Content.h" -#include "Engine/Graphics/PostProcessBase.h" #include "Engine/Graphics/GPUContext.h" #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/GPULimits.h" diff --git a/Source/Engine/Renderer/PostProcessingPass.cpp b/Source/Engine/Renderer/PostProcessingPass.cpp index a7d73f279..99d1ccd0e 100644 --- a/Source/Engine/Renderer/PostProcessingPass.cpp +++ b/Source/Engine/Renderer/PostProcessingPass.cpp @@ -4,10 +4,8 @@ #include "RenderList.h" #include "Engine/Content/Assets/Shader.h" #include "Engine/Content/Content.h" -#include "Engine/Graphics/PostProcessBase.h" #include "Engine/Graphics/GPUContext.h" #include "Engine/Graphics/RenderTask.h" -#include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/RenderTargetPool.h" #include "Engine/Engine/Time.h" diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index 92dafa21e..a2411cdaf 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -6,13 +6,13 @@ #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/GPUContext.h" #include "Engine/Graphics/GPUDevice.h" -#include "Engine/Graphics/PostProcessBase.h" #include "Engine/Graphics/GPULimits.h" #include "Engine/Graphics/RenderTargetPool.h" #include "Engine/Graphics/RenderTools.h" +#include "Engine/Graphics/Graphics.h" +#include "Engine/Graphics/PostProcessEffect.h" #include "Engine/Profiler/Profiler.h" #include "Engine/Content/Assets/CubeTexture.h" -#include "Engine/Graphics/Graphics.h" #include "Engine/Level/Scene/Lightmap.h" #include "Engine/Level/Actors/PostFxVolume.h" @@ -220,13 +220,12 @@ void RenderList::RunPostFxPass(GPUContext* context, RenderContext& renderContext } if (renderContext.View.Flags & ViewFlags::CustomPostProcess) { - for (int32 i = 0; i < renderContext.List->PostFx.Count(); i++) + for (const PostProcessEffect* fx : renderContext.List->PostFx) { - const auto fx = renderContext.List->PostFx[i]; - if (fx->IsReady() && fx->GetLocation() == locationB) + if (fx->Location == locationB) { skipPass = false; - needTempTarget |= !fx->GetUseSingleTarget(); + needTempTarget |= !fx->UseSingleTarget; } } } @@ -258,19 +257,18 @@ void RenderList::RunPostFxPass(GPUContext* context, RenderContext& renderContext } if (renderContext.View.Flags & ViewFlags::CustomPostProcess) { - for (int32 i = 0; i < renderContext.List->PostFx.Count(); i++) + for (PostProcessEffect* fx : renderContext.List->PostFx) { - auto fx = renderContext.List->PostFx[i]; - if (fx->IsReady() && fx->GetLocation() == locationB) + if (fx->Location == locationB) { - if (fx->GetUseSingleTarget()) + if (fx->UseSingleTarget) { - fx->Render(renderContext, input, nullptr); + fx->Render(context, renderContext, input, nullptr); } else { ASSERT(needTempTarget); - fx->Render(renderContext, input, output); + fx->Render(context, renderContext, input, output); Swap(input, output); } context->ResetRenderTarget(); @@ -306,19 +304,17 @@ void RenderList::RunCustomPostFxPass(GPUContext* context, RenderContext& renderC { if (!(renderContext.View.Flags & ViewFlags::CustomPostProcess)) return; - - for (int32 i = 0; i < renderContext.List->PostFx.Count(); i++) + for (PostProcessEffect* fx : renderContext.List->PostFx) { - auto fx = renderContext.List->PostFx[i]; - if (fx->IsReady() && fx->GetLocation() == location) + if (fx->Location == location) { - if (fx->GetUseSingleTarget()) + if (fx->UseSingleTarget) { - fx->Render(renderContext, input, nullptr); + fx->Render(context, renderContext, input, nullptr); } else { - fx->Render(renderContext, input, output); + fx->Render(context, renderContext, input, output); Swap(input, output); } context->ResetRenderTarget(); @@ -330,13 +326,10 @@ bool RenderList::HasAnyPostFx(const RenderContext& renderContext, PostProcessEff { if (renderContext.View.Flags & ViewFlags::CustomPostProcess) { - for (int32 i = 0; i < renderContext.List->PostFx.Count(); i++) + for (const PostProcessEffect* fx : renderContext.List->PostFx) { - auto fx = renderContext.List->PostFx[i]; - if (fx->IsReady() && fx->GetLocation() == postProcess) - { + if (fx->Location == postProcess) return true; - } } } return false; diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index b4048eca0..fd3b74658 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -12,11 +12,11 @@ enum class StaticFlags; class RenderBuffers; +class PostProcessEffect; class SceneRendering; class LightWithShadow; class IPostFxSettingsProvider; class CubeTexture; -class PostProcessBase; struct RenderContext; struct RendererDirectionalLightData @@ -352,9 +352,9 @@ public: IFogRenderer* Fog; /// - /// Post effect to render (TEMPORARY! cleared after and before rendering). + /// Post effects to render. /// - Array PostFx; + Array PostFx; /// /// The post process settings. diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 61dca918a..98bc4812e 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -315,11 +315,6 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont if (renderContext.View.Origin != renderContext.View.PrevOrigin) renderContext.Task->CameraCut(); // Cut any temporal effects on rendering origin change renderContext.Buffers->Prepare(); - for (auto& postFx : task->CustomPostFx) - { - if (postFx.Target) - renderContext.List->PostFx.Add(&postFx); - } // Build batch of render contexts (main view and shadow projections) const bool isGBufferDebug = GBufferPass::IsDebugView(renderContext.View.Mode); diff --git a/Source/Engine/UI/UICanvas.cs b/Source/Engine/UI/UICanvas.cs index d305d2bb0..731879d99 100644 --- a/Source/Engine/UI/UICanvas.cs +++ b/Source/Engine/UI/UICanvas.cs @@ -48,13 +48,20 @@ namespace FlaxEngine public UICanvas Canvas; /// - public override bool UseSingleTarget => true; + public CanvasRenderer() + { + UseSingleTarget = true; + } /// - public override PostProcessEffectLocation Location => Canvas.RenderLocation; + public override bool CanRender() + { + // Sync with canvas options + Location = Canvas.RenderLocation; + Order = Canvas.Order; - /// - public override int Order => Canvas.Order; + return base.CanRender(); + } /// public override void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output) @@ -486,9 +493,9 @@ namespace FlaxEngine if (_renderer) { #if FLAX_EDITOR - _editorTask?.CustomPostFx.Remove(_renderer); + _editorTask?.RemoveCustomPostFx(_renderer); #endif - SceneRenderTask.GlobalCustomPostFx.Remove(_renderer); + SceneRenderTask.RemoveGlobalCustomPostFx(_renderer); _renderer.Canvas = null; Destroy(_renderer); _renderer = null; @@ -526,16 +533,16 @@ namespace FlaxEngine #if FLAX_EDITOR if (_editorTask != null) { - _editorTask.CustomPostFx.Add(_renderer); + _editorTask.AddCustomPostFx(_renderer); break; } #endif - SceneRenderTask.GlobalCustomPostFx.Add(_renderer); + SceneRenderTask.AddGlobalCustomPostFx(_renderer); } #if FLAX_EDITOR else if (_editorTask != null && IsActiveInHierarchy) { - _editorTask.CustomPostFx.Add(_renderer); + _editorTask.AddCustomPostFx(_renderer); } #endif } @@ -777,11 +784,11 @@ namespace FlaxEngine #if FLAX_EDITOR if (_editorTask != null) { - _editorTask.CustomPostFx.Add(_renderer); + _editorTask.AddCustomPostFx(_renderer); return; } #endif - SceneRenderTask.GlobalCustomPostFx.Add(_renderer); + SceneRenderTask.AddGlobalCustomPostFx(_renderer); } } @@ -791,7 +798,7 @@ namespace FlaxEngine if (_renderer) { - SceneRenderTask.GlobalCustomPostFx.Remove(_renderer); + SceneRenderTask.RemoveGlobalCustomPostFx(_renderer); } } @@ -816,7 +823,7 @@ namespace FlaxEngine if (_renderer) { - SceneRenderTask.GlobalCustomPostFx.Remove(_renderer); + SceneRenderTask.RemoveGlobalCustomPostFx(_renderer); _renderer.Canvas = null; Destroy(_renderer); _renderer = null; @@ -832,7 +839,7 @@ namespace FlaxEngine if (_editorTask == task && _editorRoot == root) return; if (_editorTask != null && _renderer != null) - _editorTask.CustomPostFx.Remove(_renderer); + _editorTask.RemoveCustomPostFx(_renderer); if (_editorRoot != null && _guiRoot != null) _guiRoot.Parent = null;