mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-07-16 04:12:03 +00:00
Compare commits
15 Commits
gnif-lgmp-
...
gnif-hdr-e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8dcbddea0 | ||
|
|
1b3305e1dc | ||
|
|
ff1631da67 | ||
|
|
2f59de572b | ||
|
|
da7d1a2ead | ||
|
|
82d36a858f | ||
|
|
9047c26694 | ||
|
|
a32ba63c90 | ||
|
|
8301ec5ae5 | ||
|
|
9721fc8c7e | ||
|
|
364485ded8 | ||
|
|
28973ac4b1 | ||
|
|
0c653c1fb2 | ||
|
|
11b3ac00a8 | ||
|
|
bc96319b94 |
@@ -13,6 +13,7 @@ find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
|
|||||||
add_library(displayserver_Wayland STATIC
|
add_library(displayserver_Wayland STATIC
|
||||||
activation.c
|
activation.c
|
||||||
clipboard.c
|
clipboard.c
|
||||||
|
colormgmt.c
|
||||||
cursor.c
|
cursor.c
|
||||||
gl.c
|
gl.c
|
||||||
idle.c
|
idle.c
|
||||||
|
|||||||
110
client/displayservers/Wayland/colormgmt.c
Normal file
110
client/displayservers/Wayland/colormgmt.c
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright (C) 2017-2026 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "wayland.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include "common/debug.h"
|
||||||
|
|
||||||
|
static void cmIntent(void * data, struct wp_color_manager_v1 * cm, uint32_t intent)
|
||||||
|
{
|
||||||
|
if (intent == WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL)
|
||||||
|
wlWm.cmHasPerceptualIntent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmFeature(void * data, struct wp_color_manager_v1 * cm, uint32_t feature)
|
||||||
|
{
|
||||||
|
switch (feature)
|
||||||
|
{
|
||||||
|
case WP_COLOR_MANAGER_V1_FEATURE_PARAMETRIC:
|
||||||
|
wlWm.cmHasParametric = true;
|
||||||
|
break;
|
||||||
|
case WP_COLOR_MANAGER_V1_FEATURE_SET_LUMINANCES:
|
||||||
|
wlWm.cmHasLuminances = true;
|
||||||
|
break;
|
||||||
|
case WP_COLOR_MANAGER_V1_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES:
|
||||||
|
wlWm.cmHasMasteringPrimaries = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmTFNamed(void * data, struct wp_color_manager_v1 * cm, uint32_t tf)
|
||||||
|
{
|
||||||
|
if (tf == WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ)
|
||||||
|
wlWm.cmHasTFSt2084PQ = true;
|
||||||
|
else if (tf == WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR)
|
||||||
|
wlWm.cmHasTFExtLinear = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmPrimariesNamed(void * data, struct wp_color_manager_v1 * cm,
|
||||||
|
uint32_t primaries)
|
||||||
|
{
|
||||||
|
if (primaries == WP_COLOR_MANAGER_V1_PRIMARIES_BT2020)
|
||||||
|
wlWm.cmHasPrimariesBT2020 = true;
|
||||||
|
else if (primaries == WP_COLOR_MANAGER_V1_PRIMARIES_SRGB)
|
||||||
|
wlWm.cmHasPrimariesSRGB = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmDone(void * data, struct wp_color_manager_v1 * cm)
|
||||||
|
{
|
||||||
|
wlWm.cmFeaturesDone = true;
|
||||||
|
wlWm.cmCanDoHDR = wlWm.cmHasParametric &&
|
||||||
|
((wlWm.cmHasTFSt2084PQ && wlWm.cmHasPrimariesBT2020) ||
|
||||||
|
(wlWm.cmHasTFExtLinear && wlWm.cmHasPrimariesSRGB));
|
||||||
|
DEBUG_INFO("Color management features: parametric:%d luminances:%d "
|
||||||
|
"mastering_primaries:%d st2084_pq:%d ext_linear:%d bt2020:%d srgb:%d "
|
||||||
|
"can_do_hdr:%d",
|
||||||
|
wlWm.cmHasParametric, wlWm.cmHasLuminances,
|
||||||
|
wlWm.cmHasMasteringPrimaries,
|
||||||
|
wlWm.cmHasTFSt2084PQ, wlWm.cmHasTFExtLinear,
|
||||||
|
wlWm.cmHasPrimariesBT2020, wlWm.cmHasPrimariesSRGB,
|
||||||
|
wlWm.cmCanDoHDR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wp_color_manager_v1_listener cmListener = {
|
||||||
|
.supported_intent = cmIntent,
|
||||||
|
.supported_feature = cmFeature,
|
||||||
|
.supported_tf_named = cmTFNamed,
|
||||||
|
.supported_primaries_named = cmPrimariesNamed,
|
||||||
|
.done = cmDone,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool waylandColorMgmtInit(void)
|
||||||
|
{
|
||||||
|
if (!wlWm.colorManager)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
wp_color_manager_v1_add_listener(wlWm.colorManager, &cmListener, NULL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void waylandColorMgmtFree(void)
|
||||||
|
{
|
||||||
|
if (!wlWm.colorManager)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wp_color_manager_v1_destroy(wlWm.colorManager);
|
||||||
|
wlWm.colorManager = NULL;
|
||||||
|
}
|
||||||
@@ -70,6 +70,52 @@ EGLDisplay waylandGetEGLDisplay(void)
|
|||||||
return eglGetDisplay(native);
|
return eglGetDisplay(native);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void applyHDRPending(void)
|
||||||
|
{
|
||||||
|
if (wlWm.pendingHDRApply)
|
||||||
|
{
|
||||||
|
wlWm.pendingHDRApply = false;
|
||||||
|
atomic_thread_fence(memory_order_acquire);
|
||||||
|
waylandSetHDRImageDescription(
|
||||||
|
wlWm.pendingHDRDisplayPrimary, wlWm.pendingHDRWhitePoint,
|
||||||
|
wlWm.pendingHDRMaxDisplayLuminance,
|
||||||
|
wlWm.pendingHDRMinDisplayLuminance,
|
||||||
|
wlWm.pendingHDRMaxCLL,
|
||||||
|
wlWm.pendingHDRMaxFALL,
|
||||||
|
wlWm.pendingHDRPQ);
|
||||||
|
}
|
||||||
|
else if (wlWm.pendingHDRClear)
|
||||||
|
{
|
||||||
|
wlWm.pendingHDRClear = false;
|
||||||
|
waylandClearHDRImageDescription();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void waylandClearHDRImageDescription(void)
|
||||||
|
{
|
||||||
|
if (!wlWm.hdrActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (wlWm.hdrImageCreator)
|
||||||
|
{
|
||||||
|
wp_image_description_creator_params_v1_destroy(wlWm.hdrImageCreator);
|
||||||
|
wlWm.hdrImageCreator = NULL;
|
||||||
|
}
|
||||||
|
if (wlWm.hdrImageDesc)
|
||||||
|
{
|
||||||
|
wp_image_description_v1_destroy(wlWm.hdrImageDesc);
|
||||||
|
wlWm.hdrImageDesc = NULL;
|
||||||
|
}
|
||||||
|
if (wlWm.colorSurface)
|
||||||
|
{
|
||||||
|
wp_color_management_surface_v1_destroy(wlWm.colorSurface);
|
||||||
|
wlWm.colorSurface = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlWm.hdrActive = false;
|
||||||
|
DEBUG_INFO("HDR image description cleared");
|
||||||
|
}
|
||||||
|
|
||||||
void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count)
|
void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count)
|
||||||
{
|
{
|
||||||
if (!wlWm.swapWithDamage.init)
|
if (!wlWm.swapWithDamage.init)
|
||||||
@@ -84,6 +130,7 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
|
|||||||
}
|
}
|
||||||
|
|
||||||
waylandPresentationFrame();
|
waylandPresentationFrame();
|
||||||
|
applyHDRPending();
|
||||||
swapWithDamage(&wlWm.swapWithDamage, display, surface, damage, count);
|
swapWithDamage(&wlWm.swapWithDamage, display, surface, damage, count);
|
||||||
|
|
||||||
if (wlWm.needsResize)
|
if (wlWm.needsResize)
|
||||||
@@ -149,23 +196,239 @@ EGLNativeWindowType waylandGetEGLNativeWindow(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_OPENGL
|
// HDR color management support
|
||||||
bool waylandOpenGLInit(void)
|
void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2],
|
||||||
|
const uint16_t whitePoint[2], uint32_t maxDisplayLuminance,
|
||||||
|
uint32_t minDisplayLuminance, uint32_t maxCLL, uint32_t maxFALL,
|
||||||
|
bool hdrPQ)
|
||||||
{
|
{
|
||||||
EGLint attr[] =
|
if (!wlWm.colorManager)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!wlWm.cmFeaturesDone)
|
||||||
{
|
{
|
||||||
EGL_BUFFER_SIZE , 24,
|
DEBUG_WARN("Color management features not yet advertised, deferring HDR");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wlWm.cmHasParametric)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Compositor does not support parametric image descriptions");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the compositor supports the transfer function we need
|
||||||
|
if (hdrPQ && !wlWm.cmHasTFSt2084PQ)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Compositor does not support ST2084_PQ transfer function");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!hdrPQ && !wlWm.cmHasTFExtLinear)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Compositor does not support EXT_LINEAR transfer function");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify primaries support for the target color space
|
||||||
|
if (hdrPQ && !wlWm.cmHasPrimariesBT2020)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Compositor does not support BT.2020 primaries");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!hdrPQ && !wlWm.cmHasPrimariesSRGB)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Compositor does not support sRGB primaries");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up any previous description (allows re-application with updated metadata)
|
||||||
|
if (wlWm.hdrImageCreator)
|
||||||
|
{
|
||||||
|
wp_image_description_creator_params_v1_destroy(wlWm.hdrImageCreator);
|
||||||
|
wlWm.hdrImageCreator = NULL;
|
||||||
|
}
|
||||||
|
if (wlWm.hdrImageDesc)
|
||||||
|
{
|
||||||
|
wp_image_description_v1_destroy(wlWm.hdrImageDesc);
|
||||||
|
wlWm.hdrImageDesc = NULL;
|
||||||
|
}
|
||||||
|
if (wlWm.colorSurface)
|
||||||
|
{
|
||||||
|
wp_color_management_surface_v1_destroy(wlWm.colorSurface);
|
||||||
|
wlWm.colorSurface = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlWm.hdrActive = false;
|
||||||
|
|
||||||
|
wlWm.hdrImageCreator =
|
||||||
|
wp_color_manager_v1_create_parametric_creator(wlWm.colorManager);
|
||||||
|
if (!wlWm.hdrImageCreator)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Failed to create parametric image description creator");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set primaries: BT.2020 for PQ HDR10, sRGB for scRGB/FP16
|
||||||
|
wp_image_description_creator_params_v1_set_primaries_named(
|
||||||
|
wlWm.hdrImageCreator,
|
||||||
|
hdrPQ ? WP_COLOR_MANAGER_V1_PRIMARIES_BT2020
|
||||||
|
: WP_COLOR_MANAGER_V1_PRIMARIES_SRGB);
|
||||||
|
|
||||||
|
// Select transfer function: PQ for PQ-encoded content, linear for scRGB/FP16
|
||||||
|
wp_image_description_creator_params_v1_set_tf_named(
|
||||||
|
wlWm.hdrImageCreator,
|
||||||
|
hdrPQ ? WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ
|
||||||
|
: WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR);
|
||||||
|
|
||||||
|
// Set luminance range from metadata.
|
||||||
|
// min_lum is in 0.0001 cd/m² units (multiplied by 10000 as per protocol).
|
||||||
|
// max_lum and reference_lum are in unscaled cd/m² units.
|
||||||
|
if (wlWm.cmHasLuminances)
|
||||||
|
wp_image_description_creator_params_v1_set_luminances(
|
||||||
|
wlWm.hdrImageCreator,
|
||||||
|
minDisplayLuminance > 0 ? minDisplayLuminance : 50,
|
||||||
|
maxDisplayLuminance > 0 ? maxDisplayLuminance / 10000 : 1000,
|
||||||
|
hdrPQ ? 203 : 80);
|
||||||
|
|
||||||
|
// Set mastering display primaries from frame HDR metadata.
|
||||||
|
// Always set when the compositor supports it, falling back to the
|
||||||
|
// primaries from the metadata (even if zero, in which case the
|
||||||
|
// compositor uses its own defaults).
|
||||||
|
if (wlWm.cmHasMasteringPrimaries)
|
||||||
|
{
|
||||||
|
wp_image_description_creator_params_v1_set_mastering_display_primaries(
|
||||||
|
wlWm.hdrImageCreator,
|
||||||
|
displayPrimary[0][0], displayPrimary[0][1],
|
||||||
|
displayPrimary[1][0], displayPrimary[1][1],
|
||||||
|
displayPrimary[2][0], displayPrimary[2][1],
|
||||||
|
whitePoint[0], whitePoint[1]);
|
||||||
|
|
||||||
|
// Set mastering luminance range so the compositor knows the target
|
||||||
|
// color volume for tone mapping. min_lum is in ×10000 units (0.0001 cd/m²),
|
||||||
|
// max_lum is in unscaled cd/m².
|
||||||
|
wp_image_description_creator_params_v1_set_mastering_luminance(
|
||||||
|
wlWm.hdrImageCreator,
|
||||||
|
minDisplayLuminance > 0 ? minDisplayLuminance : 50,
|
||||||
|
maxDisplayLuminance > 0 ? maxDisplayLuminance / 10000 : 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxCLL > 0)
|
||||||
|
wp_image_description_creator_params_v1_set_max_cll(wlWm.hdrImageCreator, maxCLL);
|
||||||
|
if (maxFALL > 0)
|
||||||
|
wp_image_description_creator_params_v1_set_max_fall(wlWm.hdrImageCreator, maxFALL);
|
||||||
|
|
||||||
|
wlWm.hdrImageDesc =
|
||||||
|
wp_image_description_creator_params_v1_create(wlWm.hdrImageCreator);
|
||||||
|
wlWm.hdrImageCreator = NULL; // consumed by create
|
||||||
|
|
||||||
|
if (!wlWm.hdrImageDesc)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Failed to create HDR image description");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlWm.colorSurface =
|
||||||
|
wp_color_manager_v1_get_surface(wlWm.colorManager, wlWm.surface);
|
||||||
|
if (!wlWm.colorSurface)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Failed to get color management surface");
|
||||||
|
wp_image_description_v1_destroy(wlWm.hdrImageDesc);
|
||||||
|
wlWm.hdrImageDesc = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_color_management_surface_v1_set_image_description(
|
||||||
|
wlWm.colorSurface, wlWm.hdrImageDesc,
|
||||||
|
WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL);
|
||||||
|
|
||||||
|
wlWm.hdrActive = true;
|
||||||
|
DEBUG_INFO("HDR image description set on surface (%s, %s, %u nits)",
|
||||||
|
hdrPQ ? "PQ" : "scRGB", hdrPQ ? "BT.2020" : "sRGB", maxDisplayLuminance);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool waylandRequestHDR(const uint16_t displayPrimary[3][2],
|
||||||
|
const uint16_t whitePoint[2], uint32_t maxDisplayLuminance,
|
||||||
|
uint32_t minDisplayLuminance, uint32_t maxCLL, uint32_t maxFALL,
|
||||||
|
bool hdrPQ)
|
||||||
|
{
|
||||||
|
if (!wlWm.cmFeaturesDone || !wlWm.cmHasParametric)
|
||||||
|
return false;
|
||||||
|
if (hdrPQ && !wlWm.cmHasTFSt2084PQ)
|
||||||
|
return false;
|
||||||
|
if (!hdrPQ && !wlWm.cmHasTFExtLinear)
|
||||||
|
return false;
|
||||||
|
if (hdrPQ && !wlWm.cmHasPrimariesBT2020)
|
||||||
|
return false;
|
||||||
|
if (!hdrPQ && !wlWm.cmHasPrimariesSRGB)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wlWm.pendingHDRPQ = hdrPQ;
|
||||||
|
|
||||||
|
// Write all metadata before setting the apply flag to ensure the
|
||||||
|
// render thread sees the complete HDR metadata when it observes
|
||||||
|
// pendingHDRApply == true.
|
||||||
|
memcpy(wlWm.pendingHDRDisplayPrimary, displayPrimary,
|
||||||
|
sizeof(wlWm.pendingHDRDisplayPrimary));
|
||||||
|
memcpy(wlWm.pendingHDRWhitePoint, whitePoint,
|
||||||
|
sizeof(wlWm.pendingHDRWhitePoint));
|
||||||
|
wlWm.pendingHDRMaxDisplayLuminance = maxDisplayLuminance;
|
||||||
|
wlWm.pendingHDRMinDisplayLuminance = minDisplayLuminance;
|
||||||
|
wlWm.pendingHDRMaxCLL = maxCLL;
|
||||||
|
wlWm.pendingHDRMaxFALL = maxFALL;
|
||||||
|
|
||||||
|
wlWm.pendingHDRClear = false;
|
||||||
|
atomic_thread_fence(memory_order_release);
|
||||||
|
wlWm.pendingHDRApply = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void waylandRequestClearHDR(void)
|
||||||
|
{
|
||||||
|
wlWm.pendingHDRClear = true;
|
||||||
|
wlWm.pendingHDRApply = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_OPENGL
|
||||||
|
|
||||||
|
static const EGLint eglBaseAttrs[] =
|
||||||
|
{
|
||||||
EGL_CONFORMANT , EGL_OPENGL_BIT,
|
EGL_CONFORMANT , EGL_OPENGL_BIT,
|
||||||
EGL_RENDERABLE_TYPE , EGL_OPENGL_BIT,
|
EGL_RENDERABLE_TYPE , EGL_OPENGL_BIT,
|
||||||
EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
|
EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
|
||||||
EGL_RED_SIZE , 8,
|
|
||||||
EGL_GREEN_SIZE , 8,
|
|
||||||
EGL_BLUE_SIZE , 8,
|
|
||||||
EGL_SAMPLE_BUFFERS , 0,
|
EGL_SAMPLE_BUFFERS , 0,
|
||||||
EGL_SAMPLES , 0,
|
EGL_SAMPLES , 0,
|
||||||
EGL_NONE
|
EGL_NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool eglChooseConfigWithDepth(EGLDisplay display, EGLint red,
|
||||||
|
EGLint green, EGLint blue, EGLint alpha, EGLint depth,
|
||||||
|
EGLConfig * config, const char ** desc)
|
||||||
|
{
|
||||||
|
// Build attr array with the base attrs plus color depth
|
||||||
|
EGLint attr[32];
|
||||||
|
int ai = 0;
|
||||||
|
attr[ai++] = EGL_RED_SIZE ; attr[ai++] = red;
|
||||||
|
attr[ai++] = EGL_GREEN_SIZE; attr[ai++] = green;
|
||||||
|
attr[ai++] = EGL_BLUE_SIZE ; attr[ai++] = blue;
|
||||||
|
attr[ai++] = EGL_ALPHA_SIZE; attr[ai++] = alpha;
|
||||||
|
attr[ai++] = EGL_BUFFER_SIZE; attr[ai++] = depth;
|
||||||
|
for (int i = 0; eglBaseAttrs[i] != EGL_NONE; ++i)
|
||||||
|
attr[ai++] = eglBaseAttrs[i];
|
||||||
|
attr[ai] = EGL_NONE;
|
||||||
|
|
||||||
|
EGLint num_config;
|
||||||
|
if (eglChooseConfig(display, attr, config, 1, &num_config) && num_config > 0)
|
||||||
|
{
|
||||||
|
if (desc)
|
||||||
|
*desc = red >= 16 ? "FP16 (RGBA16F)" :
|
||||||
|
red >= 10 ? "10-bit (RGBA10)" : "8-bit (RGBA8)";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool waylandOpenGLInit(void)
|
||||||
|
{
|
||||||
wlWm.glDisplay = waylandGetEGLDisplay();
|
wlWm.glDisplay = waylandGetEGLDisplay();
|
||||||
|
|
||||||
int maj, min;
|
int maj, min;
|
||||||
@@ -181,13 +444,31 @@ bool waylandOpenGLInit(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLint num_config;
|
EGLConfig config;
|
||||||
if (!eglChooseConfig(wlWm.glDisplay, attr, &wlWm.glConfig, 1, &num_config))
|
const char * configDesc = NULL;
|
||||||
|
|
||||||
|
// Probe for best available color depth: FP16 → 10-bit → 8-bit
|
||||||
|
if (!eglChooseConfigWithDepth(wlWm.glDisplay,
|
||||||
|
16, 16, 16, 0, 48, &config, &configDesc) &&
|
||||||
|
!eglChooseConfigWithDepth(wlWm.glDisplay,
|
||||||
|
10, 10, 10, 2, 32, &config, &configDesc) &&
|
||||||
|
!eglChooseConfigWithDepth(wlWm.glDisplay,
|
||||||
|
8, 8, 8, 0, 24, &config, &configDesc))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to choose config (eglError: 0x%x)", eglGetError());
|
DEBUG_ERROR("Failed to choose any EGL config");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wlWm.glConfig = config;
|
||||||
|
DEBUG_INFO("EGL config: %s", configDesc);
|
||||||
|
|
||||||
|
// Also store the 8-bit fallback for SDR contexts
|
||||||
|
if (configDesc && strcmp(configDesc, "8-bit (RGBA8)") != 0)
|
||||||
|
eglChooseConfigWithDepth(wlWm.glDisplay,
|
||||||
|
8, 8, 8, 0, 24, &wlWm.glConfigSDR, NULL);
|
||||||
|
else
|
||||||
|
wlWm.glConfigSDR = wlWm.glConfig;
|
||||||
|
|
||||||
wlWm.glSurface = eglCreateWindowSurface(wlWm.glDisplay, wlWm.glConfig, wlWm.eglWindow, NULL);
|
wlWm.glSurface = eglCreateWindowSurface(wlWm.glDisplay, wlWm.glConfig, wlWm.eglWindow, NULL);
|
||||||
if (wlWm.glSurface == EGL_NO_SURFACE)
|
if (wlWm.glSurface == EGL_NO_SURFACE)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -66,6 +66,9 @@ wayland_generate(
|
|||||||
wayland_generate(
|
wayland_generate(
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/staging/content-type/content-type-v1.xml"
|
"${WAYLAND_PROTOCOLS_BASE}/staging/content-type/content-type-v1.xml"
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-content-type-v1-client-protocol")
|
"${CMAKE_BINARY_DIR}/wayland/wayland-content-type-v1-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/staging/color-management/color-management-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-color-management-v1-client-protocol")
|
||||||
|
|
||||||
target_link_libraries(wayland_protocol
|
target_link_libraries(wayland_protocol
|
||||||
PkgConfig::WAYLAND
|
PkgConfig::WAYLAND
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
|
|||||||
else if (!strcmp(interface, wp_content_type_manager_v1_interface.name))
|
else if (!strcmp(interface, wp_content_type_manager_v1_interface.name))
|
||||||
wlWm.contentTypeManager = wl_registry_bind(wlWm.registry, name,
|
wlWm.contentTypeManager = wl_registry_bind(wlWm.registry, name,
|
||||||
&wp_content_type_manager_v1_interface, 1);
|
&wp_content_type_manager_v1_interface, 1);
|
||||||
|
else if (!strcmp(interface, wp_color_manager_v1_interface.name))
|
||||||
|
wlWm.colorManager = wl_registry_bind(wlWm.registry, name,
|
||||||
|
&wp_color_manager_v1_interface, 1);
|
||||||
else if (wlWm.desktop->registryGlobalHandler(
|
else if (wlWm.desktop->registryGlobalHandler(
|
||||||
data, registry, name, interface, version))
|
data, registry, name, interface, version))
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "common/option.h"
|
#include "common/option.h"
|
||||||
|
#include "interface/renderer.h"
|
||||||
|
|
||||||
#include "dynamic/wayland_desktops.h"
|
#include "dynamic/wayland_desktops.h"
|
||||||
|
|
||||||
@@ -143,6 +144,9 @@ static bool waylandInit(const LG_DSInitParams params)
|
|||||||
if (!waylandRegistryInit())
|
if (!waylandRegistryInit())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (!waylandColorMgmtInit())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!waylandActivationInit())
|
if (!waylandActivationInit())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -189,6 +193,7 @@ static void waylandFree(void)
|
|||||||
waylandPresentationFree();
|
waylandPresentationFree();
|
||||||
waylandInputFree();
|
waylandInputFree();
|
||||||
waylandOutputFree();
|
waylandOutputFree();
|
||||||
|
waylandColorMgmtFree();
|
||||||
waylandRegistryFree();
|
waylandRegistryFree();
|
||||||
waylandCursorFree();
|
waylandCursorFree();
|
||||||
wl_display_disconnect(wlWm.display);
|
wl_display_disconnect(wlWm.display);
|
||||||
@@ -202,6 +207,12 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prop == LG_DS_NATIVE_HDR)
|
||||||
|
{
|
||||||
|
*(bool *)ret = wlWm.cmCanDoHDR;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,6 +238,22 @@ static void waylandMinimize(void)
|
|||||||
wlWm.desktop->minimize();
|
wlWm.desktop->minimize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool waylandHDRCallback(const void * rendererFormat)
|
||||||
|
{
|
||||||
|
if (!wlWm.colorManager)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const LG_RendererFormat * format = (const LG_RendererFormat *)rendererFormat;
|
||||||
|
if (format->hdr)
|
||||||
|
return waylandRequestHDR(format->hdrDisplayPrimary, format->hdrWhitePoint,
|
||||||
|
format->hdrMaxDisplayLuminance, format->hdrMinDisplayLuminance,
|
||||||
|
format->hdrMaxContentLightLevel, format->hdrMaxFrameAverageLightLevel,
|
||||||
|
format->hdrPQ);
|
||||||
|
else
|
||||||
|
waylandRequestClearHDR();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
struct LG_DisplayServerOps LGDS_Wayland =
|
struct LG_DisplayServerOps LGDS_Wayland =
|
||||||
{
|
{
|
||||||
.name = "Wayland",
|
.name = "Wayland",
|
||||||
@@ -276,6 +303,8 @@ struct LG_DisplayServerOps LGDS_Wayland =
|
|||||||
.getFullscreen = waylandGetFullscreen,
|
.getFullscreen = waylandGetFullscreen,
|
||||||
.minimize = waylandMinimize,
|
.minimize = waylandMinimize,
|
||||||
|
|
||||||
|
.setHDRImageDescription = waylandHDRCallback,
|
||||||
|
|
||||||
.cbInit = waylandCBInit,
|
.cbInit = waylandCBInit,
|
||||||
.cbNotice = waylandCBNotice,
|
.cbNotice = waylandCBNotice,
|
||||||
.cbRelease = waylandCBRelease,
|
.cbRelease = waylandCBRelease,
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
#include "wayland-xdg-activation-v1-client-protocol.h"
|
#include "wayland-xdg-activation-v1-client-protocol.h"
|
||||||
#include "wayland-fractional-scale-v1-client-protocol.h"
|
#include "wayland-fractional-scale-v1-client-protocol.h"
|
||||||
#include "wayland-content-type-v1-client-protocol.h"
|
#include "wayland-content-type-v1-client-protocol.h"
|
||||||
|
#include "wayland-color-management-v1-client-protocol.h"
|
||||||
|
|
||||||
#include "scale.h"
|
#include "scale.h"
|
||||||
|
|
||||||
@@ -120,7 +121,8 @@ struct WaylandDSState
|
|||||||
|
|
||||||
#ifdef ENABLE_OPENGL
|
#ifdef ENABLE_OPENGL
|
||||||
EGLDisplay glDisplay;
|
EGLDisplay glDisplay;
|
||||||
EGLConfig glConfig;
|
EGLConfig glConfig; // primary config (best bit depth probed)
|
||||||
|
EGLConfig glConfigSDR; // fallback 8-bit SDR config
|
||||||
EGLSurface glSurface;
|
EGLSurface glSurface;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -180,6 +182,42 @@ struct WaylandDSState
|
|||||||
struct wp_content_type_manager_v1 * contentTypeManager;
|
struct wp_content_type_manager_v1 * contentTypeManager;
|
||||||
struct wp_content_type_v1 * contentType;
|
struct wp_content_type_v1 * contentType;
|
||||||
|
|
||||||
|
// Color management (HDR)
|
||||||
|
struct wp_color_manager_v1 * colorManager;
|
||||||
|
struct wp_color_management_surface_v1 * colorSurface;
|
||||||
|
struct wp_image_description_v1 * hdrImageDesc;
|
||||||
|
struct wp_image_description_creator_params_v1 * hdrImageCreator;
|
||||||
|
|
||||||
|
// Set to true when an HDR image description is active on the surface
|
||||||
|
bool hdrActive;
|
||||||
|
|
||||||
|
// wp_color_manager_v1 feature advertisement tracking.
|
||||||
|
// Set to true after the done event for the color-manager has been received
|
||||||
|
// and all compositor capabilities have been recorded.
|
||||||
|
bool cmFeaturesDone;
|
||||||
|
bool cmHasParametric;
|
||||||
|
bool cmHasLuminances;
|
||||||
|
bool cmHasMasteringPrimaries;
|
||||||
|
bool cmHasTFSt2084PQ;
|
||||||
|
bool cmHasTFExtLinear;
|
||||||
|
bool cmHasPrimariesBT2020;
|
||||||
|
bool cmHasPrimariesSRGB;
|
||||||
|
bool cmHasPerceptualIntent;
|
||||||
|
bool cmCanDoHDR; // true if compositor supports features needed for HDR
|
||||||
|
|
||||||
|
// Pending HDR format to apply (set by frame thread, applied in swap buffers).
|
||||||
|
// pendingHDRApply and pendingHDRClear are accessed from multiple threads
|
||||||
|
// without a lock, so they must be atomic.
|
||||||
|
_Atomic(bool) pendingHDRApply;
|
||||||
|
_Atomic(bool) pendingHDRClear;
|
||||||
|
bool pendingHDRPQ;
|
||||||
|
uint16_t pendingHDRDisplayPrimary[3][2];
|
||||||
|
uint16_t pendingHDRWhitePoint[2];
|
||||||
|
uint32_t pendingHDRMaxDisplayLuminance;
|
||||||
|
uint32_t pendingHDRMinDisplayLuminance;
|
||||||
|
uint32_t pendingHDRMaxCLL;
|
||||||
|
uint32_t pendingHDRMaxFALL;
|
||||||
|
|
||||||
LGEvent * frameEvent;
|
LGEvent * frameEvent;
|
||||||
|
|
||||||
struct wl_list poll; // WaylandPoll::link
|
struct wl_list poll; // WaylandPoll::link
|
||||||
@@ -262,6 +300,24 @@ void waylandGLSetSwapInterval(int interval);
|
|||||||
void waylandGLSwapBuffers(void);
|
void waylandGLSwapBuffers(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// HDR color management (Wayland color-management-v1)
|
||||||
|
bool waylandColorMgmtInit(void);
|
||||||
|
void waylandColorMgmtFree(void);
|
||||||
|
void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2],
|
||||||
|
const uint16_t whitePoint[2], uint32_t maxDisplayLuminance,
|
||||||
|
uint32_t minDisplayLuminance, uint32_t maxCLL, uint32_t maxFALL,
|
||||||
|
bool hdrPQ);
|
||||||
|
void waylandClearHDRImageDescription(void);
|
||||||
|
|
||||||
|
// Queue HDR change from any thread (applied in waylandEGLSwapBuffers).
|
||||||
|
// Returns true if the request was queued, false if HDR is not available
|
||||||
|
// (no color manager, missing features, or unsupported TF/primaries).
|
||||||
|
bool waylandRequestHDR(const uint16_t displayPrimary[3][2],
|
||||||
|
const uint16_t whitePoint[2], uint32_t maxDisplayLuminance,
|
||||||
|
uint32_t minDisplayLuminance, uint32_t maxCLL, uint32_t maxFALL,
|
||||||
|
bool hdrPQ);
|
||||||
|
void waylandRequestClearHDR(void);
|
||||||
|
|
||||||
// idle module
|
// idle module
|
||||||
bool waylandIdleInit(void);
|
bool waylandIdleInit(void);
|
||||||
void waylandIdleFree(void);
|
void waylandIdleFree(void);
|
||||||
|
|||||||
@@ -72,6 +72,13 @@ void app_setFullscreen(bool fs);
|
|||||||
bool app_getFullscreen(void);
|
bool app_getFullscreen(void);
|
||||||
bool app_getProp(LG_DSProperty prop, void * ret);
|
bool app_getProp(LG_DSProperty prop, void * ret);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the last call to setHDRImageDescription() failed.
|
||||||
|
* When this returns true, the renderer should fall back to software
|
||||||
|
* tone-mapping even on a compositor that reports LG_DS_NATIVE_HDR.
|
||||||
|
*/
|
||||||
|
bool app_getHDRDescFailed(void);
|
||||||
|
|
||||||
#ifdef ENABLE_EGL
|
#ifdef ENABLE_EGL
|
||||||
EGLDisplay app_getEGLDisplay(void);
|
EGLDisplay app_getEGLDisplay(void);
|
||||||
EGLNativeWindowType app_getEGLNativeWindow(void);
|
EGLNativeWindowType app_getEGLNativeWindow(void);
|
||||||
|
|||||||
@@ -53,6 +53,14 @@ typedef enum LG_DSProperty
|
|||||||
* return data type: bool
|
* return data type: bool
|
||||||
*/
|
*/
|
||||||
LG_DS_WARP_SUPPORT,
|
LG_DS_WARP_SUPPORT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns true if the display server supports native HDR via
|
||||||
|
* setHDRImageDescription (i.e., the compositor actually has the
|
||||||
|
* required color-management features to process HDR content).
|
||||||
|
* return data type: bool
|
||||||
|
*/
|
||||||
|
LG_DS_NATIVE_HDR,
|
||||||
}
|
}
|
||||||
LG_DSProperty;
|
LG_DSProperty;
|
||||||
|
|
||||||
@@ -213,6 +221,13 @@ struct LG_DisplayServerOps
|
|||||||
void (*setFullscreen)(bool fs);
|
void (*setFullscreen)(bool fs);
|
||||||
void (*minimize)(void);
|
void (*minimize)(void);
|
||||||
|
|
||||||
|
/* HDR color management - set display image description.
|
||||||
|
* Optional, if not supported set to NULL.
|
||||||
|
* Returns true on success, false if the description could not be applied
|
||||||
|
* (e.g., compositor doesn't support the required transfer function or
|
||||||
|
* primaries, or the image description creation failed). */
|
||||||
|
bool (*setHDRImageDescription)(const void * rendererFormat);
|
||||||
|
|
||||||
/* clipboard support, optional, if not supported set to NULL */
|
/* clipboard support, optional, if not supported set to NULL */
|
||||||
bool (*cbInit)(void);
|
bool (*cbInit)(void);
|
||||||
void (*cbNotice)(LG_ClipboardData type);
|
void (*cbNotice)(LG_ClipboardData type);
|
||||||
|
|||||||
@@ -84,6 +84,14 @@ typedef struct LG_RendererFormat
|
|||||||
unsigned int pitch; // scanline bytes (or compressed size)
|
unsigned int pitch; // scanline bytes (or compressed size)
|
||||||
unsigned int bpp; // bits per pixel (zero if compressed)
|
unsigned int bpp; // bits per pixel (zero if compressed)
|
||||||
LG_RendererRotate rotate; // guest rotation
|
LG_RendererRotate rotate; // guest rotation
|
||||||
|
|
||||||
|
// HDR static metadata (from KVMFRFrame, valid when hdr is true)
|
||||||
|
uint16_t hdrDisplayPrimary[3][2];
|
||||||
|
uint16_t hdrWhitePoint[2];
|
||||||
|
uint32_t hdrMaxDisplayLuminance;
|
||||||
|
uint32_t hdrMinDisplayLuminance;
|
||||||
|
uint32_t hdrMaxContentLightLevel;
|
||||||
|
uint32_t hdrMaxFrameAverageLightLevel;
|
||||||
}
|
}
|
||||||
LG_RendererFormat;
|
LG_RendererFormat;
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ struct EGL_Desktop
|
|||||||
|
|
||||||
// map HDR content to SDR
|
// map HDR content to SDR
|
||||||
bool mapHDRtoSDR;
|
bool mapHDRtoSDR;
|
||||||
|
bool nativeHDR;
|
||||||
int peakLuminance;
|
int peakLuminance;
|
||||||
int maxCLL;
|
int maxCLL;
|
||||||
|
|
||||||
@@ -548,7 +549,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
|||||||
{
|
{
|
||||||
.type = EGL_UNIFORM_TYPE_1I,
|
.type = EGL_UNIFORM_TYPE_1I,
|
||||||
.location = shader->uCBMode,
|
.location = shader->uCBMode,
|
||||||
.f = { desktop->cbMode }
|
.i = { desktop->cbMode }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.type = EGL_UNIFORM_TYPE_1I,
|
.type = EGL_UNIFORM_TYPE_1I,
|
||||||
@@ -558,7 +559,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
|||||||
{
|
{
|
||||||
.type = EGL_UNIFORM_TYPE_1I,
|
.type = EGL_UNIFORM_TYPE_1I,
|
||||||
.location = shader->uMapHDRtoSDR,
|
.location = shader->uMapHDRtoSDR,
|
||||||
.i = { desktop->mapHDRtoSDR }
|
.i = { desktop->mapHDRtoSDR && !desktop->nativeHDR }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.type = EGL_UNIFORM_TYPE_1F,
|
.type = EGL_UNIFORM_TYPE_1F,
|
||||||
@@ -568,7 +569,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
|||||||
{
|
{
|
||||||
.type = EGL_UNIFORM_TYPE_1I,
|
.type = EGL_UNIFORM_TYPE_1I,
|
||||||
.location = shader->uMapHDRPQ,
|
.location = shader->uMapHDRPQ,
|
||||||
.f = { desktop->hdrPQ }
|
.i = { desktop->hdrPQ }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -579,6 +580,11 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void egl_desktopSetNativeHDR(EGL_Desktop * desktop, bool nativeHDR)
|
||||||
|
{
|
||||||
|
desktop->nativeHDR = nativeHDR;
|
||||||
|
}
|
||||||
|
|
||||||
void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height)
|
void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height)
|
||||||
{
|
{
|
||||||
if (!desktop->spiceTexture)
|
if (!desktop->spiceTexture)
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop, EGLDisplay * display,
|
|||||||
void egl_desktopFree(EGL_Desktop ** desktop);
|
void egl_desktopFree(EGL_Desktop ** desktop);
|
||||||
|
|
||||||
void egl_desktopConfigUI(EGL_Desktop * desktop);
|
void egl_desktopConfigUI(EGL_Desktop * desktop);
|
||||||
|
void egl_desktopSetNativeHDR(EGL_Desktop * desktop, bool nativeHDR);
|
||||||
bool egl_desktopSetup (EGL_Desktop * desktop, const LG_RendererFormat format);
|
bool egl_desktopSetup (EGL_Desktop * desktop, const LG_RendererFormat format);
|
||||||
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd,
|
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd,
|
||||||
const FrameDamageRect * damageRects, int damageRectsCount);
|
const FrameDamageRect * damageRects, int damageRectsCount);
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ struct Inst
|
|||||||
|
|
||||||
bool showSpice;
|
bool showSpice;
|
||||||
int spiceWidth, spiceHeight;
|
int spiceWidth, spiceHeight;
|
||||||
|
|
||||||
|
bool configSupportsHDR; // true if we probed FP16 or 10-bit EGL config
|
||||||
|
bool hdr; // true if current frame format is HDR
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct Option egl_options[] =
|
static struct Option egl_options[] =
|
||||||
@@ -607,6 +610,26 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo
|
|||||||
egl_cursorSetScale(this->cursor, scale);
|
egl_cursorSetScale(this->cursor, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track HDR state and warn if HDR frames arrive without any HDR path
|
||||||
|
this->hdr = format.hdr;
|
||||||
|
if (format.hdr)
|
||||||
|
{
|
||||||
|
bool nativeHDR = false;
|
||||||
|
app_getProp(LG_DS_NATIVE_HDR, &nativeHDR);
|
||||||
|
if (!this->configSupportsHDR && !nativeHDR)
|
||||||
|
DEBUG_WARN("HDR frames being displayed on an 8-bit EGL surface "
|
||||||
|
"without a color-managed compositor; tones may be incorrect");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the display server supports native HDR (via setHDRImageDescription),
|
||||||
|
// disable software tone-mapping to avoid double tone-mapping.
|
||||||
|
// However, if the last call to setHDRImageDescription actually failed
|
||||||
|
// (e.g., compositor missing required primaries), keep tone-mapping on.
|
||||||
|
bool nativeHDR = false;
|
||||||
|
app_getProp(LG_DS_NATIVE_HDR, &nativeHDR);
|
||||||
|
egl_desktopSetNativeHDR(this->desktop,
|
||||||
|
format.hdr && nativeHDR && !app_getHDRDescFailed());
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@@ -774,22 +797,51 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Probe for best available color depth: FP16 → 10-bit → 8-bit
|
||||||
|
static const struct { EGLint r, g, b, a, depth; const char * desc; } configs[] =
|
||||||
|
{
|
||||||
|
{ 16, 16, 16, 0, 48, "FP16 (RGBA16F)" },
|
||||||
|
{ 10, 10, 10, 2, 32, "10-bit (RGBA10)" },
|
||||||
|
{ 8, 8, 8, 0, 24, "8-bit (RGBA8)" },
|
||||||
|
};
|
||||||
|
EGLint num_config;
|
||||||
|
const char * configDesc = NULL;
|
||||||
|
int chosen = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)(sizeof(configs) / sizeof(configs[0])); ++i)
|
||||||
|
{
|
||||||
EGLint attr[] =
|
EGLint attr[] =
|
||||||
{
|
{
|
||||||
EGL_BUFFER_SIZE , 30,
|
EGL_RED_SIZE , configs[i].r,
|
||||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
EGL_GREEN_SIZE , configs[i].g,
|
||||||
|
EGL_BLUE_SIZE , configs[i].b,
|
||||||
|
EGL_ALPHA_SIZE , configs[i].a,
|
||||||
|
EGL_BUFFER_SIZE , configs[i].depth,
|
||||||
|
EGL_RENDERABLE_TYPE , EGL_OPENGL_ES2_BIT,
|
||||||
EGL_SAMPLE_BUFFERS , maxSamples > 0 ? 1 : 0,
|
EGL_SAMPLE_BUFFERS , maxSamples > 0 ? 1 : 0,
|
||||||
EGL_SAMPLES , maxSamples,
|
EGL_SAMPLES , maxSamples,
|
||||||
EGL_NONE
|
EGL_NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
EGLint num_config;
|
if (eglChooseConfig(this->display, attr, &this->configs, 1, &num_config) &&
|
||||||
if (!eglChooseConfig(this->display, attr, &this->configs, 1, &num_config))
|
num_config > 0)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to choose config (eglError: 0x%x)", eglGetError());
|
configDesc = configs[i].desc;
|
||||||
|
chosen = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chosen < 0)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to choose any EGL config");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->configSupportsHDR = chosen <= 1; // FP16 or 10-bit
|
||||||
|
DEBUG_INFO("EGL config: %s%s", configDesc,
|
||||||
|
this->configSupportsHDR ? " (HDR capable)" : "");
|
||||||
|
|
||||||
const EGLint surfattr[] =
|
const EGLint surfattr[] =
|
||||||
{
|
{
|
||||||
EGL_RENDER_BUFFER, this->opt.doubleBuffer ? EGL_BACK_BUFFER : EGL_SINGLE_BUFFER,
|
EGL_RENDER_BUFFER, this->opt.doubleBuffer ? EGL_BACK_BUFFER : EGL_SINGLE_BUFFER,
|
||||||
|
|||||||
@@ -618,6 +618,11 @@ bool app_getProp(LG_DSProperty prop, void * ret)
|
|||||||
return g_state.ds->getProp(prop, ret);
|
return g_state.ds->getProp(prop, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool app_getHDRDescFailed(void)
|
||||||
|
{
|
||||||
|
return atomic_load(&g_state.hdrDescFailed);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_EGL
|
#ifdef ENABLE_EGL
|
||||||
EGLDisplay app_getEGLDisplay(void)
|
EGLDisplay app_getEGLDisplay(void)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -641,6 +641,25 @@ int main_frameThread(void * unused)
|
|||||||
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;
|
||||||
|
|
||||||
|
if (lgrFormat.hdr)
|
||||||
|
{
|
||||||
|
memcpy(lgrFormat.hdrDisplayPrimary, frame->hdrDisplayPrimary, sizeof(lgrFormat.hdrDisplayPrimary));
|
||||||
|
memcpy(lgrFormat.hdrWhitePoint , frame->hdrWhitePoint , sizeof(lgrFormat.hdrWhitePoint ));
|
||||||
|
lgrFormat.hdrMaxDisplayLuminance = frame->hdrMaxDisplayLuminance;
|
||||||
|
lgrFormat.hdrMinDisplayLuminance = frame->hdrMinDisplayLuminance;
|
||||||
|
lgrFormat.hdrMaxContentLightLevel = frame->hdrMaxContentLightLevel;
|
||||||
|
lgrFormat.hdrMaxFrameAverageLightLevel = frame->hdrMaxFrameAverageLightLevel;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memset(lgrFormat.hdrDisplayPrimary, 0, sizeof(lgrFormat.hdrDisplayPrimary));
|
||||||
|
memset(lgrFormat.hdrWhitePoint , 0, sizeof(lgrFormat.hdrWhitePoint ));
|
||||||
|
lgrFormat.hdrMaxDisplayLuminance = 0;
|
||||||
|
lgrFormat.hdrMinDisplayLuminance = 0;
|
||||||
|
lgrFormat.hdrMaxContentLightLevel = 0;
|
||||||
|
lgrFormat.hdrMaxFrameAverageLightLevel = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (frame->flags & FRAME_FLAG_TRUNCATED)
|
if (frame->flags & FRAME_FLAG_TRUNCATED)
|
||||||
{
|
{
|
||||||
const float needed =
|
const float needed =
|
||||||
@@ -728,6 +747,17 @@ int main_frameThread(void * unused)
|
|||||||
}
|
}
|
||||||
LG_UNLOCK(g_state.lgrLock);
|
LG_UNLOCK(g_state.lgrLock);
|
||||||
|
|
||||||
|
if (g_state.ds->setHDRImageDescription)
|
||||||
|
{
|
||||||
|
if (!g_state.ds->setHDRImageDescription(&lgrFormat))
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Display server failed to apply HDR image description");
|
||||||
|
atomic_store(&g_state.hdrDescFailed, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
atomic_store(&g_state.hdrDescFailed, false);
|
||||||
|
}
|
||||||
|
|
||||||
g_state.srcSize.x = lgrFormat.screenWidth;
|
g_state.srcSize.x = lgrFormat.screenWidth;
|
||||||
g_state.srcSize.y = lgrFormat.screenHeight;
|
g_state.srcSize.y = lgrFormat.screenHeight;
|
||||||
g_state.haveSrcSize = true;
|
g_state.haveSrcSize = true;
|
||||||
|
|||||||
@@ -154,6 +154,11 @@ struct AppState
|
|||||||
enum MicDefaultState micDefaultState;
|
enum MicDefaultState micDefaultState;
|
||||||
|
|
||||||
int spiceHotX, spiceHotY;
|
int spiceHotX, spiceHotY;
|
||||||
|
|
||||||
|
// Set to true when setHDRImageDescription() returns false, indicating
|
||||||
|
// the display server could not apply the HDR image description.
|
||||||
|
// Checked by the renderer to fall back to software tone-mapping.
|
||||||
|
_Atomic(bool) hdrDescFailed;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AppParams
|
struct AppParams
|
||||||
|
|||||||
@@ -162,6 +162,15 @@ typedef struct KVMFRFrame
|
|||||||
uint32_t damageRectsCount; // the number of damage rectangles (zero for full-frame damage)
|
uint32_t damageRectsCount; // the number of damage rectangles (zero for full-frame damage)
|
||||||
FrameDamageRect damageRects[KVMFR_MAX_DAMAGE_RECTS];
|
FrameDamageRect damageRects[KVMFR_MAX_DAMAGE_RECTS];
|
||||||
KVMFRFrameFlags flags; // bit field combination of FRAME_FLAG_*
|
KVMFRFrameFlags flags; // bit field combination of FRAME_FLAG_*
|
||||||
|
|
||||||
|
// HDR static metadata (valid when FRAME_FLAG_HDR is set)
|
||||||
|
// Display color primaries in 0.00002 units (SMPTE ST 2086 format)
|
||||||
|
uint16_t hdrDisplayPrimary[3][2]; // Rx,Ry, Gx,Gy, Bx,By
|
||||||
|
uint16_t hdrWhitePoint[2]; // Wx, Wy
|
||||||
|
uint32_t hdrMaxDisplayLuminance; // Max 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 hdrMaxFrameAverageLightLevel; // MaxFALL (cd/m²)
|
||||||
}
|
}
|
||||||
KVMFRFrame;
|
KVMFRFrame;
|
||||||
|
|
||||||
|
|||||||
2
gl-check
2
gl-check
@@ -49,7 +49,7 @@ getCalls() {
|
|||||||
cat > "$tempdir/query" <<EOF
|
cat > "$tempdir/query" <<EOF
|
||||||
set output print
|
set output print
|
||||||
set bind-root false
|
set bind-root false
|
||||||
m callExpr(callee(functionDecl(matchesName("::$query")).bind("func")))
|
m callExpr(callee(functionDecl(matchesName("::$query"), unless(isStatic())).bind("func")))
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
"${command[@]}" -exec "$CLANG_QUERY" -p "$BUILD_DIR/compile_commands.json" -f "$tempdir/query" {} + | \
|
"${command[@]}" -exec "$CLANG_QUERY" -p "$BUILD_DIR/compile_commands.json" -f "$tempdir/query" {} + | \
|
||||||
|
|||||||
@@ -45,14 +45,14 @@ static const struct LGMPQueueConfig POINTER_QUEUE_CONFIG =
|
|||||||
|
|
||||||
static const UINT IDDCX_VERSION_1_10 = 0x1A00;
|
static const UINT IDDCX_VERSION_1_10 = 0x1A00;
|
||||||
|
|
||||||
#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \
|
#ifdef HAS_IDDCX_110
|
||||||
(IDDCX_VERSION_MAJOR > 1 || (IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10))
|
|
||||||
static inline IDDCX_WIRE_BITS_PER_COMPONENT GetWireBitsPerComponent(bool hdr)
|
static inline IDDCX_WIRE_BITS_PER_COMPONENT GetWireBitsPerComponent(bool hdr)
|
||||||
{
|
{
|
||||||
IDDCX_WIRE_BITS_PER_COMPONENT bits = {};
|
IDDCX_WIRE_BITS_PER_COMPONENT bits = {};
|
||||||
bits.Rgb = IDDCX_BITS_PER_COMPONENT_8;
|
bits.Rgb = IDDCX_BITS_PER_COMPONENT_8;
|
||||||
if (hdr)
|
if (hdr)
|
||||||
bits.Rgb = (IDDCX_BITS_PER_COMPONENT)(bits.Rgb | IDDCX_BITS_PER_COMPONENT_10);
|
bits.Rgb = (IDDCX_BITS_PER_COMPONENT)(bits.Rgb |
|
||||||
|
IDDCX_BITS_PER_COMPONENT_10 | IDDCX_BITS_PER_COMPONENT_16);
|
||||||
bits.YCbCr444 = IDDCX_BITS_PER_COMPONENT_NONE;
|
bits.YCbCr444 = IDDCX_BITS_PER_COMPONENT_NONE;
|
||||||
bits.YCbCr422 = IDDCX_BITS_PER_COMPONENT_NONE;
|
bits.YCbCr422 = IDDCX_BITS_PER_COMPONENT_NONE;
|
||||||
bits.YCbCr420 = IDDCX_BITS_PER_COMPONENT_NONE;
|
bits.YCbCr420 = IDDCX_BITS_PER_COMPONENT_NONE;
|
||||||
@@ -74,8 +74,7 @@ void CIndirectDeviceContext::QueryIddCxCapabilities()
|
|||||||
|
|
||||||
m_iddCxVersion = ver.IddCxVersion;
|
m_iddCxVersion = ver.IddCxVersion;
|
||||||
|
|
||||||
#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \
|
#ifdef HAS_IDDCX_110
|
||||||
(IDDCX_VERSION_MAJOR > 1 || (IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10))
|
|
||||||
const bool hasIddCx110DDIs =
|
const bool hasIddCx110DDIs =
|
||||||
!!IDD_IS_FUNCTION_AVAILABLE(IddCxSwapChainReleaseAndAcquireBuffer2) &&
|
!!IDD_IS_FUNCTION_AVAILABLE(IddCxSwapChainReleaseAndAcquireBuffer2) &&
|
||||||
!!IDD_IS_FUNCTION_AVAILABLE(IddCxMonitorQueryHardwareCursor3) &&
|
!!IDD_IS_FUNCTION_AVAILABLE(IddCxMonitorQueryHardwareCursor3) &&
|
||||||
@@ -126,8 +125,7 @@ void CIndirectDeviceContext::InitAdapter()
|
|||||||
* driver will not work. This behaviour is not documented by Microsoft.
|
* driver will not work. This behaviour is not documented by Microsoft.
|
||||||
*/
|
*/
|
||||||
caps.Flags = IDDCX_ADAPTER_FLAGS_USE_SMALLEST_MODE;
|
caps.Flags = IDDCX_ADAPTER_FLAGS_USE_SMALLEST_MODE;
|
||||||
#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \
|
#ifdef HAS_IDDCX_110
|
||||||
(IDDCX_VERSION_MAJOR > 1 || (IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10))
|
|
||||||
if (CanUseIddCx110DDIs())
|
if (CanUseIddCx110DDIs())
|
||||||
caps.Flags |= IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16;
|
caps.Flags |= IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16;
|
||||||
#endif
|
#endif
|
||||||
@@ -389,8 +387,7 @@ NTSTATUS CIndirectDeviceContext::MonitorQueryTargetModes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \
|
#ifdef HAS_IDDCX_110
|
||||||
(IDDCX_VERSION_MAJOR > 1 || (IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10))
|
|
||||||
NTSTATUS CIndirectDeviceContext::ParseMonitorDescription2(
|
NTSTATUS CIndirectDeviceContext::ParseMonitorDescription2(
|
||||||
const IDARG_IN_PARSEMONITORDESCRIPTION2* inArgs,
|
const IDARG_IN_PARSEMONITORDESCRIPTION2* inArgs,
|
||||||
IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs)
|
IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs)
|
||||||
@@ -446,8 +443,7 @@ bool CIndirectDeviceContext::UpdateMonitorModes()
|
|||||||
if (!m_monitor)
|
if (!m_monitor)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \
|
#ifdef HAS_IDDCX_110
|
||||||
(IDDCX_VERSION_MAJOR > 1 || (IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10))
|
|
||||||
if (CanUseIddCx110DDIs())
|
if (CanUseIddCx110DDIs())
|
||||||
{
|
{
|
||||||
IDDCX_TARGET_MODE2* modes = (IDDCX_TARGET_MODE2*)_malloca(
|
IDDCX_TARGET_MODE2* modes = (IDDCX_TARGET_MODE2*)_malloca(
|
||||||
@@ -829,6 +825,35 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
|||||||
++m_formatVer;
|
++m_formatVer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect HDR metadata changes that require a format version bump
|
||||||
|
// so the client knows to re-apply the HDR image description.
|
||||||
|
if (srcFormat.hdr)
|
||||||
|
{
|
||||||
|
if (!m_lastHDRActive ||
|
||||||
|
memcmp(m_lastHDRDisplayPrimary, srcFormat.displayPrimary, sizeof(m_lastHDRDisplayPrimary)) != 0 ||
|
||||||
|
memcmp(m_lastHDRWhitePoint , srcFormat.whitePoint , sizeof(m_lastHDRWhitePoint )) != 0 ||
|
||||||
|
m_lastHDRMaxDisplayLuminance != srcFormat.maxDisplayLuminance ||
|
||||||
|
m_lastHDRMinDisplayLuminance != srcFormat.minDisplayLuminance ||
|
||||||
|
m_lastHDRMaxContentLightLevel != srcFormat.maxContentLightLevel ||
|
||||||
|
m_lastHDRMaxFrameAverageLightLevel != srcFormat.maxFrameAverageLightLevel)
|
||||||
|
{
|
||||||
|
++m_formatVer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_lastHDRActive)
|
||||||
|
{
|
||||||
|
// HDR was turned off
|
||||||
|
++m_formatVer;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_lastHDRActive = srcFormat.hdr;
|
||||||
|
memcpy(m_lastHDRDisplayPrimary, srcFormat.displayPrimary, sizeof(m_lastHDRDisplayPrimary));
|
||||||
|
memcpy(m_lastHDRWhitePoint , srcFormat.whitePoint , sizeof(m_lastHDRWhitePoint ));
|
||||||
|
m_lastHDRMaxDisplayLuminance = srcFormat.maxDisplayLuminance;
|
||||||
|
m_lastHDRMinDisplayLuminance = srcFormat.minDisplayLuminance;
|
||||||
|
m_lastHDRMaxContentLightLevel = srcFormat.maxContentLightLevel;
|
||||||
|
m_lastHDRMaxFrameAverageLightLevel = srcFormat.maxFrameAverageLightLevel;
|
||||||
|
|
||||||
if (++m_frameIndex == LGMP_Q_FRAME_LEN)
|
if (++m_frameIndex == LGMP_Q_FRAME_LEN)
|
||||||
m_frameIndex = 0;
|
m_frameIndex = 0;
|
||||||
|
|
||||||
@@ -850,9 +875,6 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
|||||||
(dstFormat.hdr ? FRAME_FLAG_HDR : 0) |
|
(dstFormat.hdr ? FRAME_FLAG_HDR : 0) |
|
||||||
(dstFormat.hdrPQ ? FRAME_FLAG_HDR_PQ : 0);
|
(dstFormat.hdrPQ ? FRAME_FLAG_HDR_PQ : 0);
|
||||||
|
|
||||||
if (dstFormat.format == FRAME_TYPE_RGBA16F)
|
|
||||||
flags |= FRAME_FLAG_HDR;
|
|
||||||
|
|
||||||
if (maxRows < dstFormat.desc.Height)
|
if (maxRows < dstFormat.desc.Height)
|
||||||
flags |= FRAME_FLAG_TRUNCATED;
|
flags |= FRAME_FLAG_TRUNCATED;
|
||||||
|
|
||||||
@@ -870,6 +892,18 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
|||||||
fi->flags = flags;
|
fi->flags = flags;
|
||||||
fi->rotation = FRAME_ROT_0;
|
fi->rotation = FRAME_ROT_0;
|
||||||
fi->type = dstFormat.format;
|
fi->type = dstFormat.format;
|
||||||
|
|
||||||
|
// Populate HDR metadata if the frame is HDR
|
||||||
|
if (flags & FRAME_FLAG_HDR)
|
||||||
|
{
|
||||||
|
memcpy(fi->hdrDisplayPrimary, srcFormat.displayPrimary, sizeof(fi->hdrDisplayPrimary));
|
||||||
|
memcpy(fi->hdrWhitePoint , srcFormat.whitePoint , sizeof(fi->hdrWhitePoint));
|
||||||
|
fi->hdrMaxDisplayLuminance = srcFormat.maxDisplayLuminance;
|
||||||
|
fi->hdrMinDisplayLuminance = srcFormat.minDisplayLuminance;
|
||||||
|
fi->hdrMaxContentLightLevel = srcFormat.maxContentLightLevel;
|
||||||
|
fi->hdrMaxFrameAverageLightLevel = srcFormat.maxFrameAverageLightLevel;
|
||||||
|
}
|
||||||
|
|
||||||
fi->damageRectsCount = 0;
|
fi->damageRectsCount = 0;
|
||||||
if (nbDirtyRects <= ARRAYSIZE(fi->damageRects))
|
if (nbDirtyRects <= ARRAYSIZE(fi->damageRects))
|
||||||
{
|
{
|
||||||
@@ -984,6 +1018,59 @@ void CIndirectDeviceContext::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAS_IDDCX_110
|
||||||
|
void CIndirectDeviceContext::SetHDRActive(const struct IDDCX_HDR10_METADATA * hdrMeta)
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(&m_hdrLock);
|
||||||
|
|
||||||
|
if (!hdrMeta)
|
||||||
|
{
|
||||||
|
m_hdrActive = false;
|
||||||
|
ReleaseSRWLockExclusive(&m_hdrLock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hdrActive = true;
|
||||||
|
|
||||||
|
m_hdrDisplayPrimary[0][0] = hdrMeta->RedPrimary [0];
|
||||||
|
m_hdrDisplayPrimary[0][1] = hdrMeta->RedPrimary [1];
|
||||||
|
m_hdrDisplayPrimary[1][0] = hdrMeta->GreenPrimary[0];
|
||||||
|
m_hdrDisplayPrimary[1][1] = hdrMeta->GreenPrimary[1];
|
||||||
|
m_hdrDisplayPrimary[2][0] = hdrMeta->BluePrimary [0];
|
||||||
|
m_hdrDisplayPrimary[2][1] = hdrMeta->BluePrimary [1];
|
||||||
|
m_hdrWhitePoint [0] = hdrMeta->WhitePoint [0];
|
||||||
|
m_hdrWhitePoint [1] = hdrMeta->WhitePoint [1];
|
||||||
|
|
||||||
|
m_hdrMaxDisplayLuminance = hdrMeta->MaxMasteringLuminance;
|
||||||
|
m_hdrMinDisplayLuminance = hdrMeta->MinMasteringLuminance;
|
||||||
|
m_hdrMaxContentLightLevel = hdrMeta->MaxContentLightLevel;
|
||||||
|
m_hdrMaxFrameAverageLightLevel = hdrMeta->MaxFrameAverageLightLevel;
|
||||||
|
|
||||||
|
ReleaseSRWLockExclusive(&m_hdrLock);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool CIndirectDeviceContext::GetHDRMetadata(D12FrameFormat & format) const
|
||||||
|
{
|
||||||
|
AcquireSRWLockShared(&m_hdrLock);
|
||||||
|
|
||||||
|
if (!m_hdrActive)
|
||||||
|
{
|
||||||
|
ReleaseSRWLockShared(&m_hdrLock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(format.displayPrimary, m_hdrDisplayPrimary, sizeof(format.displayPrimary));
|
||||||
|
memcpy(format.whitePoint , m_hdrWhitePoint , sizeof(format.whitePoint ));
|
||||||
|
format.maxDisplayLuminance = m_hdrMaxDisplayLuminance;
|
||||||
|
format.minDisplayLuminance = m_hdrMinDisplayLuminance;
|
||||||
|
format.maxContentLightLevel = m_hdrMaxContentLightLevel;
|
||||||
|
format.maxFrameAverageLightLevel = m_hdrMaxFrameAverageLightLevel;
|
||||||
|
|
||||||
|
ReleaseSRWLockShared(&m_hdrLock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void CIndirectDeviceContext::ResendCursor() const
|
void CIndirectDeviceContext::ResendCursor() const
|
||||||
{
|
{
|
||||||
PLGMPMemory mem = m_pointerShape;
|
PLGMPMemory mem = m_pointerShape;
|
||||||
|
|||||||
@@ -35,6 +35,15 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#include "common/KVMFR.h"
|
#include "common/KVMFR.h"
|
||||||
|
|
||||||
|
// IddCx 1.10 HDR/WCG types are only visible when the WDK targets
|
||||||
|
// (NTDDI >= 0x0A000005) *and* the build flags IDDCX_VERSION_MAJOR/MINOR are set to >= 1.10.
|
||||||
|
#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \
|
||||||
|
(IDDCX_VERSION_MAJOR > 1 || (IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10)) && \
|
||||||
|
NTDDI_VERSION >= 0x0A000005
|
||||||
|
#define HAS_IDDCX_110
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MAX_POINTER_SIZE (sizeof(KVMFRCursor) + (512 * 512 * 4))
|
#define MAX_POINTER_SIZE (sizeof(KVMFRCursor) + (512 * 512 * 4))
|
||||||
#define POINTER_SHAPE_BUFFERS 3
|
#define POINTER_SHAPE_BUFFERS 3
|
||||||
|
|
||||||
@@ -90,6 +99,27 @@ private:
|
|||||||
UINT m_iddCxVersion = 0;
|
UINT m_iddCxVersion = 0;
|
||||||
bool m_canProcessFP16 = false;
|
bool m_canProcessFP16 = false;
|
||||||
|
|
||||||
|
// HDR state from EvtIddCxMonitorSetDefaultHdrMetadata (IddCx 1.10+)
|
||||||
|
// Protected by m_hdrLock - accessed from both the IDD callback thread
|
||||||
|
// (SetHDRActive) and the swap-chain thread (PrepareFrameBuffer, GetHDRMetadata).
|
||||||
|
mutable SRWLOCK m_hdrLock = SRWLOCK_INIT;
|
||||||
|
bool m_hdrActive = false;
|
||||||
|
uint16_t m_hdrDisplayPrimary[3][2] = {};
|
||||||
|
uint16_t m_hdrWhitePoint[2] = {};
|
||||||
|
uint32_t m_hdrMaxDisplayLuminance = 0;
|
||||||
|
uint32_t m_hdrMinDisplayLuminance = 0;
|
||||||
|
uint32_t m_hdrMaxContentLightLevel = 0;
|
||||||
|
uint32_t m_hdrMaxFrameAverageLightLevel = 0;
|
||||||
|
|
||||||
|
// Previous HDR metadata used to detect changes for formatVer bumps
|
||||||
|
uint16_t m_lastHDRDisplayPrimary[3][2] = {};
|
||||||
|
uint16_t m_lastHDRWhitePoint[2] = {};
|
||||||
|
uint32_t m_lastHDRMaxDisplayLuminance = 0;
|
||||||
|
uint32_t m_lastHDRMinDisplayLuminance = 0;
|
||||||
|
uint32_t m_lastHDRMaxContentLightLevel = 0;
|
||||||
|
uint32_t m_lastHDRMaxFrameAverageLightLevel = 0;
|
||||||
|
bool m_lastHDRActive = false;
|
||||||
|
|
||||||
void QueryIddCxCapabilities();
|
void QueryIddCxCapabilities();
|
||||||
bool CanUseIddCx110DDIs() const { return m_canProcessFP16; }
|
bool CanUseIddCx110DDIs() const { return m_canProcessFP16; }
|
||||||
|
|
||||||
@@ -130,8 +160,7 @@ public:
|
|||||||
NTSTATUS MonitorQueryTargetModes(
|
NTSTATUS MonitorQueryTargetModes(
|
||||||
const IDARG_IN_QUERYTARGETMODES* inArgs, IDARG_OUT_QUERYTARGETMODES* outArgs);
|
const IDARG_IN_QUERYTARGETMODES* inArgs, IDARG_OUT_QUERYTARGETMODES* outArgs);
|
||||||
|
|
||||||
#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \
|
#ifdef HAS_IDDCX_110
|
||||||
(IDDCX_VERSION_MAJOR > 1 || (IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10))
|
|
||||||
NTSTATUS ParseMonitorDescription2(
|
NTSTATUS ParseMonitorDescription2(
|
||||||
const IDARG_IN_PARSEMONITORDESCRIPTION2* inArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs);
|
const IDARG_IN_PARSEMONITORDESCRIPTION2* inArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs);
|
||||||
NTSTATUS MonitorQueryTargetModes2(
|
NTSTATUS MonitorQueryTargetModes2(
|
||||||
@@ -156,6 +185,22 @@ public:
|
|||||||
|
|
||||||
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR & info, const BYTE * data);
|
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR & info, const BYTE * data);
|
||||||
|
|
||||||
|
// Tracks HDR state from EvtIddCxMonitorSetDefaultHdrMetadata
|
||||||
|
void SetHDRActive(const struct IDDCX_HDR10_METADATA * hdrMeta);
|
||||||
|
|
||||||
|
// Returns true if the display is currently in HDR mode
|
||||||
|
bool IsHDRActive() const
|
||||||
|
{
|
||||||
|
AcquireSRWLockShared(&m_hdrLock);
|
||||||
|
bool result = m_hdrActive;
|
||||||
|
ReleaseSRWLockShared(&m_hdrLock);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copies current HDR metadata into the provided D12FrameFormat.
|
||||||
|
// Returns true if HDR is active and metadata was copied.
|
||||||
|
bool GetHDRMetadata(D12FrameFormat & format) const;
|
||||||
|
|
||||||
CIVSHMEM &GetIVSHMEM() { return m_ivshmem; }
|
CIVSHMEM &GetIVSHMEM() { return m_ivshmem; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2026 The Looking Glass Authors
|
* Copyright © 2017-2026 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
@@ -32,6 +32,60 @@ int CPlatformInfo::m_cores = 0;
|
|||||||
int CPlatformInfo::m_procs = 0;
|
int CPlatformInfo::m_procs = 0;
|
||||||
int CPlatformInfo::m_sockets = 0;
|
int CPlatformInfo::m_sockets = 0;
|
||||||
|
|
||||||
|
typedef PWSTR (WINAPI *PFN_BRANDING_FORMAT_STRING)(__in PCWSTR pstrFormat);
|
||||||
|
|
||||||
|
inline bool brandingVersionString(std::string &output)
|
||||||
|
{
|
||||||
|
HMODULE hWinBrand = LoadLibraryExA("winbrand.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||||
|
if (!hWinBrand)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PFN_BRANDING_FORMAT_STRING BrandingFormatString =
|
||||||
|
(PFN_BRANDING_FORMAT_STRING) GetProcAddress(hWinBrand, "BrandingFormatString");
|
||||||
|
|
||||||
|
bool succeeded = false;
|
||||||
|
|
||||||
|
if (BrandingFormatString)
|
||||||
|
{
|
||||||
|
PWSTR pwstrOSName = BrandingFormatString(L"%WINDOWS_LONG%");
|
||||||
|
|
||||||
|
if (pwstrOSName)
|
||||||
|
{
|
||||||
|
int cbSize = WideCharToMultiByte(CP_UTF8, 0, pwstrOSName, -1, nullptr, 0, nullptr, nullptr);
|
||||||
|
output.resize(cbSize - 1);
|
||||||
|
|
||||||
|
if (WideCharToMultiByte(CP_UTF8, 0, pwstrOSName, -1, &output[0], cbSize, nullptr, nullptr))
|
||||||
|
succeeded = true;
|
||||||
|
|
||||||
|
GlobalFree((HGLOBAL)pwstrOSName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FreeLibrary(hWinBrand);
|
||||||
|
return succeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool registryVersionString(std::string &output)
|
||||||
|
{
|
||||||
|
DWORD bufferSize = 0;
|
||||||
|
LSTATUS status = RegGetValueA(HKEY_LOCAL_MACHINE,
|
||||||
|
"Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductName",
|
||||||
|
RRF_RT_REG_SZ, nullptr, nullptr, &bufferSize);
|
||||||
|
|
||||||
|
if (status == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
output.resize(bufferSize - 1);
|
||||||
|
status = RegGetValueA(HKEY_LOCAL_MACHINE,
|
||||||
|
"Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductName",
|
||||||
|
RRF_RT_REG_SZ, nullptr, &output[0], &bufferSize);
|
||||||
|
|
||||||
|
if (status == ERROR_SUCCESS)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void CPlatformInfo::Init()
|
void CPlatformInfo::Init()
|
||||||
{
|
{
|
||||||
SYSTEM_INFO si;
|
SYSTEM_INFO si;
|
||||||
@@ -46,26 +100,7 @@ void CPlatformInfo::Init()
|
|||||||
GetVersionExA(&osvi);
|
GetVersionExA(&osvi);
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
|
||||||
DWORD bufferSize = 0;
|
if (!brandingVersionString(m_productName) && !registryVersionString(m_productName))
|
||||||
LSTATUS status = RegGetValueA(HKEY_LOCAL_MACHINE,
|
|
||||||
"Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductName",
|
|
||||||
RRF_RT_REG_SZ, nullptr, nullptr, &bufferSize);
|
|
||||||
if (status == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
m_productName.resize(bufferSize);
|
|
||||||
status = RegGetValueA(HKEY_LOCAL_MACHINE,
|
|
||||||
"Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductName",
|
|
||||||
RRF_RT_REG_SZ, nullptr, &m_productName[0], &bufferSize);
|
|
||||||
|
|
||||||
if (status != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
m_productName = "Unknown";
|
|
||||||
DEBUG_ERROR("Failed to read the ProductName");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_productName.resize(bufferSize - 1); // remove the double null termination
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
m_productName = "Windows " +
|
m_productName = "Windows " +
|
||||||
std::to_string(osvi.dwMajorVersion) + "." +
|
std::to_string(osvi.dwMajorVersion) + "." +
|
||||||
|
|||||||
@@ -41,6 +41,19 @@ struct D12FrameFormat
|
|||||||
FrameType format = FRAME_TYPE_INVALID;
|
FrameType format = FRAME_TYPE_INVALID;
|
||||||
bool hdr = false;
|
bool hdr = false;
|
||||||
bool hdrPQ = false;
|
bool hdrPQ = false;
|
||||||
|
|
||||||
|
// HDR static metadata (SMPTE ST 2086)
|
||||||
|
// Display color primaries in 0.00002 units (xy coordinates)
|
||||||
|
uint16_t displayPrimary[3][2];
|
||||||
|
// White point in 0.00002 units
|
||||||
|
uint16_t whitePoint[2];
|
||||||
|
// Max display luminance in 0.0001 cd/m² units
|
||||||
|
uint32_t maxDisplayLuminance;
|
||||||
|
// Min display luminance in 0.0001 cd/m² units
|
||||||
|
uint32_t minDisplayLuminance;
|
||||||
|
// MaxCLL and MaxFALL in cd/m²
|
||||||
|
uint32_t maxContentLightLevel;
|
||||||
|
uint32_t maxFrameAverageLightLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CPostProcessEffect
|
class CPostProcessEffect
|
||||||
|
|||||||
@@ -145,8 +145,7 @@ void CSwapChainProcessor::SwapChainThreadCore()
|
|||||||
UINT dirtyRectCount = 0;
|
UINT dirtyRectCount = 0;
|
||||||
ComPtr<IDXGIResource> surface;
|
ComPtr<IDXGIResource> surface;
|
||||||
|
|
||||||
#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \
|
#ifdef HAS_IDDCX_110
|
||||||
(IDDCX_VERSION_MAJOR > 1 || (IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10))
|
|
||||||
if (m_devContext->CanProcessFP16())
|
if (m_devContext->CanProcessFP16())
|
||||||
{
|
{
|
||||||
IDARG_IN_RELEASEANDACQUIREBUFFER2 acquireIn = {};
|
IDARG_IN_RELEASEANDACQUIREBUFFER2 acquireIn = {};
|
||||||
@@ -366,8 +365,59 @@ 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.hdr = srcDesc.Format == DXGI_FORMAT_R16G16B16A16_FLOAT;
|
|
||||||
|
// Only HDR-capable formats can carry HDR content.
|
||||||
|
// 8-bit formats (BGRA, RGBA) cannot represent HDR,
|
||||||
|
// even if IsHDRActive() is momentarily stale during mode switches.
|
||||||
|
if (srcDesc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT &&
|
||||||
|
srcDesc.Format != DXGI_FORMAT_R10G10B10A2_UNORM)
|
||||||
|
{
|
||||||
|
srcFormat.hdr = false;
|
||||||
srcFormat.hdrPQ = false;
|
srcFormat.hdrPQ = false;
|
||||||
|
}
|
||||||
|
else if (srcDesc.Format == DXGI_FORMAT_R16G16B16A16_FLOAT)
|
||||||
|
{
|
||||||
|
// FP16 is HDR content (scRGB / linear, not PQ-curve).
|
||||||
|
// FP16 always carries HDR color data regardless of OS HDR mode,
|
||||||
|
// but metadata (primaries, luminances) may be unavailable.
|
||||||
|
srcFormat.hdr = true;
|
||||||
|
srcFormat.hdrPQ = false;
|
||||||
|
if (!m_devContext->GetHDRMetadata(srcFormat))
|
||||||
|
{
|
||||||
|
// No HDR metadata from the OS; provide reasonable defaults
|
||||||
|
// so downstream consumers have valid primaries and luminances.
|
||||||
|
// BT.709/sRGB primaries (in 0.00002 units):
|
||||||
|
srcFormat.displayPrimary[0][0] = 13250; // Rx
|
||||||
|
srcFormat.displayPrimary[0][1] = 34500; // Ry
|
||||||
|
srcFormat.displayPrimary[1][0] = 7500; // Gx
|
||||||
|
srcFormat.displayPrimary[1][1] = 30000; // Gy
|
||||||
|
srcFormat.displayPrimary[2][0] = 34000; // Bx
|
||||||
|
srcFormat.displayPrimary[2][1] = 16000; // By
|
||||||
|
// D65 white point (in 0.00002 units):
|
||||||
|
srcFormat.whitePoint[0] = 15635;
|
||||||
|
srcFormat.whitePoint[1] = 16450;
|
||||||
|
// 80 cd/m² display, 0.005 cd/m² black (in 0.0001 cd/m² units):
|
||||||
|
srcFormat.maxDisplayLuminance = 800000;
|
||||||
|
srcFormat.minDisplayLuminance = 50;
|
||||||
|
// Content light levels unknown:
|
||||||
|
srcFormat.maxContentLightLevel = 0;
|
||||||
|
srcFormat.maxFrameAverageLightLevel = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_devContext->CanProcessFP16() && m_devContext->IsHDRActive())
|
||||||
|
{
|
||||||
|
// Non-FP16 format (e.g., RGBA10) with OS HDR mode active.
|
||||||
|
// Windows applies the PQ (ST.2084) transfer function for non-FP16 HDR.
|
||||||
|
srcFormat.hdr = true;
|
||||||
|
srcFormat.hdrPQ = true;
|
||||||
|
|
||||||
|
// Load HDR metadata; if none is available the frame is not HDR.
|
||||||
|
if (!m_devContext->GetHDRMetadata(srcFormat))
|
||||||
|
{
|
||||||
|
srcFormat.hdr = false;
|
||||||
|
srcFormat.hdrPQ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool postProcessFormatChanged = false;
|
bool postProcessFormatChanged = false;
|
||||||
if (!m_postProcessor.Configure(srcFormat, &postProcessFormatChanged))
|
if (!m_postProcessor.Configure(srcFormat, &postProcessFormatChanged))
|
||||||
@@ -511,8 +561,7 @@ bool CSwapChainProcessor::QueryHWCursor()
|
|||||||
|
|
||||||
IDARG_OUT_QUERY_HWCURSOR out = {};
|
IDARG_OUT_QUERY_HWCURSOR out = {};
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \
|
#ifdef HAS_IDDCX_110
|
||||||
(IDDCX_VERSION_MAJOR > 1 || (IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10))
|
|
||||||
if (m_devContext->CanProcessFP16())
|
if (m_devContext->CanProcessFP16())
|
||||||
{
|
{
|
||||||
IDARG_OUT_QUERY_HWCURSOR3 out3 = {};
|
IDARG_OUT_QUERY_HWCURSOR3 out3 = {};
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ static const UINT IDDCX_VERSION_1_10 = 0x1A00;
|
|||||||
|
|
||||||
static bool LGIddCanUseIddCx110DDIs(UINT iddCxVersion)
|
static bool LGIddCanUseIddCx110DDIs(UINT iddCxVersion)
|
||||||
{
|
{
|
||||||
#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \
|
#ifdef HAS_IDDCX_110
|
||||||
(IDDCX_VERSION_MAJOR > 1 || (IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10))
|
|
||||||
return iddCxVersion >= IDDCX_VERSION_1_10 &&
|
return iddCxVersion >= IDDCX_VERSION_1_10 &&
|
||||||
!!IDD_IS_FUNCTION_AVAILABLE(IddCxSwapChainReleaseAndAcquireBuffer2) &&
|
!!IDD_IS_FUNCTION_AVAILABLE(IddCxSwapChainReleaseAndAcquireBuffer2) &&
|
||||||
!!IDD_IS_FUNCTION_AVAILABLE(IddCxMonitorQueryHardwareCursor3) &&
|
!!IDD_IS_FUNCTION_AVAILABLE(IddCxMonitorQueryHardwareCursor3) &&
|
||||||
@@ -127,8 +126,7 @@ NTSTATUS LGIddMonitorQueryTargetModes(IDDCX_MONITOR monitor, const IDARG_IN_QUER
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \
|
#ifdef HAS_IDDCX_110
|
||||||
(IDDCX_VERSION_MAJOR > 1 || (IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10))
|
|
||||||
NTSTATUS LGIddParseMonitorDescription2(const IDARG_IN_PARSEMONITORDESCRIPTION2* inArgs,
|
NTSTATUS LGIddParseMonitorDescription2(const IDARG_IN_PARSEMONITORDESCRIPTION2* inArgs,
|
||||||
IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs)
|
IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs)
|
||||||
{
|
{
|
||||||
@@ -165,8 +163,11 @@ NTSTATUS LGIddAdapterCommitModes2(IDDCX_ADAPTER adapter, const IDARG_IN_COMMITMO
|
|||||||
NTSTATUS LGIddMonitorSetDefaultHdrMetadata(IDDCX_MONITOR monitor,
|
NTSTATUS LGIddMonitorSetDefaultHdrMetadata(IDDCX_MONITOR monitor,
|
||||||
const IDARG_IN_MONITOR_SET_DEFAULT_HDR_METADATA* inArgs)
|
const IDARG_IN_MONITOR_SET_DEFAULT_HDR_METADATA* inArgs)
|
||||||
{
|
{
|
||||||
UNREFERENCED_PARAMETER(monitor);
|
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
|
||||||
UNREFERENCED_PARAMETER(inArgs);
|
auto* ctx = wrapper->context->GetDeviceContext();
|
||||||
|
|
||||||
|
ctx->SetHDRActive(inArgs->Data.pHdr10);
|
||||||
|
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,8 +229,7 @@ NTSTATUS LGIddCreateDevice(_Inout_ PWDFDEVICE_INIT deviceInit)
|
|||||||
config.EvtIddCxMonitorAssignSwapChain = LGIddMonitorAssignSwapChain;
|
config.EvtIddCxMonitorAssignSwapChain = LGIddMonitorAssignSwapChain;
|
||||||
config.EvtIddCxMonitorUnassignSwapChain = LGIddMonitorUnassignSwapChain;
|
config.EvtIddCxMonitorUnassignSwapChain = LGIddMonitorUnassignSwapChain;
|
||||||
|
|
||||||
#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \
|
#ifdef HAS_IDDCX_110
|
||||||
(IDDCX_VERSION_MAJOR > 1 || (IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10))
|
|
||||||
if (hasIddCx110DDIs)
|
if (hasIddCx110DDIs)
|
||||||
{
|
{
|
||||||
config.EvtIddCxParseMonitorDescription2 = LGIddParseMonitorDescription2;
|
config.EvtIddCxParseMonitorDescription2 = LGIddParseMonitorDescription2;
|
||||||
|
|||||||
@@ -36,14 +36,24 @@ bool CHDR16to10Effect::Init(const ComPtr<ID3D12Device3>& device)
|
|||||||
const char * shader =
|
const char * shader =
|
||||||
"cbuffer Constants : register(b0)\n"
|
"cbuffer Constants : register(b0)\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" float SDRWhiteLevel;\n"
|
" float ReferenceWhiteNits;\n"
|
||||||
"};\n"
|
"};\n"
|
||||||
"Texture2D<float4> src : register(t0);\n"
|
"Texture2D<float4> src : register(t0);\n"
|
||||||
"RWTexture2D<float4> dst : register(u0);\n"
|
"RWTexture2D<float4> dst : register(u0);\n"
|
||||||
|
"static const float PQ_m1 = 0.1593017578125;\n"
|
||||||
|
"static const float PQ_m2 = 78.84375;\n"
|
||||||
|
"static const float PQ_c1 = 0.8359375;\n"
|
||||||
|
"static const float PQ_c2 = 18.8515625;\n"
|
||||||
|
"static const float PQ_c3 = 18.6875;\n"
|
||||||
"[numthreads(" POST_PROCESS_THREADS_STR ", " POST_PROCESS_THREADS_STR ", 1)]\n"
|
"[numthreads(" POST_PROCESS_THREADS_STR ", " POST_PROCESS_THREADS_STR ", 1)]\n"
|
||||||
"void main(uint3 dt : SV_DispatchThreadID)\n"
|
"void main(uint3 dt : SV_DispatchThreadID)\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" dst[dt.xy] = float4(src[dt.xy].rgb * SDRWhiteLevel, src[dt.xy].a);\n"
|
" float3 linearValue = src[dt.xy].rgb * ReferenceWhiteNits;\n"
|
||||||
|
" // scRGB to PQ (ST.2084)\n"
|
||||||
|
" float3 Y = linearValue / 10000.0;\n"
|
||||||
|
" float3 Ym1 = pow(max(Y, 0.0), PQ_m1);\n"
|
||||||
|
" float3 pq = pow((PQ_c1 + PQ_c2 * Ym1) / (1.0 + PQ_c3 * Ym1), PQ_m2);\n"
|
||||||
|
" dst[dt.xy] = float4(pq, src[dt.xy].a);\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
|
|
||||||
if (!InitCompute(device, ranges, ARRAYSIZE(ranges), nullptr, 0, shader))
|
if (!InitCompute(device, ranges, ARRAYSIZE(ranges), nullptr, 0, shader))
|
||||||
@@ -102,7 +112,17 @@ PostProcessStatus CHDR16to10Effect::SetFormat(
|
|||||||
dst.desc = desc;
|
dst.desc = desc;
|
||||||
dst.format = FRAME_TYPE_RGBA10;
|
dst.format = FRAME_TYPE_RGBA10;
|
||||||
dst.hdr = true;
|
dst.hdr = true;
|
||||||
dst.hdrPQ = false;
|
dst.hdrPQ = true;
|
||||||
|
|
||||||
|
// Explicitly propagate HDR static metadata so it survives post-processing.
|
||||||
|
// Do not rely on the caller's copy semantics in CPostProcessor::Configure().
|
||||||
|
memcpy(dst.displayPrimary, src.displayPrimary, sizeof(dst.displayPrimary));
|
||||||
|
memcpy(dst.whitePoint , src.whitePoint , sizeof(dst.whitePoint ));
|
||||||
|
dst.maxDisplayLuminance = src.maxDisplayLuminance;
|
||||||
|
dst.minDisplayLuminance = src.minDisplayLuminance;
|
||||||
|
dst.maxContentLightLevel = src.maxContentLightLevel;
|
||||||
|
dst.maxFrameAverageLightLevel = src.maxFrameAverageLightLevel;
|
||||||
|
|
||||||
return PostProcessStatus::SUCCESS;
|
return PostProcessStatus::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ class CHDR16to10Effect : public CComputeEffect
|
|||||||
private:
|
private:
|
||||||
struct Consts
|
struct Consts
|
||||||
{
|
{
|
||||||
float SDRWhiteLevel;
|
float ReferenceWhiteNits; // scRGB reference white in nits (BT.2408 recommends 203)
|
||||||
} m_consts = { 1.0f };
|
} m_consts = { 203.0f };
|
||||||
ComPtr<ID3D12Resource> m_constBuffer;
|
ComPtr<ID3D12Resource> m_constBuffer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
4
obs/lg.c
4
obs/lg.c
@@ -96,6 +96,7 @@ typedef struct
|
|||||||
uint32_t linesize;
|
uint32_t linesize;
|
||||||
|
|
||||||
bool hideMouse;
|
bool hideMouse;
|
||||||
|
bool hdr;
|
||||||
#if LIBOBS_API_MAJOR_VER >= 27
|
#if LIBOBS_API_MAJOR_VER >= 27
|
||||||
bool dmabuf;
|
bool dmabuf;
|
||||||
bool dmabufTested;
|
bool dmabufTested;
|
||||||
@@ -619,6 +620,7 @@ static void lgFormatInit(LGPlugin * this, const KVMFRFrame * frame,
|
|||||||
|
|
||||||
this->dataWidth = frame->dataWidth;
|
this->dataWidth = frame->dataWidth;
|
||||||
this->unpack = false;
|
this->unpack = false;
|
||||||
|
this->hdr = frame->flags & FRAME_FLAG_HDR;
|
||||||
|
|
||||||
this->bpp = 4;
|
this->bpp = 4;
|
||||||
switch(this->type)
|
switch(this->type)
|
||||||
@@ -643,7 +645,7 @@ static void lgFormatInit(LGPlugin * this, const KVMFRFrame * frame,
|
|||||||
this->format = GS_R10G10B10A2;
|
this->format = GS_R10G10B10A2;
|
||||||
this->drmFormat = DRM_FORMAT_BGRA1010102;
|
this->drmFormat = DRM_FORMAT_BGRA1010102;
|
||||||
#if LIBOBS_API_MAJOR_VER >= 28
|
#if LIBOBS_API_MAJOR_VER >= 28
|
||||||
this->colorSpace = GS_CS_709_SCRGB;
|
this->colorSpace = this->hdr ? GS_CS_709_SCRGB : GS_CS_SRGB;
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user