feat: hdr support

This commit is contained in:
DreamMaoMao 2026-06-24 07:52:20 +08:00
parent 085d65c219
commit 5fb6b230a0
8 changed files with 211 additions and 63 deletions

View file

@ -1,6 +1,7 @@
#include "dwl-ipc.h"
#include "ext-workspace.h"
#include "foreign-toplevel.h"
#include "hdr.h"
#include "tablet.h"
#include "tearing.h"
#include "text-input.h"

128
src/ext-protocol/hdr.h Normal file
View file

@ -0,0 +1,128 @@
#include <drm_fourcc.h>
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
static uint32_t output_formats_8bit[] = {
DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_RGBX8888,
DRM_FORMAT_BGRX8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_RGB888,
DRM_FORMAT_BGR888,
};
static uint32_t output_formats_10bit[] = {
DRM_FORMAT_XRGB2101010, DRM_FORMAT_XBGR2101010, DRM_FORMAT_RGBX1010102,
DRM_FORMAT_BGRX1010102, DRM_FORMAT_ARGB2101010, DRM_FORMAT_ABGR2101010,
DRM_FORMAT_RGBA1010102, DRM_FORMAT_BGRA1010102,
};
static bool output_set_render_format(Monitor *m, uint32_t candidates[],
size_t count) {
for (size_t i = 0; i < count; i++) {
wlr_output_state_set_render_format(&m->pending, candidates[i]);
if (wlr_output_test_state(m->wlr_output, &m->pending))
return true;
}
return false;
}
static bool output_format_in_candidates(uint32_t format, uint32_t candidates[],
size_t count) {
for (size_t i = 0; i < count; i++)
if (candidates[i] == format)
return true;
return false;
}
static enum render_bit_depth bit_depth_from_format(uint32_t render_format) {
if (output_format_in_candidates(render_format, output_formats_10bit,
ARRAY_SIZE(output_formats_10bit)))
return MANGO_RENDER_BIT_DEPTH_10;
if (output_format_in_candidates(render_format, output_formats_8bit,
ARRAY_SIZE(output_formats_8bit)))
return MANGO_RENDER_BIT_DEPTH_8;
return MANGO_RENDER_BIT_DEPTH_DEFAULT;
}
static bool output_supports_hdr(const struct wlr_output *output,
const char **reason) {
const char *r = NULL;
if (!(output->supported_primaries & WLR_COLOR_NAMED_PRIMARIES_BT2020))
r = "BT2020 primaries not supported";
else if (!(output->supported_transfer_functions &
WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ))
r = "PQ transfer function not supported";
else if (!drw->features.output_color_transform)
r = "renderer doesn't support output color transforms";
if (reason)
*reason = r;
return !r;
}
void output_enable_hdr(Monitor *m, struct wlr_output_state *os, bool enabled,
bool silent) {
if (enabled && !output_supports_hdr(m->wlr_output, NULL))
enabled = false;
if (!enabled) {
if (m->wlr_output->supported_primaries ||
m->wlr_output->supported_transfer_functions) {
if (!silent)
wlr_log(WLR_DEBUG, "Disabling HDR on output %s",
m->wlr_output->name);
wlr_output_state_set_image_description(os, NULL);
}
return;
}
if (!silent)
wlr_log(WLR_DEBUG, "Enabling HDR on output %s", m->wlr_output->name);
struct wlr_output_image_description desc = {
.primaries = WLR_COLOR_NAMED_PRIMARIES_BT2020,
.transfer_function = WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ,
};
wlr_output_state_set_image_description(os, &desc);
}
void output_state_setup_hdr(Monitor *m, bool silent) {
uint32_t render_format = m->wlr_output->render_format;
const char *unsupported_reason = NULL;
bool hdr_supported =
output_supports_hdr(m->wlr_output, &unsupported_reason);
bool hdr_succeeded = false;
enum render_bit_depth depth = config.hdr_depth;
if (depth == MANGO_RENDER_BIT_DEPTH_DEFAULT)
depth = bit_depth_from_format(render_format);
if (!hdr_supported && depth == MANGO_RENDER_BIT_DEPTH_10) {
if (!silent)
wlr_log(WLR_INFO, "Cannot enable HDR on output %s: %s",
m->wlr_output->name, unsupported_reason);
depth = MANGO_RENDER_BIT_DEPTH_8;
}
if (depth == MANGO_RENDER_BIT_DEPTH_10 &&
bit_depth_from_format(render_format) == depth) {
hdr_succeeded = true; // 上次已经成功设置10位直接复用
} else if (depth == MANGO_RENDER_BIT_DEPTH_10) {
hdr_succeeded = output_set_render_format(
m, output_formats_10bit, ARRAY_SIZE(output_formats_10bit));
if (!hdr_succeeded) {
if (!silent)
wlr_log(WLR_INFO,
"No 10 bit color formats supported, HDR disabled.");
if (!output_set_render_format(m, output_formats_8bit,
ARRAY_SIZE(output_formats_8bit)))
if (!silent)
wlr_log(WLR_ERROR, "No 8 bit color formats either!");
}
} else {
// 明确要求8位或自动降级
if (!output_set_render_format(m, output_formats_8bit,
ARRAY_SIZE(output_formats_8bit)))
if (!silent)
wlr_log(WLR_ERROR, "No 8 bit color formats supported!");
}
output_enable_hdr(m, &m->pending, hdr_succeeded, silent);
}

View file

@ -100,73 +100,44 @@ bool custom_wlr_scene_output_commit(struct wlr_scene_output *scene_output,
struct wlr_output *wlr_output = scene_output->output;
Monitor *m = wlr_output->data;
// 检查是否需要帧
if (!wlr_scene_output_needs_frame(scene_output)) {
wlr_log(WLR_DEBUG, "No frame needed for output %s", wlr_output->name);
if (!wlr_scene_output_needs_frame(scene_output))
return true;
}
// 构建输出状态
if (!wlr_scene_output_build_state(scene_output, state, NULL)) {
wlr_log(WLR_ERROR, "Failed to build output state for %s",
wlr_output->name);
// 构建状态,将场景的 Buffer 附着到 state 上
if (!wlr_scene_output_build_state(scene_output, state, NULL))
return false;
// 测试是否支持撕裂
if (!wlr_output_test_state(wlr_output, state)) {
// 如果 DRM 拒绝(例如当前输出/驱动不支持撕裂),降级关闭撕裂
state->tearing_page_flip = false;
}
// 测试撕裂翻页
if (state->tearing_page_flip) {
if (!wlr_output_test_state(wlr_output, state)) {
state->tearing_page_flip = false;
}
}
// 尝试提交
// 提交状态
bool committed = wlr_output_commit_state(wlr_output, state);
// 如果启用撕裂翻页但提交失败,重试禁用撕裂翻页
if (!committed && state->tearing_page_flip) {
wlr_log(WLR_DEBUG, "Retrying commit without tearing for %s",
wlr_output->name);
// 重试一次
state->tearing_page_flip = false;
committed = wlr_output_commit_state(wlr_output, state);
}
// 处理状态清理
if (committed) {
wlr_log(WLR_DEBUG, "Successfully committed output %s",
wlr_output->name);
if (state == &m->pending) {
wlr_output_state_finish(&m->pending);
wlr_output_state_init(&m->pending);
}
} else {
wlr_log(WLR_ERROR, "Failed to commit output %s", wlr_output->name);
// 即使提交失败,也清理状态避免积累
if (state == &m->pending) {
wlr_output_state_finish(&m->pending);
wlr_output_state_init(&m->pending);
}
return false;
}
return true;
return committed;
}
void apply_tear_state(Monitor *m) {
if (wlr_scene_output_needs_frame(m->scene_output)) {
wlr_output_state_init(&m->pending);
if (wlr_scene_output_build_state(m->scene_output, &m->pending, NULL)) {
struct wlr_output_state *pending = &m->pending;
pending->tearing_page_flip = true;
if (!wlr_scene_output_needs_frame(m->scene_output))
return;
if (!custom_wlr_scene_output_commit(m->scene_output, pending)) {
wlr_log(WLR_ERROR, "Failed to commit output %s",
m->scene_output->output->name);
}
} else {
wlr_log(WLR_ERROR, "Failed to build state for output %s",
m->scene_output->output->name);
wlr_output_state_finish(&m->pending);
}
wlr_output_state_init(&m->pending);
m->pending.tearing_page_flip = true;
if (!custom_wlr_scene_output_commit(m->scene_output, &m->pending)) {
wlr_log(WLR_ERROR, "Failed to commit output %s",
m->scene_output->output->name);
}
}