mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-07-20 14:22:00 +00:00
wayland: make HDR descriptions asynchronous
Publish HDR metadata through a locked request snapshot and wait for image-description readiness before using it. Commit encoding changes with matching content, preserve the active description during same-format metadata replacements, and handle creation failures without protocol errors.
This commit is contained in:
@@ -105,6 +105,23 @@ void waylandColorMgmtFree(void)
|
||||
if (!wlWm.colorManager)
|
||||
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;
|
||||
}
|
||||
wlWm.hdrImageDescReady = false;
|
||||
if (wlWm.colorSurface)
|
||||
{
|
||||
wp_color_management_surface_v1_destroy(wlWm.colorSurface);
|
||||
wlWm.colorSurface = NULL;
|
||||
}
|
||||
|
||||
wp_color_manager_v1_destroy(wlWm.colorManager);
|
||||
wlWm.colorManager = NULL;
|
||||
}
|
||||
|
||||
@@ -72,31 +72,32 @@ EGLDisplay waylandGetEGLDisplay(void)
|
||||
|
||||
static void applyHDRPending(void)
|
||||
{
|
||||
if (wlWm.pendingHDRApply)
|
||||
enum WaylandHDRPendingAction action;
|
||||
struct WaylandHDRParameters params;
|
||||
|
||||
LG_LOCK(wlWm.pendingHDRLock);
|
||||
action = wlWm.pendingHDRAction;
|
||||
params = wlWm.pendingHDR;
|
||||
wlWm.pendingHDRAction = WAYLAND_HDR_PENDING_NONE;
|
||||
LG_UNLOCK(wlWm.pendingHDRLock);
|
||||
|
||||
if (action == WAYLAND_HDR_PENDING_APPLY)
|
||||
{
|
||||
wlWm.pendingHDRApply = false;
|
||||
atomic_thread_fence(memory_order_acquire);
|
||||
waylandSetHDRImageDescription(
|
||||
wlWm.pendingHDRDisplayPrimary, wlWm.pendingHDRWhitePoint,
|
||||
wlWm.pendingHDRMaxDisplayLuminance,
|
||||
wlWm.pendingHDRMinDisplayLuminance,
|
||||
wlWm.pendingHDRMaxCLL,
|
||||
wlWm.pendingHDRMaxFALL,
|
||||
wlWm.pendingHDRPQ,
|
||||
wlWm.pendingHDRMetadata);
|
||||
params.displayPrimary, params.whitePoint,
|
||||
params.maxDisplayLuminance,
|
||||
params.minDisplayLuminance,
|
||||
params.maxCLL,
|
||||
params.maxFALL,
|
||||
params.pq,
|
||||
params.metadata);
|
||||
}
|
||||
else if (wlWm.pendingHDRClear)
|
||||
{
|
||||
wlWm.pendingHDRClear = false;
|
||||
else if (action == WAYLAND_HDR_PENDING_CLEAR)
|
||||
waylandClearHDRImageDescription();
|
||||
}
|
||||
}
|
||||
|
||||
void waylandClearHDRImageDescription(void)
|
||||
{
|
||||
if (!wlWm.hdrActive)
|
||||
return;
|
||||
|
||||
if (wlWm.hdrImageCreator)
|
||||
{
|
||||
wp_image_description_creator_params_v1_destroy(wlWm.hdrImageCreator);
|
||||
@@ -107,16 +108,103 @@ void waylandClearHDRImageDescription(void)
|
||||
wp_image_description_v1_destroy(wlWm.hdrImageDesc);
|
||||
wlWm.hdrImageDesc = NULL;
|
||||
}
|
||||
if (wlWm.colorSurface)
|
||||
wlWm.hdrImageDescReady = false;
|
||||
|
||||
if (wlWm.colorSurface && atomic_load(&wlWm.hdrActive))
|
||||
wp_color_management_surface_v1_unset_image_description(wlWm.colorSurface);
|
||||
|
||||
atomic_store(&wlWm.hdrActive, false);
|
||||
DEBUG_INFO("HDR image description removed from surface");
|
||||
}
|
||||
|
||||
static void hdrImageDescriptionReady(struct wp_image_description_v1 * desc)
|
||||
{
|
||||
if (desc != wlWm.hdrImageDesc)
|
||||
return;
|
||||
|
||||
if (!wlWm.colorSurface)
|
||||
{
|
||||
wp_color_management_surface_v1_destroy(wlWm.colorSurface);
|
||||
wlWm.colorSurface = NULL;
|
||||
DEBUG_WARN("HDR image description became ready without a color surface");
|
||||
wp_image_description_v1_destroy(desc);
|
||||
wlWm.hdrImageDesc = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
wlWm.hdrActive = false;
|
||||
DEBUG_INFO("HDR image description cleared");
|
||||
// Defer attachment until just after the current surface commit. EGL can
|
||||
// then switch to native HDR for the next render, and that native frame and
|
||||
// its image description become active in the same following commit.
|
||||
wlWm.hdrImageDescReady = true;
|
||||
DEBUG_INFO("HDR image description is ready (%s)",
|
||||
wlWm.hdrImageDescPQ ? "PQ" : "scRGB");
|
||||
app_invalidateWindow(true);
|
||||
waylandStopWaitFrame();
|
||||
}
|
||||
|
||||
static void activateReadyHDRImageDescription(void)
|
||||
{
|
||||
if (!wlWm.hdrImageDesc || !wlWm.hdrImageDescReady || !wlWm.colorSurface)
|
||||
return;
|
||||
|
||||
struct wp_image_description_v1 * desc = wlWm.hdrImageDesc;
|
||||
wp_color_management_surface_v1_set_image_description(
|
||||
wlWm.colorSurface, desc,
|
||||
WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL);
|
||||
atomic_store(&wlWm.hdrActivePQ, wlWm.hdrImageDescPQ);
|
||||
atomic_store(&wlWm.hdrActive, true);
|
||||
|
||||
// set_image_description has copy semantics; the protocol object is no
|
||||
// longer needed once it has been attached to the pending surface state.
|
||||
wlWm.hdrImageDesc = NULL;
|
||||
wlWm.hdrImageDescReady = false;
|
||||
wp_image_description_v1_destroy(desc);
|
||||
|
||||
DEBUG_INFO("HDR image description pending next surface commit (%s)",
|
||||
atomic_load(&wlWm.hdrActivePQ) ? "PQ" : "scRGB");
|
||||
app_invalidateWindow(true);
|
||||
waylandStopWaitFrame();
|
||||
}
|
||||
|
||||
static void hdrImageDescriptionFailed(void * data,
|
||||
struct wp_image_description_v1 * desc, uint32_t cause, const char * message)
|
||||
{
|
||||
(void)data;
|
||||
if (desc != wlWm.hdrImageDesc)
|
||||
return;
|
||||
|
||||
DEBUG_WARN("Failed to create HDR image description (cause:%u): %s",
|
||||
cause, message);
|
||||
wlWm.hdrImageDesc = NULL;
|
||||
wlWm.hdrImageDescReady = false;
|
||||
wp_image_description_v1_destroy(desc);
|
||||
app_invalidateWindow(true);
|
||||
waylandStopWaitFrame();
|
||||
}
|
||||
|
||||
static void hdrImageDescriptionReadyV1(void * data,
|
||||
struct wp_image_description_v1 * desc, uint32_t identity)
|
||||
{
|
||||
(void)data;
|
||||
(void)identity;
|
||||
hdrImageDescriptionReady(desc);
|
||||
}
|
||||
|
||||
static void hdrImageDescriptionReadyV2(void * data,
|
||||
struct wp_image_description_v1 * desc, uint32_t identityHi,
|
||||
uint32_t identityLo)
|
||||
{
|
||||
(void)data;
|
||||
(void)identityHi;
|
||||
(void)identityLo;
|
||||
hdrImageDescriptionReady(desc);
|
||||
}
|
||||
|
||||
static const struct wp_image_description_v1_listener hdrImageDescListener =
|
||||
{
|
||||
.failed = hdrImageDescriptionFailed,
|
||||
.ready = hdrImageDescriptionReadyV1,
|
||||
.ready2 = hdrImageDescriptionReadyV2,
|
||||
};
|
||||
|
||||
void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count)
|
||||
{
|
||||
if (!wlWm.swapWithDamage.init)
|
||||
@@ -133,6 +221,7 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
|
||||
waylandPresentationFrame();
|
||||
applyHDRPending();
|
||||
swapWithDamage(&wlWm.swapWithDamage, display, surface, damage, count);
|
||||
activateReadyHDRImageDescription();
|
||||
|
||||
if (wlWm.needsResize)
|
||||
{
|
||||
@@ -242,7 +331,8 @@ void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2],
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up any previous description (allows re-application with updated metadata)
|
||||
// Cancel only an in-flight replacement. The active surface description is
|
||||
// retained until this replacement is ready.
|
||||
if (wlWm.hdrImageCreator)
|
||||
{
|
||||
wp_image_description_creator_params_v1_destroy(wlWm.hdrImageCreator);
|
||||
@@ -253,13 +343,26 @@ void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2],
|
||||
wp_image_description_v1_destroy(wlWm.hdrImageDesc);
|
||||
wlWm.hdrImageDesc = NULL;
|
||||
}
|
||||
if (wlWm.colorSurface)
|
||||
wlWm.hdrImageDescReady = false;
|
||||
// If the encoding changed, remove the old description in the same surface
|
||||
// commit that presents the software-mapped transition frame.
|
||||
if (atomic_load(&wlWm.hdrActive) &&
|
||||
atomic_load(&wlWm.hdrActivePQ) != hdrPQ)
|
||||
{
|
||||
wp_color_management_surface_v1_destroy(wlWm.colorSurface);
|
||||
wlWm.colorSurface = NULL;
|
||||
wp_color_management_surface_v1_unset_image_description(wlWm.colorSurface);
|
||||
atomic_store(&wlWm.hdrActive, false);
|
||||
}
|
||||
|
||||
wlWm.hdrActive = false;
|
||||
if (!wlWm.colorSurface)
|
||||
{
|
||||
wlWm.colorSurface =
|
||||
wp_color_manager_v1_get_surface(wlWm.colorManager, wlWm.surface);
|
||||
if (!wlWm.colorSurface)
|
||||
{
|
||||
DEBUG_WARN("Failed to get color management surface");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
wlWm.hdrImageCreator =
|
||||
wp_color_manager_v1_create_parametric_creator(wlWm.colorManager);
|
||||
@@ -332,22 +435,12 @@ void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2],
|
||||
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;
|
||||
}
|
||||
wlWm.hdrImageDescPQ = hdrPQ;
|
||||
wlWm.hdrImageDescReady = false;
|
||||
wp_image_description_v1_add_listener(
|
||||
wlWm.hdrImageDesc, &hdrImageDescListener, NULL);
|
||||
|
||||
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, "
|
||||
DEBUG_INFO("HDR image description requested (%s, %s, "
|
||||
"maxLum:%u cd/m² minLum:%u (0.0001 cd/m²) maxCLL:%u maxFALL:%u)",
|
||||
hdrPQ ? "PQ" : "scRGB", hdrPQ ? "BT.2020" : "sRGB",
|
||||
maxDisplayLuminance, minDisplayLuminance, maxCLL, maxFALL);
|
||||
@@ -369,31 +462,31 @@ bool waylandRequestHDR(const uint16_t displayPrimary[3][2],
|
||||
if (!hdrPQ && !wlWm.cmHasPrimariesSRGB)
|
||||
return false;
|
||||
|
||||
wlWm.pendingHDRPQ = hdrPQ;
|
||||
wlWm.pendingHDRMetadata = hdrMetadata;
|
||||
atomic_store(&wlWm.hdrRequestedPQ, hdrPQ);
|
||||
atomic_store(&wlWm.hdrRequested, true);
|
||||
|
||||
// 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;
|
||||
LG_LOCK(wlWm.pendingHDRLock);
|
||||
wlWm.pendingHDR.pq = hdrPQ;
|
||||
wlWm.pendingHDR.metadata = hdrMetadata;
|
||||
memcpy(wlWm.pendingHDR.displayPrimary, displayPrimary,
|
||||
sizeof(wlWm.pendingHDR.displayPrimary));
|
||||
memcpy(wlWm.pendingHDR.whitePoint, whitePoint,
|
||||
sizeof(wlWm.pendingHDR.whitePoint));
|
||||
wlWm.pendingHDR.maxDisplayLuminance = maxDisplayLuminance;
|
||||
wlWm.pendingHDR.minDisplayLuminance = minDisplayLuminance;
|
||||
wlWm.pendingHDR.maxCLL = maxCLL;
|
||||
wlWm.pendingHDR.maxFALL = maxFALL;
|
||||
wlWm.pendingHDRAction = WAYLAND_HDR_PENDING_APPLY;
|
||||
LG_UNLOCK(wlWm.pendingHDRLock);
|
||||
return true;
|
||||
}
|
||||
|
||||
void waylandRequestClearHDR(void)
|
||||
{
|
||||
wlWm.pendingHDRClear = true;
|
||||
wlWm.pendingHDRApply = false;
|
||||
atomic_store(&wlWm.hdrRequested, false);
|
||||
LG_LOCK(wlWm.pendingHDRLock);
|
||||
wlWm.pendingHDRAction = WAYLAND_HDR_PENDING_CLEAR;
|
||||
LG_UNLOCK(wlWm.pendingHDRLock);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
|
||||
@@ -108,6 +108,7 @@ static bool getCompositor(char * dst, size_t size)
|
||||
static bool waylandInit(const LG_DSInitParams params)
|
||||
{
|
||||
memset(&wlWm, 0, sizeof(wlWm));
|
||||
LG_LOCK_INIT(wlWm.pendingHDRLock);
|
||||
wlWm.desktop = WL_Desktops[0];
|
||||
wlWm.hdrWhiteLevels = (LG_DSHDRWhiteLevels)
|
||||
{
|
||||
@@ -211,6 +212,7 @@ static void waylandFree(void)
|
||||
waylandRegistryFree();
|
||||
waylandCursorFree();
|
||||
wl_display_disconnect(wlWm.display);
|
||||
LG_LOCK_FREE(wlWm.pendingHDRLock);
|
||||
}
|
||||
|
||||
static bool waylandGetProp(LG_DSProperty prop, void * ret)
|
||||
@@ -223,7 +225,10 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
|
||||
|
||||
if (prop == LG_DS_NATIVE_HDR)
|
||||
{
|
||||
*(bool *)ret = wlWm.cmCanDoHDR;
|
||||
const bool active = atomic_load(&wlWm.hdrActive);
|
||||
const bool requested = atomic_load(&wlWm.hdrRequested);
|
||||
*(bool *)ret = active && requested &&
|
||||
atomic_load(&wlWm.hdrActivePQ) == atomic_load(&wlWm.hdrRequestedPQ);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,25 @@ struct xkb_context;
|
||||
struct xkb_keymap;
|
||||
struct xkb_state;
|
||||
|
||||
enum WaylandHDRPendingAction
|
||||
{
|
||||
WAYLAND_HDR_PENDING_NONE,
|
||||
WAYLAND_HDR_PENDING_APPLY,
|
||||
WAYLAND_HDR_PENDING_CLEAR,
|
||||
};
|
||||
|
||||
struct WaylandHDRParameters
|
||||
{
|
||||
bool pq;
|
||||
bool metadata;
|
||||
uint16_t displayPrimary[3][2];
|
||||
uint16_t whitePoint[2];
|
||||
uint32_t maxDisplayLuminance;
|
||||
uint32_t minDisplayLuminance;
|
||||
uint32_t maxCLL;
|
||||
uint32_t maxFALL;
|
||||
};
|
||||
|
||||
struct WaylandDSState
|
||||
{
|
||||
bool pointerGrabbed;
|
||||
@@ -189,8 +208,14 @@ struct WaylandDSState
|
||||
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;
|
||||
// Active and requested encodings are atomic because getProp runs on the
|
||||
// renderer while description events are dispatched by Wayland.
|
||||
_Atomic(bool) hdrActive;
|
||||
_Atomic(bool) hdrActivePQ;
|
||||
_Atomic(bool) hdrRequested;
|
||||
_Atomic(bool) hdrRequestedPQ;
|
||||
bool hdrImageDescPQ;
|
||||
bool hdrImageDescReady;
|
||||
|
||||
// wp_color_manager_v1 feature advertisement tracking.
|
||||
// Set to true after the done event for the color-manager has been received
|
||||
@@ -215,19 +240,12 @@ struct WaylandDSState
|
||||
// set by the active desktop backend during shellInit
|
||||
void * xdgToplevel;
|
||||
|
||||
// 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;
|
||||
bool pendingHDRMetadata;
|
||||
uint16_t pendingHDRDisplayPrimary[3][2];
|
||||
uint16_t pendingHDRWhitePoint[2];
|
||||
uint32_t pendingHDRMaxDisplayLuminance;
|
||||
uint32_t pendingHDRMinDisplayLuminance;
|
||||
uint32_t pendingHDRMaxCLL;
|
||||
uint32_t pendingHDRMaxFALL;
|
||||
// Pending HDR format is published by the frame thread and consumed by the
|
||||
// render thread. Keep the action and all metadata in one locked snapshot so
|
||||
// a replacement request cannot tear a request already being consumed.
|
||||
LG_Lock pendingHDRLock;
|
||||
enum WaylandHDRPendingAction pendingHDRAction;
|
||||
struct WaylandHDRParameters pendingHDR;
|
||||
|
||||
LGEvent * frameEvent;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user