Refactor PostProcessEffect to support C++ scripting

This commit is contained in:
Wojtek Figat
2022-11-02 20:29:59 +01:00
parent 86b6b20200
commit 6a61b692aa
19 changed files with 253 additions and 483 deletions

View File

@@ -33,7 +33,10 @@ namespace FlaxEditor.Gizmo
public IEditorPrimitivesOwner Viewport;
/// <inheritdoc />
public override int Order => -100;
public EditorPrimitives()
{
Order = -100;
}
/// <inheritdoc />
public override void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output)

View File

@@ -114,7 +114,10 @@ namespace FlaxEditor.Gizmo
protected bool HasDataReady => _enabled && _material && _outlineMaterial.IsLoaded;
/// <inheritdoc />
public override bool CanRender => _enabled && _material && _outlineMaterial.IsLoaded && SelectionGetter().Count > 0;
public override bool CanRender()
{
return _enabled && _material && _outlineMaterial.IsLoaded && SelectionGetter().Count > 0;
}
/// <inheritdoc />
public override void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output)

View File

@@ -20,21 +20,18 @@ namespace FlaxEditor.Tools.Foliage
public EditFoliageGizmoMode GizmoMode;
/// <inheritdoc />
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;
}
/// <inheritdoc />

View File

@@ -71,13 +71,17 @@ namespace FlaxEditor.Viewport
public SceneRenderTask Task;
/// <inheritdoc />
public override int Order => -10000000;
public EditorSpritesRenderer()
{
Order = -10000000;
UseSingleTarget = true;
}
/// <inheritdoc />
public override bool UseSingleTarget => true;
/// <inheritdoc />
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();
}
/// <inheritdoc />
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>();
SelectionOutline.SelectionGetter = () => TransformGizmo.SelectedParents;
Task.CustomPostFx.Add(SelectionOutline);
Task.AddCustomPostFx(SelectionOutline);
EditorPrimitives = Object.New<EditorPrimitives>();
EditorPrimitives.Viewport = this;
Task.CustomPostFx.Add(EditorPrimitives);
Task.AddCustomPostFx(EditorPrimitives);
_editorSpritesRenderer = Object.New<EditorSpritesRenderer>();
_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;

View File

@@ -31,7 +31,11 @@ namespace FlaxEditor.Viewport
{
public PrefabWindowViewport Viewport;
public override bool CanRender => (Task.View.Flags & ViewFlags.EditorSprites) == ViewFlags.EditorSprites && Enabled;
/// <inheritdoc />
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>();
SelectionOutline.SelectionGetter = () => TransformGizmo.SelectedParents;
Task.CustomPostFx.Add(SelectionOutline);
Task.AddCustomPostFx(SelectionOutline);
_spritesRenderer = FlaxEngine.Object.New<PrefabSpritesRenderer>();
_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;

View File

@@ -114,7 +114,7 @@ namespace FlaxEditor.Viewport.Previews
{
_editorPrimitives = Object.New<EditorPrimitives>();
_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

View File

@@ -1,52 +0,0 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
namespace FlaxEngine
{
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// Override this class and implement custom post fx logic.
/// Use <b>MainRenderTask.Instance.CustomPostFx.Add(myPostFx)</b> to attach your script to rendering.
/// Or add script to camera.
/// </remarks>
public abstract class PostProcessEffect : Script
{
/// <summary>
/// Gets a value indicating whether this effect can be rendered.
/// </summary>
public virtual bool CanRender => Enabled;
/// <summary>
/// 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.
/// </summary>
public virtual bool UseSingleTarget => false;
/// <summary>
/// Gets the effect rendering location within rendering pipeline.
/// </summary>
public virtual PostProcessEffectLocation Location => PostProcessEffectLocation.Default;
/// <summary>
/// Gets the effect rendering order. Registered post effects are sorted before rendering (from the lowest order to the highest order).
/// </summary>
public virtual int Order => 0;
/// <summary>
/// Performs custom postFx rendering.
/// </summary>
/// <param name="context">The GPU commands context.</param>
/// <param name="renderContext">The rendering context.</param>
/// <param name="input">The input texture.</param>
/// <param name="output">The output texture.</param>
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;
}
}
}

View File

@@ -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;
/// <summary>
/// Post process effects base class.
/// </summary>
class PostProcessBase
{
protected:
bool _isEnabled;
bool _useSingleTarget;
PostProcessEffectLocation _location;
PostProcessBase()
: _isEnabled(true)
, _useSingleTarget(false)
, _location(PostProcessEffectLocation::Default)
{
}
public:
/// <summary>
/// Destructor
/// </summary>
virtual ~PostProcessBase()
{
}
public:
/// <summary>
/// Returns true if effect is enabled
/// </summary>
/// <returns>True if is enabled, otherwise false</returns>
bool IsEnabled() const
{
return _isEnabled;
}
/// <summary>
/// Set enabled state
/// </summary>
/// <param name="enabled">True if enable, otherwise disable effect</param>
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();
}
}
/// <summary>
/// Returns true if effect is loaded and can be rendered
/// </summary>
/// <returns>True if is loaded</returns>
virtual bool IsLoaded() const = 0;
/// <summary>
/// Returns true if effect is ready for rendering
/// </summary>
/// <returns>True if is ready</returns>
bool IsReady() const
{
return _isEnabled && IsLoaded();
}
/// <summary>
/// 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.
/// </summary>
bool GetUseSingleTarget() const
{
return _useSingleTarget;
}
/// <summary>
/// Gets the effect rendering location within rendering pipeline.
/// </summary>
/// <returns>Render location.</returns>
PostProcessEffectLocation GetLocation() const
{
return _location;
}
public:
/// <summary>
/// Perform rendering
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="input">The input texture.</param>
/// <param name="output">The output texture.</param>
virtual void Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output) = 0;
protected:
virtual void onEnable()
{
}
virtual void onDisable()
{
}
virtual void onEnabledChanged()
{
}
};

View File

@@ -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;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// Override this class and implement custom post fx logic. Use <b>MainRenderTask.Instance.CustomPostFx.Add(myPostFx)</b> to attach your script to rendering or add script to camera actor.
/// </remarks>
API_CLASS(Abstract) class FLAXENGINE_API PostProcessEffect : public Script
{
DECLARE_SCRIPTING_TYPE(PostProcessEffect);
public:
/// <summary>
/// Effect rendering location within rendering pipeline.
/// </summary>
API_FIELD() PostProcessEffectLocation Location = PostProcessEffectLocation::Default;
/// <summary>
/// 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.
/// </summary>
API_FIELD() bool UseSingleTarget = false;
/// <summary>
/// Effect rendering order. Post effects are sorted before rendering (from the lowest order to the highest order).
/// </summary>
API_FIELD() int32 Order = 0;
public:
/// <summary>
/// Gets a value indicating whether this effect can be rendered.
/// </summary>
API_FUNCTION() virtual bool CanRender() const
{
return GetEnabled();
}
/// <summary>
/// Performs custom postFx rendering.
/// </summary>
/// <param name="context">The GPU commands context.</param>
/// <param name="renderContext">The rendering context.</param>
/// <param name="input">The input texture.</param>
/// <param name="output">The output texture.</param>
API_FUNCTION() virtual void Render(GPUContext* context, API_PARAM(Ref) RenderContext& renderContext, GPUTexture* input, GPUTexture* output)
{
}
};

View File

@@ -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 <ThirdParty/mono-2.0/mono/metadata/appdomain.h>
#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*> RenderTask::Tasks;
CriticalSection RenderTask::TasksLocker;
int32 RenderTask::TasksDoneLastFrame;
Array<PostProcessEffect*> 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<PostFxVolume*>(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<PostProcessEffect>(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)
{

View File

@@ -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;
}
}
/// <summary>
/// The global custom post processing effects applied to all <see cref="SceneRenderTask"/> (applied to tasks that have <see cref="AllowGlobalCustomPostFx"/> turned on).
/// </summary>
public static readonly HashSet<PostProcessEffect> GlobalCustomPostFx = new HashSet<PostProcessEffect>();
/// <summary>
/// The custom post processing effects.
/// </summary>
public readonly HashSet<PostProcessEffect> CustomPostFx = new HashSet<PostProcessEffect>();
/// <summary>
/// True if allow using global custom PostFx when rendering this task.
/// </summary>
public bool AllowGlobalCustomPostFx = true;
private static List<PostProcessEffect> _postFx;
private static IntPtr[] _postFxPtr;
private static Comparison<PostProcessEffect> _postFxComparison = (x, y) => x.Order - y.Order;
internal IntPtr[] GetPostFx(out int count)
{
if (_postFx == null)
_postFx = new List<PostProcessEffect>();
// 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<PostProcessEffect>();
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;
}
}
}

View File

@@ -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);
/// <summary>
/// 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)
/// </summary>
/// <seealso cref="PostProcessBase" />
class ManagedPostProcessEffect : public PostProcessBase
{
public:
/// <summary>
/// The script to use. Inherits from C# class 'PostProcessEffect'.
/// </summary>
Script* Target = nullptr;
public:
/// <summary>
/// Fetches the information about the PostFx location from the managed object.
/// </summary>
void FetchInfo();
public:
// [PostProcessBase]
bool IsLoaded() const override;
void Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output) override;
};
/// <summary>
/// Render task which draws scene actors into the output buffer.
/// </summary>
@@ -269,6 +244,7 @@ public:
/// </summary>
API_FIELD() float RenderingPercentage = 1.0f;
public:
/// <summary>
/// The custom set of actors to render.
/// </summary>
@@ -291,10 +267,45 @@ public:
/// </summary>
API_FUNCTION() void ClearCustomActors();
public:
/// <summary>
/// The custom post fx to render (managed).
/// The custom set of postfx to render.
/// </summary>
Array<ManagedPostProcessEffect> CustomPostFx;
Array<PostProcessEffect*> CustomPostFx;
/// <summary>
/// Adds the custom postfx to the rendering.
/// </summary>
/// <param name="fx">The postfx script.</param>
API_FUNCTION() void AddCustomPostFx(PostProcessEffect* fx);
/// <summary>
/// Removes the custom postfx from the rendering.
/// </summary>
/// <param name="fx">The postfx script.</param>
API_FUNCTION() void RemoveCustomPostFx(PostProcessEffect* fx);
/// <summary>
/// True if allow using global custom PostFx when rendering this task.
/// </summary>
API_FIELD() bool AllowGlobalCustomPostFx = true;
/// <summary>
/// The custom set of global postfx to render for all <see cref="SceneRenderTask"/> (applied to tasks that have <see cref="AllowGlobalCustomPostFx"/> turned on).
/// </summary>
static Array<PostProcessEffect*> GlobalCustomPostFx;
/// <summary>
/// Adds the custom global postfx to the rendering.
/// </summary>
/// <param name="fx">The postfx script.</param>
API_FUNCTION() static void AddGlobalCustomPostFx(PostProcessEffect* fx);
/// <summary>
/// Removes the custom global postfx from the rendering.
/// </summary>
/// <param name="fx">The postfx script.</param>
API_FUNCTION() static void RemoveGlobalCustomPostFx(PostProcessEffect* fx);
public:
/// <summary>

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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;

View File

@@ -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;
/// <summary>
/// Post effect to render (TEMPORARY! cleared after and before rendering).
/// Post effects to render.
/// </summary>
Array<PostProcessBase*> PostFx;
Array<PostProcessEffect*> PostFx;
/// <summary>
/// The post process settings.

View File

@@ -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);

View File

@@ -48,13 +48,20 @@ namespace FlaxEngine
public UICanvas Canvas;
/// <inheritdoc />
public override bool UseSingleTarget => true;
public CanvasRenderer()
{
UseSingleTarget = true;
}
/// <inheritdoc />
public override PostProcessEffectLocation Location => Canvas.RenderLocation;
public override bool CanRender()
{
// Sync with canvas options
Location = Canvas.RenderLocation;
Order = Canvas.Order;
/// <inheritdoc />
public override int Order => Canvas.Order;
return base.CanRender();
}
/// <inheritdoc />
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;