[idd] postprocess: separate calibrated frame branches

This commit is contained in:
Geoffrey McRae
2026-08-13 14:20:01 +10:00
parent 43d918b5f3
commit a8eb49fb17
7 changed files with 177 additions and 68 deletions

View File

@@ -28,6 +28,7 @@ bool Frame::Same(const GraphCfg& left, const GraphCfg& right)
left.srcWidth == right.srcWidth &&
left.srcHeight == right.srcHeight &&
Frame::Same(left.src, right.src) &&
left.transform == right.transform &&
left.width == right.width &&
left.height == right.height &&
Frame::Same(left.checkpoint, right.checkpoint);
@@ -141,60 +142,99 @@ unsigned CFrameGraph::Checkpoint(const FrameProfile& requested)
{
const FrameProfile profile =
Frame::Store(requested, FrameStorage::D3D12_TEXTURE);
FrameProfile current = m_cfg.src;
unsigned parent = 0;
FrameOp op;
const D12ColorTransform * transform = m_cfg.transform.get();
if (transform && transform->matrixEnabled)
{
// The common matrix stage preserves the source signal. A transfer-domain
// LUT is applied after each branch has selected scRGB or HDR10.
if (current.signal == FrameSignal::SRGB &&
current.pixel == FramePixel::BGRA8)
current.pixel = FramePixel::RGBA8;
parent = AddNode(FrameOp::CAL, 0,
m_cfg.srcWidth, m_cfg.srcHeight, current);
if (parent == FRAME_GRAPH_ROOT)
return FRAME_GRAPH_ROOT;
}
// Without calibration the established path filters linear scRGB before
// HDR10 encoding. This scale node is also shared by scRGB and HDR10 leaves.
const bool earlyScale = !transform &&
(m_cfg.srcWidth != m_cfg.width || m_cfg.srcHeight != m_cfg.height);
if (earlyScale)
{
parent = AddNode(FrameOp::SCALE, parent,
m_cfg.width, m_cfg.height, current);
if (parent == FRAME_GRAPH_ROOT)
return FRAME_GRAPH_ROOT;
}
switch (profile.signal)
{
case FrameSignal::SRGB:
op = FrameOp::SDR;
parent = AddNode(FrameOp::SDR, 0, m_cfg.width, m_cfg.height,
m_cfg.checkpoint);
if (parent == FRAME_GRAPH_ROOT)
return FRAME_GRAPH_ROOT;
if (Frame::Same(profile, m_cfg.checkpoint))
return parent;
break;
case FrameSignal::SCRGB_LINEAR:
{
op = FrameOp::SCRGB;
FrameProfile scRGB;
scRGB.storage = FrameStorage::D3D12_TEXTURE;
scRGB.pixel = FramePixel::RGBA16F;
scRGB.signal = FrameSignal::SCRGB_LINEAR;
return AddNode(
op, 0, m_cfg.width, m_cfg.height, scRGB);
}
break;
case FrameSignal::PQ_BT2020:
op = FrameOp::HDR10;
if (m_cfg.src.signal == FrameSignal::SCRGB_LINEAR)
{
FrameProfile scRGB;
scRGB.storage = FrameStorage::D3D12_TEXTURE;
scRGB.pixel = FramePixel::RGBA16F;
scRGB.signal = FrameSignal::SCRGB_LINEAR;
parent = AddNode(
FrameOp::SCRGB, 0, m_cfg.width, m_cfg.height, scRGB);
const unsigned width = earlyScale ? m_cfg.width : m_cfg.srcWidth;
const unsigned height = earlyScale ? m_cfg.height : m_cfg.srcHeight;
parent = AddNode(FrameOp::HDR10, parent,
width, height, profile);
if (parent == FRAME_GRAPH_ROOT)
return FRAME_GRAPH_ROOT;
}
else
{
parent = AddNode(FrameOp::HDR10, 0,
m_cfg.width, m_cfg.height, m_cfg.checkpoint);
if (parent == FRAME_GRAPH_ROOT)
return FRAME_GRAPH_ROOT;
if (Frame::Same(profile, m_cfg.checkpoint))
return parent;
current = profile;
}
break;
default:
return FRAME_GRAPH_ROOT;
}
return AddNode(
op, parent, m_cfg.width, m_cfg.height, profile);
if (transform && transform->lutEnabled)
{
if (current.signal == FrameSignal::SRGB &&
current.pixel == FramePixel::BGRA8)
current.pixel = FramePixel::RGBA8;
parent = AddNode(FrameOp::LUT, parent,
m_cfg.srcWidth, m_cfg.srcHeight, current);
if (parent == FRAME_GRAPH_ROOT)
return FRAME_GRAPH_ROOT;
}
if (!earlyScale &&
(m_cfg.srcWidth != m_cfg.width || m_cfg.srcHeight != m_cfg.height))
{
parent = AddNode(FrameOp::SCALE, parent,
m_cfg.width, m_cfg.height, current);
if (parent == FRAME_GRAPH_ROOT)
return FRAME_GRAPH_ROOT;
}
if (parent != 0 && Frame::Same(current, profile))
return parent;
FrameOp op;
switch (profile.signal)
{
case FrameSignal::SRGB:
op = FrameOp::SDR;
break;
case FrameSignal::SCRGB_LINEAR:
op = FrameOp::SCRGB;
break;
case FrameSignal::PQ_BT2020:
op = FrameOp::HDR10;
break;
default:
return FRAME_GRAPH_ROOT;
}
return AddNode(op, parent, m_cfg.width, m_cfg.height, profile);
}
bool CFrameGraph::Add(BackendId id, uint32_t epoch, bool required,
@@ -251,6 +291,26 @@ bool CFrameGraph::Same(const GraphCfg& cfg) const
return m_sealed && Frame::Same(m_cfg, cfg);
}
bool CFrameGraph::Need(FrameOp op) const
{
if (!m_sealed || op == FrameOp::SRC)
return false;
for (unsigned i = 1; i < m_nodeCount; ++i)
if (m_nodes[i].op == op && m_nodes[i].refs)
return true;
return false;
}
bool CFrameGraph::Want(FrameSignal signal) const
{
if (!m_sealed)
return false;
for (unsigned i = 0; i < m_leafCount; ++i)
if (m_leaves[i].cfg.profile.signal == signal)
return true;
return false;
}
bool CFrameGraph::Desc(unsigned leaf, const FrameDesc& frame,
LeafDesc& desc) const
{

View File

@@ -31,11 +31,14 @@
static const unsigned FRAME_DAMAGE_MAX = 256;
static const unsigned FRAME_GRAPH_ROOT = UINT_MAX;
static const unsigned FRAME_GRAPH_MAX_NODES =
1 + 2 * TRANSPORT_MAX_INSTANCES;
1 + 5 * TRANSPORT_MAX_INSTANCES;
enum class FrameOp : uint8_t
{
SRC,
CAL,
LUT,
SCALE,
SDR,
SCRGB,
HDR10,
@@ -50,14 +53,15 @@ enum class FrameDamage : uint8_t
struct GraphCfg
{
GpuMode mode = GpuMode::HARDWARE;
LUID adapter = {};
unsigned srcWidth = 0;
unsigned srcHeight = 0;
FrameProfile src;
unsigned width = 0;
unsigned height = 0;
FrameProfile checkpoint;
GpuMode mode = GpuMode::HARDWARE;
LUID adapter = {};
unsigned srcWidth = 0;
unsigned srcHeight = 0;
FrameProfile src;
std::shared_ptr<const D12ColorTransform> transform;
unsigned width = 0;
unsigned height = 0;
FrameProfile checkpoint;
};
namespace Frame
@@ -134,6 +138,8 @@ public:
const FrameCfg& cfg);
bool Seal();
bool Same(const GraphCfg& cfg) const;
bool Want(FrameSignal signal) const;
bool Need(FrameOp op) const;
bool Desc(unsigned leaf, const FrameDesc& frame, LeafDesc& desc) const;
const GraphCfg& Cfg() const { return m_cfg; }

View File

@@ -63,6 +63,7 @@ static bool MakeGraphCfg(const D12FrameFormat& source,
cfg.srcWidth = source.width;
cfg.srcHeight = source.height;
cfg.src = src;
cfg.transform = D12::Transform(source.colorTransform);
cfg.width = checkpoint.width;
cfg.height = checkpoint.height;
cfg.checkpoint = output;