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;
|
||||
PostProcessSettings Graphics::PostProcessSettings;
|
||||
bool Graphics::SpreadWorkload = true;
|
||||
bool Graphics::PostProcessing::ColorGradingVolumeLUT = true;
|
||||
|
||||
#if GRAPHICS_API_NULL
|
||||
extern GPUDevice* CreateGPUDeviceNull();
|
||||
|
||||
@@ -84,6 +84,16 @@ public:
|
||||
/// </summary>
|
||||
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:
|
||||
/// <summary>
|
||||
/// Disposes the device.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "Engine/Graphics/GPULimits.h"
|
||||
#include "Engine/Graphics/Graphics.h"
|
||||
#include "Engine/Graphics/RenderTargetPool.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
|
||||
@@ -36,12 +37,6 @@ GPU_CB_STRUCT(Data {
|
||||
float LutWeight;
|
||||
});
|
||||
|
||||
ColorGradingPass::ColorGradingPass()
|
||||
: _useVolumeTexture(false)
|
||||
, _lutFormat()
|
||||
{
|
||||
}
|
||||
|
||||
String ColorGradingPass::ToString() const
|
||||
{
|
||||
return TEXT("ColorGradingPass");
|
||||
@@ -49,103 +44,87 @@ String ColorGradingPass::ToString() const
|
||||
|
||||
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();
|
||||
|
||||
// Load shader
|
||||
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/ColorGrading"));
|
||||
if (_shader == nullptr)
|
||||
return true;
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
_shader.Get()->OnReloading.Bind<ColorGradingPass, &ColorGradingPass::OnShaderReloading>(this);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ColorGradingPass::setupResources()
|
||||
{
|
||||
// Wait for shader
|
||||
if (!_shader || !_shader->IsLoaded())
|
||||
return true;
|
||||
const auto shader = _shader->GetShader();
|
||||
CHECK_INVALID_SHADER_PASS_CB_SIZE(shader, 0, Data);
|
||||
|
||||
// Create pipeline stages
|
||||
GPUPipelineState::Description psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
|
||||
if (!_psLut.IsValid())
|
||||
{
|
||||
StringAnsiView psName;
|
||||
// Create pipeline stage
|
||||
auto psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
|
||||
StringAnsiView psName;
|
||||
#if GPU_ALLOW_GEOMETRY_SHADERS
|
||||
if (_useVolumeTexture)
|
||||
{
|
||||
psDesc.VS = shader->GetVS("VS_WriteToSlice");
|
||||
psDesc.GS = shader->GetGS("GS_WriteToSlice");
|
||||
psName = "PS_Lut3D";
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
psName = "PS_Lut2D";
|
||||
}
|
||||
if (_psLut.Create(psDesc, shader, psName))
|
||||
return true;
|
||||
if (_use3D == 1)
|
||||
{
|
||||
psDesc.VS = shader->GetVS("VS_WriteToSlice");
|
||||
psDesc.GS = shader->GetGS("GS_WriteToSlice");
|
||||
psName = "PS_Lut3D";
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
psName = "PS_Lut2D";
|
||||
}
|
||||
if (_psLut.Create(psDesc, shader, psName))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ColorGradingPass::Dispose()
|
||||
{
|
||||
// Base
|
||||
RendererPass::Dispose();
|
||||
|
||||
// Cleanup
|
||||
_psLut.Delete();
|
||||
_shader = nullptr;
|
||||
}
|
||||
|
||||
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
|
||||
if (checkIfSkipPass())
|
||||
return nullptr;
|
||||
|
||||
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
|
||||
const int32 LutSize = 32; // this must match value in shader (see ColorGrading.shader and PostProcessing.shader)
|
||||
// Pick a proper LUT pixels format
|
||||
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;
|
||||
#if GPU_ALLOW_GEOMETRY_SHADERS
|
||||
if (_useVolumeTexture)
|
||||
{
|
||||
lutDesc = GPUTextureDescription::New3D(LutSize, LutSize, LutSize, 1, _lutFormat);
|
||||
}
|
||||
if (use3D)
|
||||
lutDesc = GPUTextureDescription::New3D(lutSize, lutSize, lutSize, 1, lutFormat);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
lutDesc = GPUTextureDescription::New2D(LutSize * LutSize, LutSize, 1, _lutFormat);
|
||||
}
|
||||
lutDesc = GPUTextureDescription::New2D(lutSize * lutSize, lutSize, 1, lutFormat);
|
||||
const auto lut = RenderTargetPool::Get(lutDesc);
|
||||
RENDER_TARGET_POOL_SET_NAME(lut, "ColorGrading.LUT");
|
||||
|
||||
@@ -181,7 +160,6 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext)
|
||||
data.LutWeight = useLut ? colorGrading.LutWeight : 0.0f;
|
||||
|
||||
// Prepare
|
||||
auto device = GPUDevice::Instance;
|
||||
auto context = device->GetMainContext();
|
||||
const auto cb = _shader->GetShader()->GetCB(0);
|
||||
context->UpdateCB(cb, &data);
|
||||
@@ -192,7 +170,7 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext)
|
||||
|
||||
// Draw
|
||||
#if GPU_ALLOW_GEOMETRY_SHADERS
|
||||
if (_useVolumeTexture)
|
||||
if (use3D)
|
||||
{
|
||||
context->SetRenderTarget(lut->ViewVolume());
|
||||
|
||||
|
||||
@@ -11,30 +11,19 @@
|
||||
class ColorGradingPass : public RendererPass<ColorGradingPass>
|
||||
{
|
||||
private:
|
||||
|
||||
bool _useVolumeTexture;
|
||||
PixelFormat _lutFormat;
|
||||
int32 _use3D = -1;
|
||||
AssetReference<Shader> _shader;
|
||||
GPUPipelineStatePermutationsPs<4> _psLut;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
ColorGradingPass();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Performs Look Up Table rendering for the input task.
|
||||
/// Renders Look Up table with color grading parameters mixed in.
|
||||
/// </summary>
|
||||
/// <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);
|
||||
|
||||
private:
|
||||
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
void OnShaderReloading(Asset* obj)
|
||||
{
|
||||
@@ -44,14 +33,12 @@ private:
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
// [RendererPass]
|
||||
String ToString() const override;
|
||||
bool Init() override;
|
||||
void Dispose() override;
|
||||
|
||||
protected:
|
||||
|
||||
// [RendererPass]
|
||||
bool setupResources() override;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user