[idd/client/obs] hdr: wire up the SdrWhiteLevel from the capture

This commit is contained in:
Geoffrey McRae
2026-07-17 17:49:49 +10:00
parent 3b69e6bdf4
commit 2006fb640f
17 changed files with 111 additions and 66 deletions

View File

@@ -92,6 +92,7 @@ typedef struct LG_RendererFormat
uint32_t hdrMinDisplayLuminance; uint32_t hdrMinDisplayLuminance;
uint32_t hdrMaxContentLightLevel; uint32_t hdrMaxContentLightLevel;
uint32_t hdrMaxFrameAverageLightLevel; uint32_t hdrMaxFrameAverageLightLevel;
uint32_t sdrWhiteLevel;
} }
LG_RendererFormat; LG_RendererFormat;

View File

@@ -46,6 +46,7 @@ struct CursorTex
GLuint uRotate; GLuint uRotate;
GLuint uCBMode; GLuint uCBMode;
GLint uMapSDRtoPQ; GLint uMapSDRtoPQ;
GLint uSDRWhiteLevel;
}; };
struct CursorPos struct CursorPos
@@ -79,6 +80,7 @@ struct EGL_Cursor
_Atomic(struct CursorSize) size; _Atomic(struct CursorSize) size;
_Atomic(float) scale; _Atomic(float) scale;
_Atomic(bool) mapSDRtoPQ; _Atomic(bool) mapSDRtoPQ;
_Atomic(float) sdrWhiteLevel;
struct CursorTex norm; struct CursorTex norm;
struct CursorTex mono; struct CursorTex mono;
@@ -113,6 +115,7 @@ static bool cursorTexInit(struct CursorTex * t,
t->uRotate = egl_shaderGetUniform(t->shader, "rotate" ); t->uRotate = egl_shaderGetUniform(t->shader, "rotate" );
t->uCBMode = egl_shaderGetUniform(t->shader, "cbMode" ); t->uCBMode = egl_shaderGetUniform(t->shader, "cbMode" );
t->uMapSDRtoPQ = egl_shaderGetUniform(t->shader, "mapSDRtoPQ" ); t->uMapSDRtoPQ = egl_shaderGetUniform(t->shader, "mapSDRtoPQ" );
t->uSDRWhiteLevel = egl_shaderGetUniform(t->shader, "sdrWhiteLevel");
return true; return true;
} }
@@ -126,6 +129,7 @@ static inline void setCursorTexUniforms(EGL_Cursor * cursor,
glUniform1i(t->uRotate , cursor->rotate); glUniform1i(t->uRotate , cursor->rotate);
glUniform1i(t->uCBMode , cursor->cbMode); glUniform1i(t->uCBMode , cursor->cbMode);
glUniform1i(t->uMapSDRtoPQ , !mono && atomic_load(&cursor->mapSDRtoPQ)); glUniform1i(t->uMapSDRtoPQ , !mono && atomic_load(&cursor->mapSDRtoPQ));
glUniform1f(t->uSDRWhiteLevel, atomic_load(&cursor->sdrWhiteLevel));
} }
static void cursorTexFree(struct CursorTex * t) static void cursorTexFree(struct CursorTex * t)
@@ -174,6 +178,8 @@ bool egl_cursorInit(EGL_Cursor ** cursor)
atomic_init(&(*cursor)->size , size ); atomic_init(&(*cursor)->size , size );
atomic_init(&(*cursor)->scale , 1.0f ); atomic_init(&(*cursor)->scale , 1.0f );
atomic_init(&(*cursor)->mapSDRtoPQ, false); atomic_init(&(*cursor)->mapSDRtoPQ, false);
atomic_init(&(*cursor)->sdrWhiteLevel,
(float)KVMFR_SDR_WHITE_LEVEL_DEFAULT);
return true; return true;
} }
@@ -435,7 +441,10 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
return state; return state;
} }
void egl_cursorSetHDRState(EGL_Cursor * cursor, bool hdrActive, bool hdrPQ) void egl_cursorSetHDRState(EGL_Cursor * cursor, bool hdrActive, bool hdrPQ,
float sdrWhiteLevel)
{ {
atomic_store(&cursor->sdrWhiteLevel, sdrWhiteLevel > 0.0f ?
sdrWhiteLevel : (float)KVMFR_SDR_WHITE_LEVEL_DEFAULT);
atomic_store(&cursor->mapSDRtoPQ, hdrActive && hdrPQ); atomic_store(&cursor->mapSDRtoPQ, hdrActive && hdrPQ);
} }

View File

@@ -53,4 +53,5 @@ void egl_cursorSetState(EGL_Cursor * cursor, const bool visible,
struct CursorState egl_cursorRender(EGL_Cursor * cursor, struct CursorState egl_cursorRender(EGL_Cursor * cursor,
LG_RendererRotate rotate, int width, int height); LG_RendererRotate rotate, int width, int height);
void egl_cursorSetHDRState(EGL_Cursor * cursor, bool hdrActive, bool hdrPQ); void egl_cursorSetHDRState(EGL_Cursor * cursor, bool hdrActive, bool hdrPQ,
float sdrWhiteLevel);

View File

@@ -631,7 +631,8 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo
egl_desktopSetNativeHDR(this->desktop, useNativeHDR); egl_desktopSetNativeHDR(this->desktop, useNativeHDR);
// Tell the cursor shader whether to map SDR cursor colors into PQ // Tell the cursor shader whether to map SDR cursor colors into PQ
egl_cursorSetHDRState(this->cursor, useNativeHDR, format.hdrPQ); egl_cursorSetHDRState(this->cursor, useNativeHDR, format.hdrPQ,
format.sdrWhiteLevel);
egl_update_scale_type(this); egl_update_scale_type(this);
egl_damageSetup(this->damage, format.frameWidth, format.frameHeight); egl_damageSetup(this->damage, format.frameWidth, format.frameHeight);

View File

@@ -10,6 +10,7 @@ out vec4 color;
uniform sampler2D sampler1; uniform sampler2D sampler1;
uniform float scale; uniform float scale;
uniform bool mapSDRtoPQ; uniform bool mapSDRtoPQ;
uniform float sdrWhiteLevel;
void main() void main()
{ {
@@ -32,7 +33,7 @@ void main()
if (mapSDRtoPQ) if (mapSDRtoPQ)
{ {
vec3 linear = bt709to2020(srgb2lin(tmp.rgb)); vec3 linear = bt709to2020(srgb2lin(tmp.rgb));
tmp.rgb = lin2pq(linear * (203.0 / 10000.0)); tmp.rgb = lin2pq(linear * (sdrWhiteLevel / 10000.0));
} }
color = tmp; color = tmp;

View File

@@ -12,6 +12,7 @@ uniform sampler2D sampler1;
uniform float scale; uniform float scale;
uniform int cbMode; uniform int cbMode;
uniform bool mapSDRtoPQ; uniform bool mapSDRtoPQ;
uniform float sdrWhiteLevel;
void main() void main()
{ {
@@ -38,7 +39,7 @@ void main()
// then premultiply again so the blend operation remains valid. // then premultiply again so the blend operation remains valid.
vec3 srgb = clamp(color.rgb / color.a, 0.0, 1.0); vec3 srgb = clamp(color.rgb / color.a, 0.0, 1.0);
vec3 linear = bt709to2020(srgb2lin(srgb)); vec3 linear = bt709to2020(srgb2lin(srgb));
color.rgb = lin2pq(linear * (203.0 / 10000.0)) * color.a; color.rgb = lin2pq(linear * (sdrWhiteLevel / 10000.0)) * color.a;
} }
else else
color.rgb = vec3(0.0); color.rgb = vec3(0.0);

View File

@@ -640,6 +640,8 @@ int main_frameThread(void * unused)
lgrFormat.pitch = frame->pitch; lgrFormat.pitch = frame->pitch;
lgrFormat.hdr = frame->flags & FRAME_FLAG_HDR; lgrFormat.hdr = frame->flags & FRAME_FLAG_HDR;
lgrFormat.hdrPQ = frame->flags & FRAME_FLAG_HDR_PQ; lgrFormat.hdrPQ = frame->flags & FRAME_FLAG_HDR_PQ;
lgrFormat.sdrWhiteLevel = frame->sdrWhiteLevel ?
frame->sdrWhiteLevel : KVMFR_SDR_WHITE_LEVEL_DEFAULT;
if (lgrFormat.hdr) if (lgrFormat.hdr)
{ {
@@ -728,14 +730,15 @@ int main_frameThread(void * unused)
g_state.formatValid = true; g_state.formatValid = true;
formatVer = frame->formatVer; formatVer = frame->formatVer;
DEBUG_INFO("Format: %s %ux%u (%ux%u) stride:%u pitch:%u rotation:%d hdr:%d pq:%d", DEBUG_INFO("Format: %s %ux%u (%ux%u) stride:%u pitch:%u rotation:%d hdr:%d pq:%d sdrWhite:%u nits",
FrameTypeStr[frame->type], FrameTypeStr[frame->type],
frame->frameWidth, frame->frameHeight, frame->frameWidth, frame->frameHeight,
frame->dataWidth , frame->dataHeight , frame->dataWidth , frame->dataHeight ,
frame->stride, frame->pitch, frame->stride, frame->pitch,
frame->rotation, frame->rotation,
frame->flags & FRAME_FLAG_HDR ? 1 : 0, frame->flags & FRAME_FLAG_HDR ? 1 : 0,
frame->flags & FRAME_FLAG_HDR_PQ ? 1 : 0); frame->flags & FRAME_FLAG_HDR_PQ ? 1 : 0,
lgrFormat.sdrWhiteLevel);
LG_LOCK(g_state.lgrLock); LG_LOCK(g_state.lgrLock);
if (!RENDERER(onFrameFormat, lgrFormat)) if (!RENDERER(onFrameFormat, lgrFormat))

View File

@@ -28,7 +28,11 @@
#include "types.h" #include "types.h"
#define KVMFR_MAGIC "KVMFR---" #define KVMFR_MAGIC "KVMFR---"
#define KVMFR_VERSION 20 #define KVMFR_VERSION 21
// Fallback used by producers that cannot report the source display's SDR
// white level. IDD frames override this with IDDCX_METADATA2::SdrWhiteLevel.
#define KVMFR_SDR_WHITE_LEVEL_DEFAULT 203u
#define KVMFR_MAX_DAMAGE_RECTS 64 #define KVMFR_MAX_DAMAGE_RECTS 64
@@ -175,6 +179,10 @@ typedef struct KVMFRFrame
uint32_t hdrMinDisplayLuminance; // Min mastering display luminance (0.0001 cd/m²) uint32_t hdrMinDisplayLuminance; // Min mastering display luminance (0.0001 cd/m²)
uint32_t hdrMaxContentLightLevel; // MaxCLL (cd/m²) uint32_t hdrMaxContentLightLevel; // MaxCLL (cd/m²)
uint32_t hdrMaxFrameAverageLightLevel; // MaxFALL (cd/m²) uint32_t hdrMaxFrameAverageLightLevel; // MaxFALL (cd/m²)
// White level in nits for SDR content composited into an HDR frame, such as
// the hardware cursor. Valid for all frames; SDR modes normally report 80.
uint32_t sdrWhiteLevel;
} }
KVMFRFrame; KVMFRFrame;

View File

@@ -347,6 +347,7 @@ static bool sendFrame(CaptureResult result, bool * restart)
fi->pitch = frame.pitch; fi->pitch = frame.pitch;
// fi->offset is initialized at startup // fi->offset is initialized at startup
fi->flags = flags; fi->flags = flags;
fi->sdrWhiteLevel = KVMFR_SDR_WHITE_LEVEL_DEFAULT;
fi->damageRectsCount = frame.damageRectsCount; fi->damageRectsCount = frame.damageRectsCount;
memcpy(fi->damageRects, frame.damageRects, memcpy(fi->damageRects, frame.damageRects,
frame.damageRectsCount * sizeof(FrameDamageRect)); frame.damageRectsCount * sizeof(FrameDamageRect));

View File

@@ -957,7 +957,8 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
m_lastHDRMaxDisplayLuminance != dstFormat.maxDisplayLuminance || m_lastHDRMaxDisplayLuminance != dstFormat.maxDisplayLuminance ||
m_lastHDRMinDisplayLuminance != dstFormat.minDisplayLuminance || m_lastHDRMinDisplayLuminance != dstFormat.minDisplayLuminance ||
m_lastHDRMaxContentLightLevel != dstFormat.maxContentLightLevel || m_lastHDRMaxContentLightLevel != dstFormat.maxContentLightLevel ||
m_lastHDRMaxFrameAverageLightLevel != dstFormat.maxFrameAverageLightLevel) m_lastHDRMaxFrameAverageLightLevel != dstFormat.maxFrameAverageLightLevel ||
m_lastSDRWhiteLevel != dstFormat.sdrWhiteLevel)
{ {
++m_formatVer; ++m_formatVer;
} }
@@ -975,6 +976,7 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
m_lastHDRMinDisplayLuminance = dstFormat.minDisplayLuminance; m_lastHDRMinDisplayLuminance = dstFormat.minDisplayLuminance;
m_lastHDRMaxContentLightLevel = dstFormat.maxContentLightLevel; m_lastHDRMaxContentLightLevel = dstFormat.maxContentLightLevel;
m_lastHDRMaxFrameAverageLightLevel = dstFormat.maxFrameAverageLightLevel; m_lastHDRMaxFrameAverageLightLevel = dstFormat.maxFrameAverageLightLevel;
m_lastSDRWhiteLevel = dstFormat.sdrWhiteLevel;
if (++m_frameIndex == LGMP_Q_FRAME_LEN) if (++m_frameIndex == LGMP_Q_FRAME_LEN)
m_frameIndex = 0; m_frameIndex = 0;
@@ -1012,6 +1014,7 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
fi->pitch = pitch; fi->pitch = pitch;
// fi->offset is initialized at startup // fi->offset is initialized at startup
fi->flags = flags; fi->flags = flags;
fi->sdrWhiteLevel = dstFormat.sdrWhiteLevel;
fi->rotation = FRAME_ROT_0; fi->rotation = FRAME_ROT_0;
fi->type = dstFormat.format; fi->type = dstFormat.format;

View File

@@ -129,6 +129,7 @@ private:
uint32_t m_lastHDRMinDisplayLuminance = 0; uint32_t m_lastHDRMinDisplayLuminance = 0;
uint32_t m_lastHDRMaxContentLightLevel = 0; uint32_t m_lastHDRMaxContentLightLevel = 0;
uint32_t m_lastHDRMaxFrameAverageLightLevel = 0; uint32_t m_lastHDRMaxFrameAverageLightLevel = 0;
uint32_t m_lastSDRWhiteLevel = 0;
bool m_lastHDRActive = false; bool m_lastHDRActive = false;
void QueryIddCxCapabilities(); void QueryIddCxCapabilities();

View File

@@ -73,7 +73,8 @@ bool CPostProcessor::Configure(const D12FrameFormat& srcFormat, bool * formatCha
srcFormat.width == m_srcFormat.width && srcFormat.width == m_srcFormat.width &&
srcFormat.height == m_srcFormat.height && srcFormat.height == m_srcFormat.height &&
srcFormat.hdr == m_srcFormat.hdr && srcFormat.hdr == m_srcFormat.hdr &&
srcFormat.hdrPQ == m_srcFormat.hdrPQ) srcFormat.hdrPQ == m_srcFormat.hdrPQ &&
srcFormat.sdrWhiteLevel == m_srcFormat.sdrWhiteLevel)
return true; return true;
D12FrameFormat oldDst = m_dstFormat; D12FrameFormat oldDst = m_dstFormat;
@@ -112,7 +113,8 @@ bool CPostProcessor::Configure(const D12FrameFormat& srcFormat, bool * formatCha
oldDst.width != m_dstFormat.width || oldDst.width != m_dstFormat.width ||
oldDst.height != m_dstFormat.height || oldDst.height != m_dstFormat.height ||
oldDst.hdr != m_dstFormat.hdr || oldDst.hdr != m_dstFormat.hdr ||
oldDst.hdrPQ != m_dstFormat.hdrPQ; oldDst.hdrPQ != m_dstFormat.hdrPQ ||
oldDst.sdrWhiteLevel != m_dstFormat.sdrWhiteLevel;
return true; return true;
} }

View File

@@ -21,6 +21,7 @@
struct CD3D12Device; struct CD3D12Device;
extern "C" { extern "C" {
#include "common/KVMFR.h"
#include "common/types.h" #include "common/types.h"
} }
@@ -41,6 +42,7 @@ struct D12FrameFormat
FrameType format = FRAME_TYPE_INVALID; FrameType format = FRAME_TYPE_INVALID;
bool hdr = false; bool hdr = false;
bool hdrPQ = false; bool hdrPQ = false;
uint32_t sdrWhiteLevel = KVMFR_SDR_WHITE_LEVEL_DEFAULT;
// HDR static metadata (SMPTE ST 2086) // HDR static metadata (SMPTE ST 2086)
// Display color primaries in 0.00002 units (xy coordinates) // Display color primaries in 0.00002 units (xy coordinates)

View File

@@ -165,6 +165,7 @@ bool CSwapChainProcessor::SwapChainThreadCore()
// Only the buffer2 acquisition path (IddCx 1.10+) reports it; on the legacy // Only the buffer2 acquisition path (IddCx 1.10+) reports it; on the legacy
// path HDR is not available, so default to SDR. // path HDR is not available, so default to SDR.
DXGI_COLOR_SPACE_TYPE colorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; DXGI_COLOR_SPACE_TYPE colorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
UINT sdrWhiteLevel = KVMFR_SDR_WHITE_LEVEL_DEFAULT;
#ifdef HAS_IDDCX_110 #ifdef HAS_IDDCX_110
if (m_devContext->CanProcessFP16()) if (m_devContext->CanProcessFP16())
@@ -183,6 +184,7 @@ bool CSwapChainProcessor::SwapChainThreadCore()
dirtyRectCount = buffer.MetaData.DirtyRectCount; dirtyRectCount = buffer.MetaData.DirtyRectCount;
surface = buffer.MetaData.pSurface; surface = buffer.MetaData.pSurface;
colorSpace = buffer.MetaData.SurfaceColorSpace; colorSpace = buffer.MetaData.SurfaceColorSpace;
sdrWhiteLevel = buffer.MetaData.SdrWhiteLevel;
} }
} }
else else
@@ -222,7 +224,7 @@ bool CSwapChainProcessor::SwapChainThreadCore()
if (frameNumber != lastFrameNumber) if (frameNumber != lastFrameNumber)
{ {
lastFrameNumber = frameNumber; lastFrameNumber = frameNumber;
SwapChainNewFrame(surface, dirtyRectCount, colorSpace); SwapChainNewFrame(surface, dirtyRectCount, colorSpace, sdrWhiteLevel);
// report that all GPU processing for this frame has been queued // report that all GPU processing for this frame has been queued
hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain); hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain);
@@ -334,7 +336,7 @@ static FrameType GetFrameType(DXGI_FORMAT format)
} }
bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer, unsigned dirtyRectCount, bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer, unsigned dirtyRectCount,
DXGI_COLOR_SPACE_TYPE colorSpace) DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel)
{ {
ComPtr<ID3D11Texture2D> texture; ComPtr<ID3D11Texture2D> texture;
HRESULT hr = acquiredBuffer.As(&texture); HRESULT hr = acquiredBuffer.As(&texture);
@@ -386,6 +388,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
srcFormat.width = (unsigned)srcDesc.Width; srcFormat.width = (unsigned)srcDesc.Width;
srcFormat.height = srcDesc.Height; srcFormat.height = srcDesc.Height;
srcFormat.format = GetFrameType(srcDesc.Format); srcFormat.format = GetFrameType(srcDesc.Format);
srcFormat.sdrWhiteLevel = sdrWhiteLevel;
switch (colorSpace) switch (colorSpace)
{ {

View File

@@ -71,7 +71,7 @@ private:
static void CompletionFunction( static void CompletionFunction(
CD3D12CommandQueue * queue, bool result, void * param1, void * param2); CD3D12CommandQueue * queue, bool result, void * param1, void * param2);
bool SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer, unsigned dirtyRectCount, bool SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer, unsigned dirtyRectCount,
DXGI_COLOR_SPACE_TYPE colorSpace); DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel);
public: public:
CSwapChainProcessor(IDDCX_MONITOR monitor, CIndirectDeviceContext * devContext, IDDCX_SWAPCHAIN hSwapChain, CSwapChainProcessor(IDDCX_MONITOR monitor, CIndirectDeviceContext * devContext, IDDCX_SWAPCHAIN hSwapChain,

View File

@@ -140,6 +140,7 @@ PostProcessStatus CHDR16to10Effect::SetFormat(
dst.minDisplayLuminance = src.minDisplayLuminance; dst.minDisplayLuminance = src.minDisplayLuminance;
dst.maxContentLightLevel = src.maxContentLightLevel; dst.maxContentLightLevel = src.maxContentLightLevel;
dst.maxFrameAverageLightLevel = src.maxFrameAverageLightLevel; dst.maxFrameAverageLightLevel = src.maxFrameAverageLightLevel;
dst.sdrWhiteLevel = src.sdrWhiteLevel;
return PostProcessStatus::SUCCESS; return PostProcessStatus::SUCCESS;
} }

View File

@@ -104,6 +104,7 @@ typedef struct
bool hideMouse; bool hideMouse;
bool hdr; bool hdr;
bool hdrPQ; bool hdrPQ;
float sdrWhiteLevel; // source SDR white level in nits
struct vec3 colorMatrix[3]; // source primaries -> BT.709 (linear) struct vec3 colorMatrix[3]; // source primaries -> BT.709 (linear)
float hdrScale; // scRGB reference white scaling float hdrScale; // scRGB reference white scaling
#if LIBOBS_API_MAJOR_VER >= 27 #if LIBOBS_API_MAJOR_VER >= 27
@@ -826,6 +827,8 @@ static void lgFormatInit(LGPlugin * this, const KVMFRFrame * frame,
this->unpack = false; this->unpack = false;
this->hdr = frame->flags & FRAME_FLAG_HDR; this->hdr = frame->flags & FRAME_FLAG_HDR;
this->hdrPQ = frame->flags & FRAME_FLAG_HDR_PQ; this->hdrPQ = frame->flags & FRAME_FLAG_HDR_PQ;
this->sdrWhiteLevel = frame->sdrWhiteLevel ?
(float)frame->sdrWhiteLevel : (float)KVMFR_SDR_WHITE_LEVEL_DEFAULT;
if (this->hdr && this->hdrPQ) if (this->hdr && this->hdrPQ)
lgComputeColorMatrix(this, frame); lgComputeColorMatrix(this, frame);
@@ -1223,14 +1226,14 @@ static void lgVideoRender(void * data, gs_effect_t * effect)
gs_set_scissor_rect(&r); gs_set_scissor_rect(&r);
/* Color cursor pixels are premultiplied sRGB. When rendering into OBS's /* Color cursor pixels are premultiplied sRGB. When rendering into OBS's
* linear scRGB space, decode and scale them to its configured SDR white. */ * linear scRGB space, decode them and reproduce the source display's SDR
* white level reported by the IDD. */
bool mapCursorToScRGB = false; bool mapCursorToScRGB = false;
float cursorScale = 1.0f; float cursorScale = 1.0f;
#if LIBOBS_API_MAJOR_VER >= 28 #if LIBOBS_API_MAJOR_VER >= 28
mapCursorToScRGB = this->colorSpace == GS_CS_709_SCRGB; mapCursorToScRGB = this->colorSpace == GS_CS_709_SCRGB;
if (mapCursorToScRGB) if (mapCursorToScRGB)
cursorScale = cursorScale = this->sdrWhiteLevel / LG_SCRGB_REFERENCE_WHITE;
obs_get_video_sdr_white_level() / LG_SCRGB_REFERENCE_WHITE;
#endif #endif
effect = mapCursorToScRGB && !this->cursorMono ? effect = mapCursorToScRGB && !this->cursorMono ?
@@ -1261,9 +1264,13 @@ static void lgVideoRender(void * data, gs_effect_t * effect)
if (this->cursor.type == CURSOR_TYPE_MASKED_COLOR && if (this->cursor.type == CURSOR_TYPE_MASKED_COLOR &&
this->cursorXorTex) this->cursorXorTex)
{ {
effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); effect = mapCursorToScRGB ?
image = gs_effect_get_param_by_name(effect, "image"); this->cursorEffect : obs_get_base_effect(OBS_EFFECT_DEFAULT);
image = mapCursorToScRGB ?
this->cursorImage : gs_effect_get_param_by_name(effect, "image");
gs_effect_set_texture(image, this->cursorXorTex); gs_effect_set_texture(image, this->cursorXorTex);
if (mapCursorToScRGB)
gs_effect_set_float(this->cursorMultiplier, cursorScale);
gs_blend_function_separate( gs_blend_function_separate(
GS_BLEND_INVDSTCOLOR, GS_BLEND_INVSRCALPHA, GS_BLEND_INVDSTCOLOR, GS_BLEND_INVSRCALPHA,
GS_BLEND_ZERO , GS_BLEND_ONE); GS_BLEND_ZERO , GS_BLEND_ONE);