Merge branch 'fix_layout_color_assert' into 'master'

fix color_range assertion when wlr_color_representation_v1 is used

See merge request wlroots/wlroots!5242
This commit is contained in:
Steve Williams 2026-02-03 18:44:50 +00:00
commit 02d6732563
8 changed files with 136 additions and 27 deletions

View file

@ -63,4 +63,9 @@ enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt);
*/
bool pixel_format_has_alpha(uint32_t fmt);
/**
* Return true if the DRM FourCC fmt belongs to a YCbCr colorspace family, false otherwise.
*/
bool pixel_format_is_ycbcr(uint32_t fmt);
#endif

View file

@ -86,7 +86,6 @@ struct wlr_vk_format {
uint32_t drm;
VkFormat vk;
VkFormat vk_srgb; // sRGB version of the format, or 0 if nonexistent
bool is_ycbcr;
};
extern const VkImageUsageFlags vulkan_render_usage, vulkan_shm_tex_usage, vulkan_dma_tex_usage;
@ -125,6 +124,7 @@ void vulkan_format_props_query(struct wlr_vk_device *dev,
const struct wlr_vk_format_modifier_props *vulkan_format_props_find_modifier(
const struct wlr_vk_format_props *props, uint64_t mod, bool render);
void vulkan_format_props_finish(struct wlr_vk_format_props *props);
bool vulkan_format_is_ycbcr(const struct wlr_vk_format *format);
struct wlr_vk_pipeline_layout_key {
enum wlr_scale_filter_mode filter_mode;

View file

@ -307,3 +307,68 @@ bool pixel_format_has_alpha(uint32_t fmt) {
}
return true;
}
bool pixel_format_is_ycbcr(uint32_t format) {
switch(format) {
case DRM_FORMAT_AYUV:
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV15:
case DRM_FORMAT_NV16:
case DRM_FORMAT_NV20:
case DRM_FORMAT_NV21:
case DRM_FORMAT_NV24:
case DRM_FORMAT_NV30:
case DRM_FORMAT_NV42:
case DRM_FORMAT_NV61:
case DRM_FORMAT_P010:
case DRM_FORMAT_P012:
case DRM_FORMAT_P016:
case DRM_FORMAT_P030:
case DRM_FORMAT_P210:
case DRM_FORMAT_Q401:
case DRM_FORMAT_Q410:
case DRM_FORMAT_S010:
case DRM_FORMAT_S012:
case DRM_FORMAT_S016:
case DRM_FORMAT_S210:
case DRM_FORMAT_S212:
case DRM_FORMAT_S216:
case DRM_FORMAT_S410:
case DRM_FORMAT_S412:
case DRM_FORMAT_S416:
case DRM_FORMAT_UYVY:
case DRM_FORMAT_VUY101010:
case DRM_FORMAT_VUY888:
case DRM_FORMAT_VYUY:
case DRM_FORMAT_X0L0:
case DRM_FORMAT_X0L2:
case DRM_FORMAT_XVYU12_16161616:
case DRM_FORMAT_XVYU16161616:
case DRM_FORMAT_XVYU2101010:
case DRM_FORMAT_XYUV8888:
case DRM_FORMAT_Y0L0:
case DRM_FORMAT_Y0L2:
case DRM_FORMAT_Y210:
case DRM_FORMAT_Y212:
case DRM_FORMAT_Y216:
case DRM_FORMAT_Y410:
case DRM_FORMAT_Y412:
case DRM_FORMAT_Y416:
case DRM_FORMAT_YUV410:
case DRM_FORMAT_YUV411:
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YUV420_10BIT:
case DRM_FORMAT_YUV420_8BIT:
case DRM_FORMAT_YUV422:
case DRM_FORMAT_YUV444:
case DRM_FORMAT_YUYV:
case DRM_FORMAT_YVU410:
case DRM_FORMAT_YVU411:
case DRM_FORMAT_YVU420:
case DRM_FORMAT_YVU422:
case DRM_FORMAT_YVU444:
case DRM_FORMAT_YVYU:
return true;
}
return false;
}

View file

@ -822,12 +822,13 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
}
enum wlr_color_encoding color_encoding = options->color_encoding;
if (texture->format->is_ycbcr && color_encoding == WLR_COLOR_ENCODING_NONE) {
bool is_ycbcr = vulkan_format_is_ycbcr(texture->format);
if (is_ycbcr && color_encoding == WLR_COLOR_ENCODING_NONE) {
color_encoding = WLR_COLOR_ENCODING_BT601;
}
enum wlr_color_range color_range = options->color_range;
if (texture->format->is_ycbcr && color_range == WLR_COLOR_RANGE_NONE) {
if (is_ycbcr && color_range == WLR_COLOR_RANGE_NONE) {
color_range = WLR_COLOR_RANGE_LIMITED;
}
@ -837,7 +838,7 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
.source = WLR_VK_SHADER_SOURCE_TEXTURE,
.layout = {
.ycbcr = {
.format = texture->format->is_ycbcr ? texture->format : NULL,
.format = is_ycbcr ? texture->format : NULL,
.encoding = color_encoding,
.range = color_range,
},

View file

@ -182,37 +182,30 @@ static const struct wlr_vk_format formats[] = {
{
.drm = DRM_FORMAT_UYVY,
.vk = VK_FORMAT_B8G8R8G8_422_UNORM,
.is_ycbcr = true,
},
{
.drm = DRM_FORMAT_YUYV,
.vk = VK_FORMAT_G8B8G8R8_422_UNORM,
.is_ycbcr = true,
},
{
.drm = DRM_FORMAT_NV12,
.vk = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
.is_ycbcr = true,
},
{
.drm = DRM_FORMAT_NV16,
.vk = VK_FORMAT_G8_B8R8_2PLANE_422_UNORM,
.is_ycbcr = true,
},
{
.drm = DRM_FORMAT_YUV420,
.vk = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM,
.is_ycbcr = true,
},
{
.drm = DRM_FORMAT_YUV422,
.vk = VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM,
.is_ycbcr = true,
},
{
.drm = DRM_FORMAT_YUV444,
.vk = VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM,
.is_ycbcr = true,
},
// 3PACK16 formats split the memory in three 16-bit words, so they have an
// inverted channel order compared to DRM formats.
@ -220,27 +213,22 @@ static const struct wlr_vk_format formats[] = {
{
.drm = DRM_FORMAT_P010,
.vk = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16,
.is_ycbcr = true,
},
{
.drm = DRM_FORMAT_P210,
.vk = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16,
.is_ycbcr = true,
},
{
.drm = DRM_FORMAT_P012,
.vk = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16,
.is_ycbcr = true,
},
{
.drm = DRM_FORMAT_P016,
.vk = VK_FORMAT_G16_B16R16_2PLANE_420_UNORM,
.is_ycbcr = true,
},
{
.drm = DRM_FORMAT_Q410,
.vk = VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16,
.is_ycbcr = true,
},
#endif
// TODO: add DRM_FORMAT_NV24/VK_FORMAT_G8_B8R8_2PLANE_444_UNORM (requires
@ -446,7 +434,7 @@ static bool query_modifier_support(struct wlr_vk_device *dev,
// check that specific modifier for render usage
const char *errmsg = "unknown error";
if ((m.drmFormatModifierTilingFeatures & render_features) == render_features &&
!props->format.is_ycbcr) {
!vulkan_format_is_ycbcr(&props->format)) {
struct wlr_vk_format_modifier_props p = {0};
bool supported = false;
if (query_modifier_usage_support(dev, props->format.vk,
@ -477,7 +465,7 @@ static bool query_modifier_support(struct wlr_vk_device *dev,
// check that specific modifier for texture usage
errmsg = "unknown error";
VkFormatFeatureFlags features = dma_tex_features;
if (props->format.is_ycbcr) {
if(vulkan_format_is_ycbcr(&props->format)) {
features |= ycbcr_tex_features;
}
if ((m.drmFormatModifierTilingFeatures & features) == features) {
@ -522,7 +510,7 @@ static bool query_modifier_support(struct wlr_vk_device *dev,
void vulkan_format_props_query(struct wlr_vk_device *dev,
const struct wlr_vk_format *format) {
if (format->is_ycbcr && !dev->sampler_ycbcr_conversion) {
if (vulkan_format_is_ycbcr(format) && !dev->sampler_ycbcr_conversion) {
return;
}
@ -551,7 +539,7 @@ void vulkan_format_props_query(struct wlr_vk_device *dev,
char shm_texture_status[256];
const char *errmsg = "unknown error";
if ((fmtp.formatProperties.optimalTilingFeatures & shm_tex_features) == shm_tex_features &&
!format->is_ycbcr && format_info != NULL) {
!vulkan_format_is_ycbcr(format) && format_info != NULL) {
VkImageFormatProperties ifmtp;
bool supported = false, has_mutable_srgb = false;
if (query_shm_support(dev, format->vk, format->vk_srgb, &ifmtp, &errmsg)) {
@ -621,3 +609,7 @@ const struct wlr_vk_format_modifier_props *vulkan_format_props_find_modifier(
}
return NULL;
}
bool vulkan_format_is_ycbcr(const struct wlr_vk_format *format) {
return pixel_format_is_ycbcr(format->drm);
}

View file

@ -1630,8 +1630,8 @@ static bool init_blend_to_output_layouts(struct wlr_vk_renderer *renderer) {
static bool pipeline_layout_key_equals(
const struct wlr_vk_pipeline_layout_key *a,
const struct wlr_vk_pipeline_layout_key *b) {
assert(!a->ycbcr.format || a->ycbcr.format->is_ycbcr);
assert(!b->ycbcr.format || b->ycbcr.format->is_ycbcr);
assert(!a->ycbcr.format || vulkan_format_is_ycbcr(a->ycbcr.format));
assert(!b->ycbcr.format || vulkan_format_is_ycbcr(b->ycbcr.format));
if (a->filter_mode != b->filter_mode) {
return false;

View file

@ -297,7 +297,7 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text
.components.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.components.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.components.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.components.a = texture->has_alpha || texture->format->is_ycbcr
.components.a = texture->has_alpha || vulkan_format_is_ycbcr(texture->format)
? VK_COMPONENT_SWIZZLE_IDENTITY
: VK_COMPONENT_SWIZZLE_ONE,
.subresourceRange = (VkImageSubresourceRange){
@ -311,7 +311,7 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text
};
VkSamplerYcbcrConversionInfo ycbcr_conversion_info;
if (texture->format->is_ycbcr) {
if (vulkan_format_is_ycbcr(texture->format)) {
assert(pipeline_layout->ycbcr.conversion != VK_NULL_HANDLE);
ycbcr_conversion_info = (VkSamplerYcbcrConversionInfo){
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
@ -355,7 +355,7 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text
static void texture_set_format(struct wlr_vk_texture *texture,
const struct wlr_vk_format *format, bool has_mutable_srgb) {
assert(!(format->is_ycbcr && has_mutable_srgb));
assert(!(vulkan_format_is_ycbcr(format) && has_mutable_srgb));
texture->format = format;
texture->using_mutable_srgb = has_mutable_srgb;
@ -366,7 +366,7 @@ static void texture_set_format(struct wlr_vk_texture *texture,
texture->has_alpha = pixel_format_has_alpha(format->drm);
} else {
// We don't have format info for multi-planar formats
assert(texture->format->is_ycbcr);
assert(vulkan_format_is_ycbcr(texture->format));
}
}
@ -378,7 +378,7 @@ static struct wlr_texture *vulkan_texture_from_pixels(
const struct wlr_vk_format_props *fmt =
vulkan_format_props_from_drm(renderer->dev, drm_fmt);
if (fmt == NULL || fmt->format.is_ycbcr) {
if (fmt == NULL || vulkan_format_is_ycbcr(&fmt->format)) {
char *format_name = drmGetFormatName(drm_fmt);
wlr_log(WLR_ERROR, "Unsupported pixel format %s (0x%08"PRIX32")",
format_name, drm_fmt);

View file

@ -2,12 +2,14 @@
#include <stdlib.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_color_representation_v1.h>
#include <wlr/util/addon.h>
#include <wlr/util/log.h>
#include "color-representation-v1-protocol.h"
#include "render/pixel_format.h"
#include "util/mem.h"
#define WP_COLOR_REPRESENTATION_VERSION 1
@ -230,8 +232,47 @@ static void color_repr_manager_handle_destroy(struct wl_client *client,
wl_resource_destroy(resource);
}
static void surface_synced_commit(struct wlr_surface_synced *synced) {
struct wlr_color_representation_v1 *color_repr = wl_container_of(synced, color_repr, synced);
struct wlr_dmabuf_attributes dmabuf;
if (!color_repr->surface->buffer ||
!wlr_buffer_get_dmabuf(&color_repr->surface->buffer->base, &dmabuf)) {
return;
}
bool is_unset = color_repr->current.coefficients == 0 && color_repr->current.range == 0;
bool is_identity_full = color_repr->current.coefficients == WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_IDENTITY && color_repr->current.range == WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_FULL;
bool is_ycbcr = pixel_format_is_ycbcr(dmabuf.format);
if (is_ycbcr) {
if (is_unset) {
color_repr->current.coefficients = WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT601;
color_repr->current.range = WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_LIMITED;
}
else if(is_identity_full) {
wl_resource_post_error(color_repr->resource,
WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_PIXEL_FORMAT,
"unexpected encoding/range for yuv");
}
} else /* rgb */ {
if(is_identity_full) {
color_repr->current.coefficients = 0;
color_repr->current.range = 0;
is_unset = true;
}
if (!is_unset) {
wl_resource_post_error(color_repr->resource,
WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_PIXEL_FORMAT,
"unexpected encoding/range for rgb");
}
}
}
static const struct wlr_surface_synced_impl surface_synced_impl = {
.state_size = sizeof(struct wlr_color_representation_v1_surface_state),
.commit = surface_synced_commit
};
static struct wlr_color_representation_v1 *color_repr_from_surface(
@ -276,6 +317,7 @@ static void color_repr_manager_handle_get_surface(struct wl_client *client,
}
color_repr->manager = manager_from_resource(manager_resource);
color_repr->surface = surface;
if (!wlr_surface_synced_init(&color_repr->synced, surface,
&surface_synced_impl, &color_repr->pending, &color_repr->current)) {
@ -427,6 +469,10 @@ struct wlr_color_representation_manager_v1 *wlr_color_representation_manager_v1_
struct wlr_color_representation_v1_coeffs_and_range coeffs_and_ranges[COEFFICIENTS_LEN * RANGES_LEN];
size_t coeffs_and_ranges_len = 0;
coeffs_and_ranges[coeffs_and_ranges_len++] = (struct wlr_color_representation_v1_coeffs_and_range){
.coeffs = WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_IDENTITY,
.range = WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_FULL,
};
for (size_t i = 0; i < COEFFICIENTS_LEN; i++) {
enum wp_color_representation_surface_v1_coefficients coeffs = coefficients[i];
enum wlr_color_encoding enc = wlr_color_representation_v1_color_encoding_to_wlr(coeffs);