mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-07-21 14:52:04 +00:00
[client] wayland: harden native HDR colour state
Serialize asynchronous image-description state and publish compositor capabilities safely across the Wayland and render threads. Use the predefined Windows-scRGB description, query each output image description for its configured reference white, and reapply HDR when the surface moves to an output with different colour settings.
This commit is contained in:
@@ -21,12 +21,264 @@
|
||||
#include "wayland.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "app.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
struct OutputColorDescription
|
||||
{
|
||||
struct WaylandOutput * output;
|
||||
struct wp_image_description_v1 * description;
|
||||
struct wp_image_description_info_v1 * info;
|
||||
};
|
||||
|
||||
static void outputColorDescriptionFree(struct OutputColorDescription * desc)
|
||||
{
|
||||
if (!desc)
|
||||
return;
|
||||
|
||||
if (desc->info)
|
||||
wl_proxy_destroy((struct wl_proxy *)desc->info);
|
||||
if (desc->description)
|
||||
wp_image_description_v1_destroy(desc->description);
|
||||
if (desc->output && desc->output->colorDescription == desc)
|
||||
desc->output->colorDescription = NULL;
|
||||
free(desc);
|
||||
}
|
||||
|
||||
static struct WaylandOutput * outputFind(struct wl_output * output)
|
||||
{
|
||||
struct WaylandOutput * node;
|
||||
wl_list_for_each(node, &wlWm.outputs, link)
|
||||
if (node->output == output)
|
||||
return node;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void waylandOutputUpdateHDRWhiteLevel(void)
|
||||
{
|
||||
uint32_t pqWhite = 203;
|
||||
uint32_t scRGBWhite = 80;
|
||||
struct SurfaceOutput * surfaceOutput;
|
||||
|
||||
wl_list_for_each(surfaceOutput, &wlWm.surfaceOutputs, link)
|
||||
{
|
||||
struct WaylandOutput * output = outputFind(surfaceOutput->output);
|
||||
if (!output || !output->referenceWhiteValid)
|
||||
continue;
|
||||
|
||||
pqWhite = scRGBWhite = output->referenceWhiteLevel;
|
||||
break;
|
||||
}
|
||||
|
||||
const uint32_t oldPQ = atomic_exchange(&wlWm.hdrPQWhiteLevel, pqWhite);
|
||||
const uint32_t oldScRGB =
|
||||
atomic_exchange(&wlWm.hdrScRGBWhiteLevel, scRGBWhite);
|
||||
if ((oldPQ != pqWhite || oldScRGB != scRGBWhite) && wlWm.frameEvent)
|
||||
{
|
||||
DEBUG_INFO("Wayland output reference white: %u cd/m²", pqWhite);
|
||||
if (atomic_load(&wlWm.hdrRequested))
|
||||
{
|
||||
LG_LOCK(wlWm.pendingHDRLock);
|
||||
wlWm.pendingHDRAction = WAYLAND_HDR_PENDING_APPLY;
|
||||
LG_UNLOCK(wlWm.pendingHDRLock);
|
||||
}
|
||||
app_invalidateWindow(true);
|
||||
waylandStopWaitFrame();
|
||||
}
|
||||
}
|
||||
|
||||
static void outputInfoDone(void * data,
|
||||
struct wp_image_description_info_v1 * info)
|
||||
{
|
||||
struct OutputColorDescription * desc = data;
|
||||
desc->info = NULL; // the protocol destroys it after this event
|
||||
outputColorDescriptionFree(desc);
|
||||
}
|
||||
|
||||
static void outputInfoICCFile(void * data,
|
||||
struct wp_image_description_info_v1 * info, int32_t fd, uint32_t size)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void outputInfoPrimaries(void * data,
|
||||
struct wp_image_description_info_v1 * info,
|
||||
int32_t rx, int32_t ry, int32_t gx, int32_t gy,
|
||||
int32_t bx, int32_t by, int32_t wx, int32_t wy)
|
||||
{
|
||||
}
|
||||
|
||||
static void outputInfoPrimariesNamed(void * data,
|
||||
struct wp_image_description_info_v1 * info, uint32_t primaries)
|
||||
{
|
||||
}
|
||||
|
||||
static void outputInfoTFPower(void * data,
|
||||
struct wp_image_description_info_v1 * info, uint32_t exponent)
|
||||
{
|
||||
}
|
||||
|
||||
static void outputInfoTFNamed(void * data,
|
||||
struct wp_image_description_info_v1 * info, uint32_t tf)
|
||||
{
|
||||
}
|
||||
|
||||
static void outputInfoLuminances(void * data,
|
||||
struct wp_image_description_info_v1 * info,
|
||||
uint32_t minLum, uint32_t maxLum, uint32_t referenceLum)
|
||||
{
|
||||
struct OutputColorDescription * desc = data;
|
||||
if (!referenceLum || desc->output->colorDescription != desc)
|
||||
return;
|
||||
|
||||
desc->output->referenceWhiteLevel = referenceLum;
|
||||
desc->output->referenceWhiteValid = true;
|
||||
waylandOutputUpdateHDRWhiteLevel();
|
||||
}
|
||||
|
||||
static void outputInfoTargetPrimaries(void * data,
|
||||
struct wp_image_description_info_v1 * info,
|
||||
int32_t rx, int32_t ry, int32_t gx, int32_t gy,
|
||||
int32_t bx, int32_t by, int32_t wx, int32_t wy)
|
||||
{
|
||||
}
|
||||
|
||||
static void outputInfoTargetLuminance(void * data,
|
||||
struct wp_image_description_info_v1 * info,
|
||||
uint32_t minLum, uint32_t maxLum)
|
||||
{
|
||||
}
|
||||
|
||||
static void outputInfoTargetMaxCLL(void * data,
|
||||
struct wp_image_description_info_v1 * info, uint32_t maxCLL)
|
||||
{
|
||||
}
|
||||
|
||||
static void outputInfoTargetMaxFALL(void * data,
|
||||
struct wp_image_description_info_v1 * info, uint32_t maxFALL)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wp_image_description_info_v1_listener outputInfoListener =
|
||||
{
|
||||
.done = outputInfoDone,
|
||||
.icc_file = outputInfoICCFile,
|
||||
.primaries = outputInfoPrimaries,
|
||||
.primaries_named = outputInfoPrimariesNamed,
|
||||
.tf_power = outputInfoTFPower,
|
||||
.tf_named = outputInfoTFNamed,
|
||||
.luminances = outputInfoLuminances,
|
||||
.target_primaries = outputInfoTargetPrimaries,
|
||||
.target_luminance = outputInfoTargetLuminance,
|
||||
.target_max_cll = outputInfoTargetMaxCLL,
|
||||
.target_max_fall = outputInfoTargetMaxFALL,
|
||||
};
|
||||
|
||||
static void outputDescriptionFailed(void * data,
|
||||
struct wp_image_description_v1 * description,
|
||||
uint32_t cause, const char * message)
|
||||
{
|
||||
struct OutputColorDescription * desc = data;
|
||||
DEBUG_WARN("Failed to query Wayland output colour description "
|
||||
"(cause:%u): %s", cause, message);
|
||||
outputColorDescriptionFree(desc);
|
||||
}
|
||||
|
||||
static void outputDescriptionReady(struct OutputColorDescription * desc)
|
||||
{
|
||||
desc->info = wp_image_description_v1_get_information(desc->description);
|
||||
if (!desc->info)
|
||||
{
|
||||
outputColorDescriptionFree(desc);
|
||||
return;
|
||||
}
|
||||
wp_image_description_info_v1_add_listener(
|
||||
desc->info, &outputInfoListener, desc);
|
||||
}
|
||||
|
||||
static void outputDescriptionReadyV1(void * data,
|
||||
struct wp_image_description_v1 * description, uint32_t identity)
|
||||
{
|
||||
outputDescriptionReady(data);
|
||||
}
|
||||
|
||||
static void outputDescriptionReadyV2(void * data,
|
||||
struct wp_image_description_v1 * description,
|
||||
uint32_t identityHi, uint32_t identityLo)
|
||||
{
|
||||
outputDescriptionReady(data);
|
||||
}
|
||||
|
||||
static const struct wp_image_description_v1_listener outputDescriptionListener =
|
||||
{
|
||||
.failed = outputDescriptionFailed,
|
||||
.ready = outputDescriptionReadyV1,
|
||||
.ready2 = outputDescriptionReadyV2,
|
||||
};
|
||||
|
||||
static void outputRequestColorDescription(struct WaylandOutput * output)
|
||||
{
|
||||
outputColorDescriptionFree(output->colorDescription);
|
||||
output->referenceWhiteValid = false;
|
||||
waylandOutputUpdateHDRWhiteLevel();
|
||||
|
||||
struct OutputColorDescription * desc = calloc(1, sizeof(*desc));
|
||||
if (!desc)
|
||||
return;
|
||||
|
||||
desc->output = output;
|
||||
desc->description =
|
||||
wp_color_management_output_v1_get_image_description(output->colorOutput);
|
||||
if (!desc->description)
|
||||
{
|
||||
free(desc);
|
||||
return;
|
||||
}
|
||||
|
||||
output->colorDescription = desc;
|
||||
wp_image_description_v1_add_listener(
|
||||
desc->description, &outputDescriptionListener, desc);
|
||||
}
|
||||
|
||||
static void outputImageDescriptionChanged(void * data,
|
||||
struct wp_color_management_output_v1 * colorOutput)
|
||||
{
|
||||
outputRequestColorDescription(data);
|
||||
}
|
||||
|
||||
static const struct wp_color_management_output_v1_listener colorOutputListener =
|
||||
{
|
||||
.image_description_changed = outputImageDescriptionChanged,
|
||||
};
|
||||
|
||||
void waylandOutputColorMgmtInit(struct WaylandOutput * output)
|
||||
{
|
||||
if (!wlWm.colorManager || output->colorOutput)
|
||||
return;
|
||||
|
||||
output->colorOutput =
|
||||
wp_color_manager_v1_get_output(wlWm.colorManager, output->output);
|
||||
if (!output->colorOutput)
|
||||
return;
|
||||
|
||||
wp_color_management_output_v1_add_listener(
|
||||
output->colorOutput, &colorOutputListener, output);
|
||||
outputRequestColorDescription(output);
|
||||
}
|
||||
|
||||
void waylandOutputColorMgmtInitAll(void)
|
||||
{
|
||||
struct WaylandOutput * output;
|
||||
wl_list_for_each(output, &wlWm.outputs, link)
|
||||
waylandOutputColorMgmtInit(output);
|
||||
}
|
||||
|
||||
static void outputUpdateScale(struct WaylandOutput * node)
|
||||
{
|
||||
struct WaylandScale original = node->scale;
|
||||
@@ -144,6 +396,9 @@ void waylandOutputFree(void)
|
||||
struct WaylandOutput * temp;
|
||||
wl_list_for_each_safe(node, temp, &wlWm.outputs, link)
|
||||
{
|
||||
outputColorDescriptionFree(node->colorDescription);
|
||||
if (node->colorOutput)
|
||||
wp_color_management_output_v1_destroy(node->colorOutput);
|
||||
if (node->version >= 3)
|
||||
wl_output_release(node->output);
|
||||
if (node->xdgOutput)
|
||||
@@ -188,6 +443,7 @@ void waylandOutputBind(uint32_t name, uint32_t version)
|
||||
|
||||
wl_output_add_listener(node->output, &outputListener, node);
|
||||
wl_list_insert(&wlWm.outputs, &node->link);
|
||||
waylandOutputColorMgmtInit(node);
|
||||
}
|
||||
|
||||
void waylandOutputTryUnbind(uint32_t name)
|
||||
@@ -198,12 +454,27 @@ void waylandOutputTryUnbind(uint32_t name)
|
||||
{
|
||||
if (node->name == name)
|
||||
{
|
||||
struct SurfaceOutput * surfaceOutput;
|
||||
struct SurfaceOutput * temp;
|
||||
wl_list_for_each_safe(
|
||||
surfaceOutput, temp, &wlWm.surfaceOutputs, link)
|
||||
if (surfaceOutput->output == node->output)
|
||||
{
|
||||
wl_list_remove(&surfaceOutput->link);
|
||||
free(surfaceOutput);
|
||||
}
|
||||
|
||||
outputColorDescriptionFree(node->colorDescription);
|
||||
if (node->colorOutput)
|
||||
wp_color_management_output_v1_destroy(node->colorOutput);
|
||||
if (node->version >= 3)
|
||||
wl_output_release(node->output);
|
||||
if (node->xdgOutput)
|
||||
zxdg_output_v1_destroy(node->xdgOutput);
|
||||
wl_list_remove(&node->link);
|
||||
free(node);
|
||||
waylandWindowUpdateScale();
|
||||
waylandOutputUpdateHDRWhiteLevel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user