diff options
author | nyorain <nyorain@gmail.com> | 2021-02-21 18:30:12 +0100 |
---|---|---|
committer | Simon Ser <contact@emersion.fr> | 2021-10-18 11:51:13 +0200 |
commit | 8e346922508aa3eaccd6e12f2917f6574f349843 (patch) | |
tree | 550742b8a086287b6d478db1ade14b2cc4a21294 /render/vulkan/pixel_format.c | |
parent | 2edf468aeb7d4703aa211cea3b58f04cbc73298c (diff) |
render/vulkan: add Vulkan renderer
This new renderer is implemented with the existing wlr_renderer API
(which is known to be sub-optimal for some operations). It's not
used by default, but users can opt-in by setting WLR_RENDERER=vulkan.
The renderer depends on VK_EXT_image_drm_format_modifier and
VK_EXT_physical_device_drm.
Co-authored-by: Simon Ser <contact@emersion.fr>
Co-authored-by: Jan Beich <jbeich@FreeBSD.org>
Diffstat (limited to 'render/vulkan/pixel_format.c')
-rw-r--r-- | render/vulkan/pixel_format.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/render/vulkan/pixel_format.c b/render/vulkan/pixel_format.c new file mode 100644 index 00000000..cfab5ffe --- /dev/null +++ b/render/vulkan/pixel_format.c @@ -0,0 +1,325 @@ +#include <drm_fourcc.h> +#include <stdlib.h> +#include <vulkan/vulkan.h> +#include <wlr/util/log.h> +#include "render/vulkan.h" + +// Reversed endianess of shm and vulkan format names +static const struct wlr_vk_format formats[] = { + { + .drm_format = DRM_FORMAT_ARGB8888, + .vk_format = VK_FORMAT_B8G8R8A8_SRGB, + }, + { + .drm_format = DRM_FORMAT_XRGB8888, + .vk_format = VK_FORMAT_B8G8R8A8_SRGB, + }, + { + .drm_format = DRM_FORMAT_XBGR8888, + .vk_format = VK_FORMAT_R8G8B8A8_SRGB, + }, + { + .drm_format = DRM_FORMAT_ABGR8888, + .vk_format = VK_FORMAT_R8G8B8A8_SRGB, + }, +}; + +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_format == drm_format) { + return &formats[i]; + } + } + return NULL; +} + +static const VkImageUsageFlags render_usage = + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; +static const VkImageUsageFlags tex_usage = + VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT; +static const VkImageUsageFlags dma_tex_usage = + VK_IMAGE_USAGE_SAMPLED_BIT; + +static const VkFormatFeatureFlags 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 render_features = + VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | + VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_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 bool query_modifier_support(struct wlr_vk_device *dev, + struct wlr_vk_format_props *props, size_t modifier_count, + VkPhysicalDeviceImageFormatInfo2 fmti) { + VkResult res; + + VkFormatProperties2 fmtp = {0}; + fmtp.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + VkDrmFormatModifierPropertiesListEXT modp = {0}; + modp.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT; + modp.drmFormatModifierCount = modifier_count; + fmtp.pNext = &modp; + + // the first call to vkGetPhysicalDeviceFormatProperties2 did only + // retrieve the number of modifiers, we now have to retrieve + // the modifiers + 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_format, &fmtp); + + props->render_mods = calloc(modp.drmFormatModifierCount, + sizeof(*props->render_mods)); + if (!props->render_mods) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + free(modp.pDrmFormatModifierProperties); + return false; + } + + props->texture_mods = calloc(modp.drmFormatModifierCount, + sizeof(*props->texture_mods)); + if (!props->texture_mods) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + free(modp.pDrmFormatModifierProperties); + free(props->render_mods); + return false; + } + + // detailed check + // format info + // only added if dmabuf/drm_fmt_ext supported + VkPhysicalDeviceExternalImageFormatInfo efmti = {0}; + efmti.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO; + efmti.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; + + fmti.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; + fmti.pNext = &efmti; + + VkPhysicalDeviceImageDrmFormatModifierInfoEXT modi = {0}; + modi.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT; + modi.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + efmti.pNext = &modi; + + // format properties + VkExternalImageFormatProperties efmtp = {0}; + efmtp.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES; + + VkImageFormatProperties2 ifmtp = {0}; + ifmtp.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2; + ifmtp.pNext = &efmtp; + const VkExternalMemoryProperties *emp = &efmtp.externalMemoryProperties; + + bool found = false; + + for (unsigned i = 0u; i < modp.drmFormatModifierCount; ++i) { + VkDrmFormatModifierPropertiesEXT m = + modp.pDrmFormatModifierProperties[i]; + wlr_log(WLR_DEBUG, " modifier: 0x%"PRIx64 ": features 0x%"PRIx32", %d planes", + m.drmFormatModifier, m.drmFormatModifierTilingFeatures, + m.drmFormatModifierPlaneCount); + + // check that specific modifier for render usage + if ((m.drmFormatModifierTilingFeatures & render_features) == render_features) { + fmti.usage = render_usage; + + modi.drmFormatModifier = m.drmFormatModifier; + res = vkGetPhysicalDeviceImageFormatProperties2( + dev->phdev, &fmti, &ifmtp); + if (res != VK_SUCCESS) { + if (res != VK_ERROR_FORMAT_NOT_SUPPORTED) { + wlr_vk_error("vkGetPhysicalDeviceImageFormatProperties2", + res); + } + + wlr_log(WLR_DEBUG, " >> rendering: format not supported"); + } else if (emp->externalMemoryFeatures & + VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT) { + unsigned c = props->render_mod_count; + VkExtent3D me = ifmtp.imageFormatProperties.maxExtent; + VkExternalMemoryProperties emp = efmtp.externalMemoryProperties; + props->render_mods[c].props = m; + props->render_mods[c].max_extent.width = me.width; + props->render_mods[c].max_extent.height = me.height; + props->render_mods[c].dmabuf_flags = emp.externalMemoryFeatures; + props->render_mods[c].export_imported = + (emp.exportFromImportedHandleTypes & + VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT); + ++props->render_mod_count; + + found = true; + wlr_drm_format_set_add(&dev->dmabuf_render_formats, + props->format.drm_format, m.drmFormatModifier); + + wlr_log(WLR_DEBUG, " >> rendering: supported"); + } else { + wlr_log(WLR_DEBUG, " >> rendering: importing not supported"); + } + } else { + wlr_log(WLR_DEBUG, " >> rendering: format features not supported"); + } + + // check that specific modifier for texture usage + if ((m.drmFormatModifierTilingFeatures & dma_tex_features) == dma_tex_features) { + fmti.usage = dma_tex_usage; + + modi.drmFormatModifier = m.drmFormatModifier; + res = vkGetPhysicalDeviceImageFormatProperties2( + dev->phdev, &fmti, &ifmtp); + if (res != VK_SUCCESS) { + if (res != VK_ERROR_FORMAT_NOT_SUPPORTED) { + wlr_vk_error("vkGetPhysicalDeviceImageFormatProperties2", + res); + } + + wlr_log(WLR_DEBUG, " >> dmatex: format not supported"); + } else if (emp->externalMemoryFeatures & + VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT) { + unsigned c = props->texture_mod_count; + VkExtent3D me = ifmtp.imageFormatProperties.maxExtent; + VkExternalMemoryProperties emp = efmtp.externalMemoryProperties; + props->texture_mods[c].props = m; + props->texture_mods[c].max_extent.width = me.width; + props->texture_mods[c].max_extent.height = me.height; + props->texture_mods[c].dmabuf_flags = emp.externalMemoryFeatures; + props->texture_mods[c].export_imported = + (emp.exportFromImportedHandleTypes & + VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT); + ++props->texture_mod_count; + + found = true; + wlr_drm_format_set_add(&dev->dmabuf_texture_formats, + props->format.drm_format, m.drmFormatModifier); + + wlr_log(WLR_DEBUG, " >> dmatex: supported"); + } else { + wlr_log(WLR_DEBUG, " >> dmatex: importing not supported"); + } + } else { + wlr_log(WLR_DEBUG, " >> dmatex: format features not supported"); + } + } + + free(modp.pDrmFormatModifierProperties); + return found; +} + +void vulkan_format_props_query(struct wlr_vk_device *dev, + const struct wlr_vk_format *format) { + + wlr_log(WLR_DEBUG, "vulkan: Checking support for format %.4s (0x%" PRIx32 ")", + (const char *)&format->drm_format, format->drm_format); + VkResult res; + + // get general features and modifiers + VkFormatProperties2 fmtp = {0}; + fmtp.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + VkDrmFormatModifierPropertiesListEXT modp = {0}; + modp.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT; + fmtp.pNext = &modp; + + vkGetPhysicalDeviceFormatProperties2(dev->phdev, + format->vk_format, &fmtp); + + // detailed check + VkPhysicalDeviceImageFormatInfo2 fmti = {0}; + fmti.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2; + fmti.type = VK_IMAGE_TYPE_2D; + fmti.format = format->vk_format; + + VkImageFormatProperties2 ifmtp = {0}; + ifmtp.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2; + + bool add_fmt_props = false; + struct wlr_vk_format_props props = {0}; + props.format = *format; + + wlr_log(WLR_DEBUG, " drmFormatModifierCount: %d", modp.drmFormatModifierCount); + if (modp.drmFormatModifierCount > 0) { + add_fmt_props |= query_modifier_support(dev, &props, + modp.drmFormatModifierCount, fmti); + } + + // non-dmabuf texture properties + if (fmtp.formatProperties.optimalTilingFeatures & tex_features) { + fmti.pNext = NULL; + ifmtp.pNext = NULL; + fmti.tiling = VK_IMAGE_TILING_OPTIMAL; + fmti.usage = tex_usage; + + res = vkGetPhysicalDeviceImageFormatProperties2( + dev->phdev, &fmti, &ifmtp); + if (res != VK_SUCCESS) { + if (res != VK_ERROR_FORMAT_NOT_SUPPORTED) { + wlr_vk_error("vkGetPhysicalDeviceImageFormatProperties2", + res); + } + + wlr_log(WLR_DEBUG, " >> shmtex: format not supported"); + } else { + VkExtent3D me = ifmtp.imageFormatProperties.maxExtent; + props.max_extent.width = me.width; + props.max_extent.height = me.height; + props.features = fmtp.formatProperties.optimalTilingFeatures; + + wlr_log(WLR_DEBUG, " >> shmtex: supported"); + + dev->shm_formats[dev->shm_format_count] = format->drm_format; + ++dev->shm_format_count; + + add_fmt_props = true; + } + } else { + wlr_log(WLR_DEBUG, " >> shmtex: format features not supported"); + } + + if (add_fmt_props) { + dev->format_props[dev->format_prop_count] = props; + ++dev->format_prop_count; + } +} + +void vulkan_format_props_finish(struct wlr_vk_format_props *props) { + free(props->texture_mods); + free(props->render_mods); +} + +struct wlr_vk_format_modifier_props *vulkan_format_props_find_modifier( + struct wlr_vk_format_props *props, uint64_t mod, bool render) { + if (render) { + for (unsigned i = 0u; i < props->render_mod_count; ++i) { + if (props->render_mods[i].props.drmFormatModifier == mod) { + return &props->render_mods[i]; + } + } + } else { + for (unsigned i = 0u; i < props->texture_mod_count; ++i) { + if (props->texture_mods[i].props.drmFormatModifier == mod) { + return &props->texture_mods[i]; + } + } + } + + return NULL; +} + |