mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-07-16 04:12:03 +00:00
[client] wayland: implement HDR support
This commit is contained in:
@@ -13,6 +13,7 @@ find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
|
||||
add_library(displayserver_Wayland STATIC
|
||||
activation.c
|
||||
clipboard.c
|
||||
colormgmt.c
|
||||
cursor.c
|
||||
gl.c
|
||||
idle.c
|
||||
|
||||
105
client/displayservers/Wayland/colormgmt.c
Normal file
105
client/displayservers/Wayland/colormgmt.c
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 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 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 = NULL,
|
||||
.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);
|
||||
wl_display_roundtrip(wlWm.display);
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!wlWm.swapWithDamage.init)
|
||||
@@ -84,6 +130,7 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
|
||||
}
|
||||
|
||||
waylandPresentationFrame();
|
||||
applyHDRPending();
|
||||
swapWithDamage(&wlWm.swapWithDamage, display, surface, damage, count);
|
||||
|
||||
if (wlWm.needsResize)
|
||||
@@ -149,23 +196,226 @@ EGLNativeWindowType waylandGetEGLNativeWindow(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
// HDR color management support
|
||||
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)
|
||||
{
|
||||
if (!wlWm.colorManager)
|
||||
return;
|
||||
|
||||
if (!wlWm.cmFeaturesDone)
|
||||
{
|
||||
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 (values already in 0.0001 cd/m² units)
|
||||
wp_image_description_creator_params_v1_set_luminances(
|
||||
wlWm.hdrImageCreator,
|
||||
minDisplayLuminance > 0 ? minDisplayLuminance : 50,
|
||||
maxDisplayLuminance > 0 ? maxDisplayLuminance : 10000000,
|
||||
hdrPQ ? 2030000 : 800000);
|
||||
|
||||
// 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]);
|
||||
|
||||
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_RENDERABLE_TYPE , EGL_OPENGL_BIT,
|
||||
EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
|
||||
EGL_SAMPLE_BUFFERS , 0,
|
||||
EGL_SAMPLES , 0,
|
||||
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)
|
||||
{
|
||||
EGLint attr[] =
|
||||
{
|
||||
EGL_BUFFER_SIZE , 24,
|
||||
EGL_CONFORMANT , EGL_OPENGL_BIT,
|
||||
EGL_RENDERABLE_TYPE , EGL_OPENGL_BIT,
|
||||
EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
|
||||
EGL_RED_SIZE , 8,
|
||||
EGL_GREEN_SIZE , 8,
|
||||
EGL_BLUE_SIZE , 8,
|
||||
EGL_SAMPLE_BUFFERS , 0,
|
||||
EGL_SAMPLES , 0,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
wlWm.glDisplay = waylandGetEGLDisplay();
|
||||
|
||||
int maj, min;
|
||||
@@ -181,13 +431,31 @@ bool waylandOpenGLInit(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
EGLint num_config;
|
||||
if (!eglChooseConfig(wlWm.glDisplay, attr, &wlWm.glConfig, 1, &num_config))
|
||||
EGLConfig 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;
|
||||
}
|
||||
|
||||
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);
|
||||
if (wlWm.glSurface == EGL_NO_SURFACE)
|
||||
{
|
||||
|
||||
@@ -66,6 +66,9 @@ wayland_generate(
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/staging/content-type/content-type-v1.xml"
|
||||
"${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
|
||||
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))
|
||||
wlWm.contentTypeManager = wl_registry_bind(wlWm.registry, name,
|
||||
&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(
|
||||
data, registry, name, interface, version))
|
||||
return;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/option.h"
|
||||
#include "interface/renderer.h"
|
||||
|
||||
#include "dynamic/wayland_desktops.h"
|
||||
|
||||
@@ -143,6 +144,9 @@ static bool waylandInit(const LG_DSInitParams params)
|
||||
if (!waylandRegistryInit())
|
||||
return false;
|
||||
|
||||
if (!waylandColorMgmtInit())
|
||||
return false;
|
||||
|
||||
if (!waylandActivationInit())
|
||||
return false;
|
||||
|
||||
@@ -189,6 +193,7 @@ static void waylandFree(void)
|
||||
waylandPresentationFree();
|
||||
waylandInputFree();
|
||||
waylandOutputFree();
|
||||
waylandColorMgmtFree();
|
||||
waylandRegistryFree();
|
||||
waylandCursorFree();
|
||||
wl_display_disconnect(wlWm.display);
|
||||
@@ -202,6 +207,12 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (prop == LG_DS_NATIVE_HDR)
|
||||
{
|
||||
*(bool *)ret = wlWm.cmCanDoHDR;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -227,6 +238,22 @@ static void waylandMinimize(void)
|
||||
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 =
|
||||
{
|
||||
.name = "Wayland",
|
||||
@@ -276,6 +303,8 @@ struct LG_DisplayServerOps LGDS_Wayland =
|
||||
.getFullscreen = waylandGetFullscreen,
|
||||
.minimize = waylandMinimize,
|
||||
|
||||
.setHDRImageDescription = waylandHDRCallback,
|
||||
|
||||
.cbInit = waylandCBInit,
|
||||
.cbNotice = waylandCBNotice,
|
||||
.cbRelease = waylandCBRelease,
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "wayland-xdg-activation-v1-client-protocol.h"
|
||||
#include "wayland-fractional-scale-v1-client-protocol.h"
|
||||
#include "wayland-content-type-v1-client-protocol.h"
|
||||
#include "wayland-color-management-v1-client-protocol.h"
|
||||
|
||||
#include "scale.h"
|
||||
|
||||
@@ -120,7 +121,8 @@ struct WaylandDSState
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
EGLDisplay glDisplay;
|
||||
EGLConfig glConfig;
|
||||
EGLConfig glConfig; // primary config (best bit depth probed)
|
||||
EGLConfig glConfigSDR; // fallback 8-bit SDR config
|
||||
EGLSurface glSurface;
|
||||
#endif
|
||||
|
||||
@@ -180,6 +182,41 @@ struct WaylandDSState
|
||||
struct wp_content_type_manager_v1 * contentTypeManager;
|
||||
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 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;
|
||||
|
||||
struct wl_list poll; // WaylandPoll::link
|
||||
@@ -262,6 +299,24 @@ void waylandGLSetSwapInterval(int interval);
|
||||
void waylandGLSwapBuffers(void);
|
||||
#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
|
||||
bool waylandIdleInit(void);
|
||||
void waylandIdleFree(void);
|
||||
|
||||
Reference in New Issue
Block a user