Refactor Color Grading LUT rendering to have config for 2D/3D mode
This commit is contained in:
@@ -26,6 +26,7 @@ Quality Graphics::GIQuality = Quality::High;
|
|||||||
bool Graphics::GICascadesBlending = false;
|
bool Graphics::GICascadesBlending = false;
|
||||||
PostProcessSettings Graphics::PostProcessSettings;
|
PostProcessSettings Graphics::PostProcessSettings;
|
||||||
bool Graphics::SpreadWorkload = true;
|
bool Graphics::SpreadWorkload = true;
|
||||||
|
bool Graphics::PostProcessing::ColorGradingVolumeLUT = true;
|
||||||
|
|
||||||
#if GRAPHICS_API_NULL
|
#if GRAPHICS_API_NULL
|
||||||
extern GPUDevice* CreateGPUDeviceNull();
|
extern GPUDevice* CreateGPUDeviceNull();
|
||||||
|
|||||||
@@ -84,6 +84,16 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
API_FIELD() static bool SpreadWorkload;
|
API_FIELD() static bool SpreadWorkload;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Post Processing effects rendering configuration.
|
||||||
|
API_CLASS(Static, Attributes = "DebugCommand") class FLAXENGINE_API PostProcessing
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_MINIMAL(PostProcessing);
|
||||||
|
|
||||||
|
// Toggles between 2D and 3D LUT texture for Color Grading.
|
||||||
|
API_FIELD() static bool ColorGradingVolumeLUT;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Disposes the device.
|
/// Disposes the device.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "Engine/Content/Content.h"
|
#include "Engine/Content/Content.h"
|
||||||
#include "Engine/Graphics/GPUContext.h"
|
#include "Engine/Graphics/GPUContext.h"
|
||||||
#include "Engine/Graphics/GPULimits.h"
|
#include "Engine/Graphics/GPULimits.h"
|
||||||
|
#include "Engine/Graphics/Graphics.h"
|
||||||
#include "Engine/Graphics/RenderTargetPool.h"
|
#include "Engine/Graphics/RenderTargetPool.h"
|
||||||
#include "Engine/Graphics/RenderTask.h"
|
#include "Engine/Graphics/RenderTask.h"
|
||||||
|
|
||||||
@@ -36,12 +37,6 @@ GPU_CB_STRUCT(Data {
|
|||||||
float LutWeight;
|
float LutWeight;
|
||||||
});
|
});
|
||||||
|
|
||||||
ColorGradingPass::ColorGradingPass()
|
|
||||||
: _useVolumeTexture(false)
|
|
||||||
, _lutFormat()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
String ColorGradingPass::ToString() const
|
String ColorGradingPass::ToString() const
|
||||||
{
|
{
|
||||||
return TEXT("ColorGradingPass");
|
return TEXT("ColorGradingPass");
|
||||||
@@ -49,55 +44,28 @@ String ColorGradingPass::ToString() const
|
|||||||
|
|
||||||
bool ColorGradingPass::Init()
|
bool ColorGradingPass::Init()
|
||||||
{
|
{
|
||||||
// Detect if can use volume texture (3d) for a LUT (faster, requires geometry shader)
|
|
||||||
const auto device = GPUDevice::Instance;
|
|
||||||
#if GPU_ALLOW_GEOMETRY_SHADERS
|
|
||||||
_useVolumeTexture = device->Limits.HasGeometryShaders && device->Limits.HasVolumeTextureRendering;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Pick a proper LUT pixels format
|
|
||||||
_lutFormat = PixelFormat::R10G10B10A2_UNorm;
|
|
||||||
const auto formatSupport = device->GetFormatFeatures(_lutFormat).Support;
|
|
||||||
FormatSupport formatSupportFlags = FormatSupport::ShaderSample | FormatSupport::RenderTarget;
|
|
||||||
if (_useVolumeTexture)
|
|
||||||
formatSupportFlags |= FormatSupport::Texture3D;
|
|
||||||
else
|
|
||||||
formatSupportFlags |= FormatSupport::Texture2D;
|
|
||||||
if (EnumHasNoneFlags(formatSupport, formatSupportFlags))
|
|
||||||
{
|
|
||||||
// Fallback to format that is supported on every washing machine
|
|
||||||
_lutFormat = PixelFormat::R8G8B8A8_UNorm;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create pipeline state
|
|
||||||
_psLut.CreatePipelineStates();
|
_psLut.CreatePipelineStates();
|
||||||
|
|
||||||
// Load shader
|
|
||||||
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/ColorGrading"));
|
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/ColorGrading"));
|
||||||
if (_shader == nullptr)
|
if (_shader == nullptr)
|
||||||
return true;
|
return true;
|
||||||
#if COMPILE_WITH_DEV_ENV
|
#if COMPILE_WITH_DEV_ENV
|
||||||
_shader.Get()->OnReloading.Bind<ColorGradingPass, &ColorGradingPass::OnShaderReloading>(this);
|
_shader.Get()->OnReloading.Bind<ColorGradingPass, &ColorGradingPass::OnShaderReloading>(this);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ColorGradingPass::setupResources()
|
bool ColorGradingPass::setupResources()
|
||||||
{
|
{
|
||||||
// Wait for shader
|
|
||||||
if (!_shader || !_shader->IsLoaded())
|
if (!_shader || !_shader->IsLoaded())
|
||||||
return true;
|
return true;
|
||||||
const auto shader = _shader->GetShader();
|
const auto shader = _shader->GetShader();
|
||||||
CHECK_INVALID_SHADER_PASS_CB_SIZE(shader, 0, Data);
|
CHECK_INVALID_SHADER_PASS_CB_SIZE(shader, 0, Data);
|
||||||
|
|
||||||
// Create pipeline stages
|
// Create pipeline stage
|
||||||
GPUPipelineState::Description psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
|
auto psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
|
||||||
if (!_psLut.IsValid())
|
|
||||||
{
|
|
||||||
StringAnsiView psName;
|
StringAnsiView psName;
|
||||||
#if GPU_ALLOW_GEOMETRY_SHADERS
|
#if GPU_ALLOW_GEOMETRY_SHADERS
|
||||||
if (_useVolumeTexture)
|
if (_use3D == 1)
|
||||||
{
|
{
|
||||||
psDesc.VS = shader->GetVS("VS_WriteToSlice");
|
psDesc.VS = shader->GetVS("VS_WriteToSlice");
|
||||||
psDesc.GS = shader->GetGS("GS_WriteToSlice");
|
psDesc.GS = shader->GetGS("GS_WriteToSlice");
|
||||||
@@ -110,42 +78,53 @@ bool ColorGradingPass::setupResources()
|
|||||||
}
|
}
|
||||||
if (_psLut.Create(psDesc, shader, psName))
|
if (_psLut.Create(psDesc, shader, psName))
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ColorGradingPass::Dispose()
|
void ColorGradingPass::Dispose()
|
||||||
{
|
{
|
||||||
// Base
|
|
||||||
RendererPass::Dispose();
|
RendererPass::Dispose();
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
_psLut.Delete();
|
_psLut.Delete();
|
||||||
_shader = nullptr;
|
_shader = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext)
|
GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext)
|
||||||
{
|
{
|
||||||
|
// Check if can use volume texture (3D) for a LUT (faster on modern platforms, requires geometry shader)
|
||||||
|
const auto device = GPUDevice::Instance;
|
||||||
|
bool use3D = GPU_ALLOW_GEOMETRY_SHADERS && Graphics::PostProcessing::ColorGradingVolumeLUT;
|
||||||
|
use3D &= device->Limits.HasGeometryShaders && device->Limits.HasVolumeTextureRendering;
|
||||||
|
use3D &= !PLATFORM_SWITCH; // TODO: move this in future to platform-specific configs
|
||||||
|
|
||||||
|
// Rebuild PSO on change
|
||||||
|
if (_use3D != (int32)use3D)
|
||||||
|
{
|
||||||
|
invalidateResources();
|
||||||
|
_use3D = use3D;
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure to have valid data
|
// Ensure to have valid data
|
||||||
if (checkIfSkipPass())
|
if (checkIfSkipPass())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
PROFILE_GPU_CPU("Color Grading LUT");
|
PROFILE_GPU_CPU("Color Grading LUT");
|
||||||
|
|
||||||
// For a 3D texture, the viewport is 16x16 (per slice), for a 2D texture, it's unwrapped to 256x16
|
// Pick a proper LUT pixels format
|
||||||
const int32 LutSize = 32; // this must match value in shader (see ColorGrading.shader and PostProcessing.shader)
|
auto lutFormat = PixelFormat::R10G10B10A2_UNorm;
|
||||||
|
const auto formatSupport = device->GetFormatFeatures(lutFormat).Support;
|
||||||
|
FormatSupport formatSupportFlags = FormatSupport::ShaderSample | FormatSupport::RenderTarget;
|
||||||
|
formatSupportFlags |= use3D ? FormatSupport::Texture3D : FormatSupport::Texture2D;
|
||||||
|
if (EnumHasNoneFlags(formatSupport, formatSupportFlags))
|
||||||
|
lutFormat = PixelFormat::R8G8B8A8_UNorm;
|
||||||
|
|
||||||
|
// For a 3D texture, the viewport is 32x32 (per slice), for a 2D texture, it's unwrapped to 1024x32
|
||||||
|
constexpr int32 lutSize = 32; // this must match value in shader (see ColorGrading.shader and PostProcessing.shader)
|
||||||
GPUTextureDescription lutDesc;
|
GPUTextureDescription lutDesc;
|
||||||
#if GPU_ALLOW_GEOMETRY_SHADERS
|
if (use3D)
|
||||||
if (_useVolumeTexture)
|
lutDesc = GPUTextureDescription::New3D(lutSize, lutSize, lutSize, 1, lutFormat);
|
||||||
{
|
|
||||||
lutDesc = GPUTextureDescription::New3D(LutSize, LutSize, LutSize, 1, _lutFormat);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
#endif
|
lutDesc = GPUTextureDescription::New2D(lutSize * lutSize, lutSize, 1, lutFormat);
|
||||||
{
|
|
||||||
lutDesc = GPUTextureDescription::New2D(LutSize * LutSize, LutSize, 1, _lutFormat);
|
|
||||||
}
|
|
||||||
const auto lut = RenderTargetPool::Get(lutDesc);
|
const auto lut = RenderTargetPool::Get(lutDesc);
|
||||||
RENDER_TARGET_POOL_SET_NAME(lut, "ColorGrading.LUT");
|
RENDER_TARGET_POOL_SET_NAME(lut, "ColorGrading.LUT");
|
||||||
|
|
||||||
@@ -181,7 +160,6 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext)
|
|||||||
data.LutWeight = useLut ? colorGrading.LutWeight : 0.0f;
|
data.LutWeight = useLut ? colorGrading.LutWeight : 0.0f;
|
||||||
|
|
||||||
// Prepare
|
// Prepare
|
||||||
auto device = GPUDevice::Instance;
|
|
||||||
auto context = device->GetMainContext();
|
auto context = device->GetMainContext();
|
||||||
const auto cb = _shader->GetShader()->GetCB(0);
|
const auto cb = _shader->GetShader()->GetCB(0);
|
||||||
context->UpdateCB(cb, &data);
|
context->UpdateCB(cb, &data);
|
||||||
@@ -192,7 +170,7 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext)
|
|||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
#if GPU_ALLOW_GEOMETRY_SHADERS
|
#if GPU_ALLOW_GEOMETRY_SHADERS
|
||||||
if (_useVolumeTexture)
|
if (use3D)
|
||||||
{
|
{
|
||||||
context->SetRenderTarget(lut->ViewVolume());
|
context->SetRenderTarget(lut->ViewVolume());
|
||||||
|
|
||||||
|
|||||||
@@ -11,30 +11,19 @@
|
|||||||
class ColorGradingPass : public RendererPass<ColorGradingPass>
|
class ColorGradingPass : public RendererPass<ColorGradingPass>
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
int32 _use3D = -1;
|
||||||
bool _useVolumeTexture;
|
|
||||||
PixelFormat _lutFormat;
|
|
||||||
AssetReference<Shader> _shader;
|
AssetReference<Shader> _shader;
|
||||||
GPUPipelineStatePermutationsPs<4> _psLut;
|
GPUPipelineStatePermutationsPs<4> _psLut;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Init
|
/// Renders Look Up table with color grading parameters mixed in.
|
||||||
/// </summary>
|
|
||||||
ColorGradingPass();
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Performs Look Up Table rendering for the input task.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="renderContext">The rendering context.</param>
|
/// <param name="renderContext">The rendering context.</param>
|
||||||
/// <returns>Allocated temp render target with a rendered LUT. Can be 2d or 3d based on current graphics hardware caps. Release after usage.</returns>
|
/// <returns>Allocated temp render target with a rendered LUT. Can be 2d or 3d based on current graphics hardware caps. Release after usage (via RenderTargetPool::Release).</returns>
|
||||||
GPUTexture* RenderLUT(RenderContext& renderContext);
|
GPUTexture* RenderLUT(RenderContext& renderContext);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
#if COMPILE_WITH_DEV_ENV
|
#if COMPILE_WITH_DEV_ENV
|
||||||
void OnShaderReloading(Asset* obj)
|
void OnShaderReloading(Asset* obj)
|
||||||
{
|
{
|
||||||
@@ -44,14 +33,12 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// [RendererPass]
|
// [RendererPass]
|
||||||
String ToString() const override;
|
String ToString() const override;
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
void Dispose() override;
|
void Dispose() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// [RendererPass]
|
// [RendererPass]
|
||||||
bool setupResources() override;
|
bool setupResources() override;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user