wlroots/render/vulkan/pixel_format.c
Kenny Levinsen 9a457a3f1d render/vulkan: Use VK_EXT_host_image_copy for shm
When uploading shm buffers to our internal backing texture, we first
allocate suitable chunk from our staging buffer accessible from both CPU
and GPU, and then we queue a GPU-side copy from the staging buffer to
the texture. This is a lot of copying, especially on iGPUs where
everything is system memory anyway.

Instead, use VK_EXT_host_image_copy when available for formats that are
reported as having optimal device access. This allows us to copy
directly to the target texture from CPU, eliminating the queued GPU-side
copy. To keep things simple we keep this texture in GENERAL for the
entire duration for now.
2026-04-17 01:29:20 +02:00

648 lines
19 KiB
C

#include <drm_fourcc.h>
#include <stdio.h>
#include <stdlib.h>
#include <vulkan/vulkan.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#include "render/pixel_format.h"
#include "render/vulkan.h"
static const struct wlr_vk_format formats[] = {
// Vulkan non-packed 8-bits-per-channel formats have an inverted channel
// order compared to the DRM formats, because DRM format channel order
// is little-endian while Vulkan format channel order is in memory byte
// order.
{
.drm = DRM_FORMAT_R8,
.vk = VK_FORMAT_R8_UNORM,
.vk_srgb = VK_FORMAT_R8_SRGB,
},
{
.drm = DRM_FORMAT_R16F,
.vk = VK_FORMAT_R16_SFLOAT,
},
{
.drm = DRM_FORMAT_R32F,
.vk = VK_FORMAT_R32_SFLOAT,
},
{
.drm = DRM_FORMAT_GR88,
.vk = VK_FORMAT_R8G8_UNORM,
.vk_srgb = VK_FORMAT_R8G8_SRGB,
},
{
.drm = DRM_FORMAT_GR1616F,
.vk = VK_FORMAT_R16G16_SFLOAT,
},
{
.drm = DRM_FORMAT_GR3232F,
.vk = VK_FORMAT_R32G32_SFLOAT,
},
{
.drm = DRM_FORMAT_RGB888,
.vk = VK_FORMAT_B8G8R8_UNORM,
.vk_srgb = VK_FORMAT_B8G8R8_SRGB,
},
{
.drm = DRM_FORMAT_BGR888,
.vk = VK_FORMAT_R8G8B8_UNORM,
.vk_srgb = VK_FORMAT_R8G8B8_SRGB,
},
{
.drm = DRM_FORMAT_XRGB8888,
.vk = VK_FORMAT_B8G8R8A8_UNORM,
.vk_srgb = VK_FORMAT_B8G8R8A8_SRGB,
},
{
.drm = DRM_FORMAT_XBGR8888,
.vk = VK_FORMAT_R8G8B8A8_UNORM,
.vk_srgb = VK_FORMAT_R8G8B8A8_SRGB,
},
// The Vulkan _SRGB formats correspond to unpremultiplied alpha, but
// the Wayland protocol specifies premultiplied alpha on electrical values
{
.drm = DRM_FORMAT_ARGB8888,
.vk = VK_FORMAT_B8G8R8A8_UNORM,
},
{
.drm = DRM_FORMAT_ABGR8888,
.vk = VK_FORMAT_R8G8B8A8_UNORM,
},
// Vulkan packed formats have the same channel order as DRM formats on
// little endian systems.
#if WLR_LITTLE_ENDIAN
{
.drm = DRM_FORMAT_RGBA4444,
.vk = VK_FORMAT_R4G4B4A4_UNORM_PACK16,
},
{
.drm = DRM_FORMAT_RGBX4444,
.vk = VK_FORMAT_R4G4B4A4_UNORM_PACK16,
},
{
.drm = DRM_FORMAT_BGRA4444,
.vk = VK_FORMAT_B4G4R4A4_UNORM_PACK16,
},
{
.drm = DRM_FORMAT_BGRX4444,
.vk = VK_FORMAT_B4G4R4A4_UNORM_PACK16,
},
{
.drm = DRM_FORMAT_RGB565,
.vk = VK_FORMAT_R5G6B5_UNORM_PACK16,
},
{
.drm = DRM_FORMAT_BGR565,
.vk = VK_FORMAT_B5G6R5_UNORM_PACK16,
},
{
.drm = DRM_FORMAT_RGBA5551,
.vk = VK_FORMAT_R5G5B5A1_UNORM_PACK16,
},
{
.drm = DRM_FORMAT_RGBX5551,
.vk = VK_FORMAT_R5G5B5A1_UNORM_PACK16,
},
{
.drm = DRM_FORMAT_BGRA5551,
.vk = VK_FORMAT_B5G5R5A1_UNORM_PACK16,
},
{
.drm = DRM_FORMAT_BGRX5551,
.vk = VK_FORMAT_B5G5R5A1_UNORM_PACK16,
},
{
.drm = DRM_FORMAT_ARGB1555,
.vk = VK_FORMAT_A1R5G5B5_UNORM_PACK16,
},
{
.drm = DRM_FORMAT_XRGB1555,
.vk = VK_FORMAT_A1R5G5B5_UNORM_PACK16,
},
{
.drm = DRM_FORMAT_ARGB2101010,
.vk = VK_FORMAT_A2R10G10B10_UNORM_PACK32,
},
{
.drm = DRM_FORMAT_XRGB2101010,
.vk = VK_FORMAT_A2R10G10B10_UNORM_PACK32,
},
{
.drm = DRM_FORMAT_ABGR2101010,
.vk = VK_FORMAT_A2B10G10R10_UNORM_PACK32,
},
{
.drm = DRM_FORMAT_XBGR2101010,
.vk = VK_FORMAT_A2B10G10R10_UNORM_PACK32,
},
#endif
// Vulkan 16-bits-per-channel formats have an inverted channel order
// compared to DRM formats, just like the 8-bits-per-channel ones.
// On little endian systems the memory representation of each channel
// matches the DRM formats'.
#if WLR_LITTLE_ENDIAN
{
.drm = DRM_FORMAT_BGR161616,
.vk = VK_FORMAT_R16G16B16_UNORM,
},
{
.drm = DRM_FORMAT_BGR161616F,
.vk = VK_FORMAT_R16G16B16_SFLOAT,
},
{
.drm = DRM_FORMAT_ABGR16161616,
.vk = VK_FORMAT_R16G16B16A16_UNORM,
},
{
.drm = DRM_FORMAT_XBGR16161616,
.vk = VK_FORMAT_R16G16B16A16_UNORM,
},
{
.drm = DRM_FORMAT_ABGR16161616F,
.vk = VK_FORMAT_R16G16B16A16_SFLOAT,
},
{
.drm = DRM_FORMAT_XBGR16161616F,
.vk = VK_FORMAT_R16G16B16A16_SFLOAT,
},
{
.drm = DRM_FORMAT_BGR323232F,
.vk = VK_FORMAT_R32G32B32_SFLOAT,
},
{
.drm = DRM_FORMAT_ABGR32323232F,
.vk = VK_FORMAT_R32G32B32A32_SFLOAT,
},
#endif
// YCbCr formats
// R -> V, G -> Y, B -> U
// 420 -> 2x2 subsampled, 422 -> 2x1 subsampled, 444 -> non-subsampled
{
.drm = DRM_FORMAT_UYVY,
.vk = VK_FORMAT_B8G8R8G8_422_UNORM,
},
{
.drm = DRM_FORMAT_YUYV,
.vk = VK_FORMAT_G8B8G8R8_422_UNORM,
},
{
.drm = DRM_FORMAT_NV12,
.vk = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
},
{
.drm = DRM_FORMAT_NV16,
.vk = VK_FORMAT_G8_B8R8_2PLANE_422_UNORM,
},
{
.drm = DRM_FORMAT_YUV420,
.vk = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM,
},
{
.drm = DRM_FORMAT_YUV422,
.vk = VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM,
},
{
.drm = DRM_FORMAT_YUV444,
.vk = VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM,
},
// 3PACK16 formats split the memory in three 16-bit words, so they have an
// inverted channel order compared to DRM formats.
#if WLR_LITTLE_ENDIAN
{
.drm = DRM_FORMAT_P010,
.vk = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16,
},
{
.drm = DRM_FORMAT_P210,
.vk = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16,
},
{
.drm = DRM_FORMAT_P012,
.vk = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16,
},
{
.drm = DRM_FORMAT_P016,
.vk = VK_FORMAT_G16_B16R16_2PLANE_420_UNORM,
},
{
.drm = DRM_FORMAT_Q410,
.vk = VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16,
},
#endif
// TODO: add DRM_FORMAT_NV24/VK_FORMAT_G8_B8R8_2PLANE_444_UNORM (requires
// Vulkan 1.3 or VK_EXT_ycbcr_2plane_444_formats)
};
const struct wlr_vk_format *vulkan_get_format_list(size_t *len) {
*len = sizeof(formats) / sizeof(formats[0]);
return formats;
}
const struct wlr_vk_format *vulkan_get_format_from_drm(uint32_t drm_format) {
for (unsigned i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) {
if (formats[i].drm == drm_format) {
return &formats[i];
}
}
return NULL;
}
const VkImageUsageFlags vulkan_render_usage =
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
const VkImageUsageFlags vulkan_shm_tex_usage =
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
const VkImageUsageFlags vulkan_dma_tex_usage =
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
static const VkFormatFeatureFlags render_features =
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT;
static const VkFormatFeatureFlags shm_tex_features =
VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
VK_FORMAT_FEATURE_TRANSFER_DST_BIT |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
// NOTE: we don't strictly require this, we could create a NEAREST
// sampler for formats that need it, in case this ever makes problems.
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
static const VkFormatFeatureFlags dma_tex_features =
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
// NOTE: we don't strictly require this, we could create a NEAREST
// sampler for formats that need it, in case this ever makes problems.
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
static const VkFormatFeatureFlags ycbcr_tex_features =
VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT |
VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT;
// vk_format_variant should be set to 0=VK_FORMAT_UNDEFINED when not used
static bool query_modifier_usage_support(struct wlr_vk_device *dev, VkFormat vk_format,
VkFormat vk_format_variant, VkImageUsageFlags usage,
const VkDrmFormatModifierPropertiesEXT *m,
struct wlr_vk_format_modifier_props *out, const char **errmsg) {
VkResult res;
*errmsg = NULL;
VkFormat view_formats[2] = {
vk_format,
vk_format_variant,
};
VkImageFormatListCreateInfoKHR listi = {
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR,
.pViewFormats = view_formats,
.viewFormatCount = vk_format_variant ? 2 : 1,
};
VkPhysicalDeviceImageDrmFormatModifierInfoEXT modi = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT,
.drmFormatModifier = m->drmFormatModifier,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.pNext = &listi,
};
VkPhysicalDeviceExternalImageFormatInfo efmti = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
.pNext = &modi,
};
VkPhysicalDeviceImageFormatInfo2 fmti = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
.type = VK_IMAGE_TYPE_2D,
.format = vk_format,
.usage = usage,
.flags = vk_format_variant ? VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT : 0,
.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
.pNext = &efmti,
};
VkExternalImageFormatProperties efmtp = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES,
};
VkImageFormatProperties2 ifmtp = {
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2,
.pNext = &efmtp,
};
const VkExternalMemoryProperties *emp = &efmtp.externalMemoryProperties;
res = vkGetPhysicalDeviceImageFormatProperties2(dev->phdev, &fmti, &ifmtp);
if (res != VK_SUCCESS) {
if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) {
*errmsg = "unsupported format";
} else {
wlr_vk_error("vkGetPhysicalDeviceImageFormatProperties2", res);
*errmsg = "failed to get format properties";
}
return false;
} else if (!(emp->externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT)) {
*errmsg = "import not supported";
return false;
}
VkExtent3D me = ifmtp.imageFormatProperties.maxExtent;
*out = (struct wlr_vk_format_modifier_props){
.props = *m,
.max_extent.width = me.width,
.max_extent.height = me.height,
};
return true;
}
static bool query_shm_support(struct wlr_vk_device *dev, VkFormat vk_format,
VkFormat vk_format_variant, VkImageUsageFlags usage,
VkImageFormatProperties *out, bool *out_host_copy_optimal,
const char **errmsg) {
VkResult res;
*errmsg = NULL;
VkFormat view_formats[2] = {
vk_format,
vk_format_variant,
};
VkImageFormatListCreateInfoKHR listi = {
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR,
.pViewFormats = view_formats,
.viewFormatCount = vk_format_variant ? 2 : 1,
.pNext = NULL,
};
VkPhysicalDeviceImageFormatInfo2 fmti = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
.type = VK_IMAGE_TYPE_2D,
.format = vk_format,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = usage,
.flags = vk_format_variant ? VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT : 0,
.pNext = &listi,
};
VkHostImageCopyDevicePerformanceQueryEXT perf_query = {
.sType = VK_STRUCTURE_TYPE_HOST_IMAGE_COPY_DEVICE_PERFORMANCE_QUERY_EXT,
};
VkImageFormatProperties2 ifmtp = {
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2,
.pNext = out_host_copy_optimal ? &perf_query : NULL,
};
res = vkGetPhysicalDeviceImageFormatProperties2(dev->phdev, &fmti, &ifmtp);
if (res != VK_SUCCESS) {
if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) {
*errmsg = "unsupported format";
} else {
wlr_vk_error("vkGetPhysicalDeviceImageFormatProperties2", res);
*errmsg = "failed to get format properties";
}
return false;
}
*out = ifmtp.imageFormatProperties;
if (out_host_copy_optimal) {
*out_host_copy_optimal = perf_query.optimalDeviceAccess;
}
return true;
}
static bool query_modifier_support(struct wlr_vk_device *dev,
struct wlr_vk_format_props *props, size_t modifier_count) {
VkDrmFormatModifierPropertiesListEXT modp = {
.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT,
.drmFormatModifierCount = modifier_count,
};
VkFormatProperties2 fmtp = {
.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2,
.pNext = &modp,
};
modp.pDrmFormatModifierProperties =
calloc(modifier_count, sizeof(*modp.pDrmFormatModifierProperties));
if (!modp.pDrmFormatModifierProperties) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return false;
}
vkGetPhysicalDeviceFormatProperties2(dev->phdev, props->format.vk, &fmtp);
props->dmabuf.render_mods =
calloc(modp.drmFormatModifierCount, sizeof(*props->dmabuf.render_mods));
props->dmabuf.texture_mods =
calloc(modp.drmFormatModifierCount, sizeof(*props->dmabuf.texture_mods));
if (!props->dmabuf.render_mods || !props->dmabuf.texture_mods) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
free(modp.pDrmFormatModifierProperties);
free(props->dmabuf.render_mods);
free(props->dmabuf.texture_mods);
props->dmabuf.render_mods = NULL;
props->dmabuf.texture_mods = NULL;
return false;
}
bool found = false;
for (uint32_t i = 0; i < modp.drmFormatModifierCount; ++i) {
VkDrmFormatModifierPropertiesEXT m = modp.pDrmFormatModifierProperties[i];
char render_status[256], texture_status[256];
// check that specific modifier for render usage
const char *errmsg = "unknown error";
if ((m.drmFormatModifierTilingFeatures & render_features) == render_features &&
!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,
props->format.vk_srgb, vulkan_render_usage, &m, &p, &errmsg)) {
supported = true;
p.has_mutable_srgb = props->format.vk_srgb != 0;
}
if (!supported && props->format.vk_srgb) {
supported = query_modifier_usage_support(dev, props->format.vk,
0, vulkan_render_usage, &m, &p, &errmsg);
}
if (supported) {
props->dmabuf.render_mods[props->dmabuf.render_mod_count++] = p;
wlr_drm_format_set_add(&dev->dmabuf_render_formats,
props->format.drm, m.drmFormatModifier);
found = true;
}
} else {
errmsg = "missing required features";
}
if (errmsg != NULL) {
snprintf(render_status, sizeof(render_status), "✗ render (%s)", errmsg);
} else {
snprintf(render_status, sizeof(render_status), "✓ render");
}
// check that specific modifier for texture usage
errmsg = "unknown error";
VkFormatFeatureFlags features = dma_tex_features;
if (vulkan_format_is_ycbcr(&props->format)) {
features |= ycbcr_tex_features;
}
if ((m.drmFormatModifierTilingFeatures & features) == features) {
struct wlr_vk_format_modifier_props p = {0};
bool supported = false;
if (query_modifier_usage_support(dev, props->format.vk,
props->format.vk_srgb, vulkan_dma_tex_usage, &m, &p, &errmsg)) {
supported = true;
p.has_mutable_srgb = props->format.vk_srgb != 0;
}
if (!supported && props->format.vk_srgb) {
supported = query_modifier_usage_support(dev, props->format.vk,
0, vulkan_dma_tex_usage, &m, &p, &errmsg);
}
if (supported) {
props->dmabuf.texture_mods[props->dmabuf.texture_mod_count++] = p;
wlr_drm_format_set_add(&dev->dmabuf_texture_formats,
props->format.drm, m.drmFormatModifier);
found = true;
}
} else {
errmsg = "missing required features";
}
if (errmsg != NULL) {
snprintf(texture_status, sizeof(texture_status), "✗ texture (%s)", errmsg);
} else {
snprintf(texture_status, sizeof(texture_status), "✓ texture");
}
char *modifier_name = drmGetFormatModifierName(m.drmFormatModifier);
wlr_log(WLR_DEBUG, " DMA-BUF modifier %s "
"(0x%016"PRIX64", %"PRIu32" planes): %s %s",
modifier_name ? modifier_name : "<unknown>", m.drmFormatModifier,
m.drmFormatModifierPlaneCount, texture_status, render_status);
free(modifier_name);
}
free(modp.pDrmFormatModifierProperties);
return found;
}
void vulkan_format_props_query(struct wlr_vk_device *dev,
const struct wlr_vk_format *format) {
if (vulkan_format_is_ycbcr(format) && !dev->sampler_ycbcr_conversion) {
return;
}
char *format_name = drmGetFormatName(format->drm);
wlr_log(WLR_DEBUG, " %s (0x%08"PRIX32")",
format_name ? format_name : "<unknown>", format->drm);
free(format_name);
VkDrmFormatModifierPropertiesListEXT modp = {
.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT,
};
VkFormatProperties3 fmtp3 = {
.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3,
.pNext = &modp,
};
VkFormatProperties2 fmtp = {
.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2,
.pNext = dev->host_image_copy ? (void *)&fmtp3 : (void *)&modp,
};
vkGetPhysicalDeviceFormatProperties2(dev->phdev, format->vk, &fmtp);
bool add_fmt_props = false;
struct wlr_vk_format_props props = {0};
props.format = *format;
const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(format->drm);
// shm texture properties
char shm_texture_status[256];
const char *errmsg = "unknown error";
if ((fmtp.formatProperties.optimalTilingFeatures & shm_tex_features) == shm_tex_features &&
!vulkan_format_is_ycbcr(format) && format_info != NULL) {
VkImageFormatProperties ifmtp;
bool supported = false, has_mutable_srgb = false;
bool host_copy_optimal = false;
if (dev->host_image_copy &&
(fmtp3.optimalTilingFeatures & VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT)) {
VkImageUsageFlags usage = vulkan_shm_tex_usage |
VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT;
if (query_shm_support(dev, format->vk, format->vk_srgb, usage, &ifmtp,
&host_copy_optimal, &errmsg)) {
has_mutable_srgb = format->vk_srgb != 0;
supported = true;
} else if (format->vk_srgb && query_shm_support(dev, format->vk, 0, usage, &ifmtp,
&host_copy_optimal, &errmsg)) {
supported = true;
}
if (!host_copy_optimal) {
has_mutable_srgb = false;
supported = false;
}
}
if (!supported && query_shm_support(dev, format->vk, format->vk_srgb,
vulkan_shm_tex_usage, &ifmtp, NULL, &errmsg)) {
supported = true;
has_mutable_srgb = format->vk_srgb != 0;
}
if (!supported && format->vk_srgb) {
supported = query_shm_support(dev, format->vk, 0,
vulkan_shm_tex_usage, &ifmtp, NULL, &errmsg);
}
if (supported) {
props.shm.max_extent.width = ifmtp.maxExtent.width;
props.shm.max_extent.height = ifmtp.maxExtent.height;
props.shm.features = fmtp.formatProperties.optimalTilingFeatures;
props.shm.has_mutable_srgb = has_mutable_srgb;
props.shm.host_image_copy = host_copy_optimal;
wlr_drm_format_set_add(&dev->shm_texture_formats,
format->drm, DRM_FORMAT_MOD_LINEAR);
add_fmt_props = true;
}
} else {
errmsg = "missing required features";
}
if (errmsg != NULL) {
snprintf(shm_texture_status, sizeof(shm_texture_status), "✗ texture (%s)", errmsg);
} else {
snprintf(shm_texture_status, sizeof(shm_texture_status), "✓ texture");
}
wlr_log(WLR_DEBUG, " Shared memory: %s", shm_texture_status);
if (modp.drmFormatModifierCount > 0) {
add_fmt_props |= query_modifier_support(dev, &props,
modp.drmFormatModifierCount);
}
if (add_fmt_props) {
dev->format_props[dev->format_prop_count] = props;
++dev->format_prop_count;
} else {
vulkan_format_props_finish(&props);
}
}
void vulkan_format_props_finish(struct wlr_vk_format_props *props) {
free(props->dmabuf.texture_mods);
free(props->dmabuf.render_mods);
}
const struct wlr_vk_format_modifier_props *vulkan_format_props_find_modifier(
const struct wlr_vk_format_props *props, uint64_t mod, bool render) {
uint32_t len;
const struct wlr_vk_format_modifier_props *mods;
if (render) {
len = props->dmabuf.render_mod_count;
mods = props->dmabuf.render_mods;
} else {
len = props->dmabuf.texture_mod_count;
mods = props->dmabuf.texture_mods;
}
for (uint32_t i = 0; i < len; ++i) {
if (mods[i].props.drmFormatModifier == mod) {
return &mods[i];
}
}
return NULL;
}
bool vulkan_format_is_ycbcr(const struct wlr_vk_format *format) {
return pixel_format_is_ycbcr(format->drm);
}