328 lines
10 KiB
C
328 lines
10 KiB
C
#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) == 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;
|
|
} else {
|
|
vulkan_format_props_finish(&props);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|