mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-02-16 22:05:45 -05:00
render/vulkan: support hardware cursors on nvidia+vulkan
This commit is contained in:
parent
a962d58727
commit
e1c8292c20
5 changed files with 221 additions and 10 deletions
|
|
@ -89,7 +89,7 @@ struct wlr_vk_format {
|
|||
bool is_ycbcr;
|
||||
};
|
||||
|
||||
extern const VkImageUsageFlags vulkan_render_usage, vulkan_shm_tex_usage, vulkan_dma_tex_usage;
|
||||
extern const VkImageUsageFlags vulkan_render_usage, vulkan_render_bridged_usage, vulkan_shm_tex_usage, vulkan_dma_tex_usage;
|
||||
|
||||
// Returns all known format mappings.
|
||||
// Might not be supported for gpu/usecase.
|
||||
|
|
@ -100,6 +100,7 @@ struct wlr_vk_format_modifier_props {
|
|||
VkDrmFormatModifierPropertiesEXT props;
|
||||
VkExtent2D max_extent;
|
||||
bool has_mutable_srgb;
|
||||
bool render_needs_bridge;
|
||||
};
|
||||
|
||||
struct wlr_vk_format_props {
|
||||
|
|
@ -233,6 +234,7 @@ struct wlr_vk_render_buffer {
|
|||
|
||||
VkDeviceMemory memories[WLR_DMABUF_MAX_PLANES];
|
||||
uint32_t mem_count;
|
||||
bool has_bridge;
|
||||
VkImage image;
|
||||
|
||||
// Framebuffer and image view for rendering directly onto the buffer image,
|
||||
|
|
@ -264,6 +266,17 @@ struct wlr_vk_render_buffer {
|
|||
struct wlr_vk_descriptor_pool *blend_attachment_pool;
|
||||
bool blend_transitioned;
|
||||
} two_pass;
|
||||
|
||||
// Sometimes, we want to output to a buffer that cannot be used as a
|
||||
// COLOR_ATTACHMENT, but can be used as a TRANSFER_DST. So, render to an
|
||||
// intermediate bridge buffer, then transfer it to the real output. In
|
||||
// this case, the image field above is the fake intermediate buffer, and
|
||||
// bridge.image is the real output.
|
||||
struct {
|
||||
VkImage image;
|
||||
VkDeviceMemory memory;
|
||||
bool transitioned;
|
||||
} bridge;
|
||||
};
|
||||
|
||||
bool vulkan_setup_one_pass_framebuffer(struct wlr_vk_render_buffer *buffer,
|
||||
|
|
@ -515,7 +528,7 @@ struct wlr_vk_texture *vulkan_get_texture(struct wlr_texture *wlr_texture);
|
|||
VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer,
|
||||
const struct wlr_dmabuf_attributes *attribs,
|
||||
VkDeviceMemory mems[static WLR_DMABUF_MAX_PLANES], uint32_t *n_mems,
|
||||
bool for_render, bool *using_mutable_srgb);
|
||||
bool for_render, bool *render_needs_bridge, bool *using_mutable_srgb);
|
||||
struct wlr_texture *vulkan_texture_from_buffer(
|
||||
struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer);
|
||||
void vulkan_texture_destroy(struct wlr_vk_texture *texture);
|
||||
|
|
|
|||
|
|
@ -319,6 +319,84 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
|
|||
|
||||
vkCmdEndRenderPass(render_cb->vk);
|
||||
|
||||
if (render_buffer->has_bridge) {
|
||||
VkImageLayout bridge_old_layout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
if (!render_buffer->bridge.transitioned) {
|
||||
bridge_old_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
render_buffer->bridge.transitioned = true;
|
||||
}
|
||||
|
||||
VkImageMemoryBarrier image_barrier = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
|
||||
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = render_buffer->image,
|
||||
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.subresourceRange.layerCount = 1,
|
||||
.subresourceRange.levelCount = 1,
|
||||
};
|
||||
vkCmdPipelineBarrier(render_cb->vk, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &image_barrier);
|
||||
|
||||
VkImageMemoryBarrier bridge_barrier = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.oldLayout = bridge_old_layout,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT,
|
||||
.dstQueueFamilyIndex = renderer->dev->queue_family,
|
||||
.image = render_buffer->bridge.image,
|
||||
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.subresourceRange.layerCount = 1,
|
||||
.subresourceRange.levelCount = 1,
|
||||
};
|
||||
vkCmdPipelineBarrier(render_cb->vk, 0, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &bridge_barrier);
|
||||
|
||||
int region_rects_len;
|
||||
const pixman_region32_t *regions = rect_union_evaluate(&pass->updated_region);
|
||||
const pixman_box32_t *clip_rects = pixman_region32_rectangles(regions, ®ion_rects_len);
|
||||
|
||||
VkImageCopy *vkRegions = calloc(region_rects_len, sizeof(VkImageCopy));
|
||||
if (vkRegions == NULL) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
VkImageSubresourceLayers image_subresource_layers = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
};
|
||||
for(int i = 0; i < region_rects_len; i++) {
|
||||
vkRegions[i] = (VkImageCopy){
|
||||
.srcSubresource = image_subresource_layers,
|
||||
.srcOffset.x = clip_rects[i].x1,
|
||||
.srcOffset.y = clip_rects[i].y1,
|
||||
.srcOffset.z = 0,
|
||||
.dstSubresource = image_subresource_layers,
|
||||
.dstOffset.x = clip_rects[i].x1,
|
||||
.dstOffset.y = clip_rects[i].y1,
|
||||
.dstOffset.z = 0,
|
||||
.extent.width = clip_rects[i].x2 - clip_rects[i].x1,
|
||||
.extent.height = clip_rects[i].y2 - clip_rects[i].y1,
|
||||
.extent.depth = 1,
|
||||
};
|
||||
}
|
||||
|
||||
vkCmdCopyImage(render_cb->vk,
|
||||
render_buffer->image, VK_IMAGE_LAYOUT_GENERAL,
|
||||
render_buffer->bridge.image, VK_IMAGE_LAYOUT_GENERAL,
|
||||
region_rects_len, vkRegions);
|
||||
|
||||
free(vkRegions);
|
||||
}
|
||||
|
||||
size_t pass_textures_len = pass->textures.size / sizeof(struct wlr_vk_render_pass_texture);
|
||||
size_t render_wait_cap = pass_textures_len * WLR_DMABUF_MAX_PLANES;
|
||||
render_wait = calloc(render_wait_cap, sizeof(*render_wait));
|
||||
|
|
@ -483,11 +561,12 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
|
|||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcQueueFamilyIndex = renderer->dev->queue_family,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT,
|
||||
.image = render_buffer->image,
|
||||
.image = render_buffer->has_bridge ? render_buffer->bridge.image : render_buffer->image,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.srcAccessMask = render_buffer->has_bridge
|
||||
? VK_ACCESS_TRANSFER_WRITE_BIT
|
||||
: VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = 0, // ignored anyways
|
||||
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.subresourceRange.layerCount = 1,
|
||||
|
|
@ -500,7 +579,12 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
|
|||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
0, 0, NULL, 0, NULL, barrier_count, acquire_barriers);
|
||||
|
||||
vkCmdPipelineBarrier(render_cb->vk, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
||||
VkPipelineStageFlags release_barrier_src_stage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
|
||||
if (render_buffer->has_bridge) {
|
||||
release_barrier_src_stage |= VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
}
|
||||
|
||||
vkCmdPipelineBarrier(render_cb->vk, release_barrier_src_stage,
|
||||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL,
|
||||
barrier_count, release_barriers);
|
||||
|
||||
|
|
|
|||
|
|
@ -231,6 +231,8 @@ const struct wlr_vk_format *vulkan_get_format_from_drm(uint32_t drm_format) {
|
|||
|
||||
const VkImageUsageFlags vulkan_render_usage =
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
const VkImageUsageFlags vulkan_render_bridged_usage =
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
const VkImageUsageFlags vulkan_shm_tex_usage =
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
|
|
@ -242,6 +244,8 @@ const VkImageUsageFlags vulkan_dma_tex_usage =
|
|||
static const VkFormatFeatureFlags render_features =
|
||||
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
|
||||
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT;
|
||||
static const VkFormatFeatureFlags render_bridged_features =
|
||||
VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
|
||||
static const VkFormatFeatureFlags shm_tex_features =
|
||||
VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
|
||||
VK_FORMAT_FEATURE_TRANSFER_DST_BIT |
|
||||
|
|
@ -427,6 +431,43 @@ static bool query_modifier_support(struct wlr_vk_device *dev,
|
|||
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 if ((m.drmFormatModifierTilingFeatures & render_bridged_features) == render_bridged_features &&
|
||||
m.drmFormatModifier == DRM_FORMAT_MOD_LINEAR && !props->format.is_ycbcr) {
|
||||
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_bridged_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_bridged_usage, &m, &p, &errmsg);
|
||||
}
|
||||
|
||||
if (supported) {
|
||||
struct wlr_vk_format_modifier_props bridge_format = {0};
|
||||
supported = query_modifier_usage_support(dev, props->format.vk,
|
||||
0, vulkan_render_usage, &m, &bridge_format, &errmsg);
|
||||
|
||||
if (supported) {
|
||||
if (p.max_extent.width > bridge_format.max_extent.width) {
|
||||
p.max_extent.width = bridge_format.max_extent.width;
|
||||
}
|
||||
if (p.max_extent.height > bridge_format.max_extent.height) {
|
||||
p.max_extent.height = bridge_format.max_extent.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.render_needs_bridge = true;
|
||||
|
||||
if (supported) {
|
||||
props->dmabuf.render_mods[props->dmabuf.render_mod_count++] = p;
|
||||
wlr_drm_format_set_add(&dev->dmabuf_render_formats,
|
||||
|
|
|
|||
|
|
@ -637,6 +637,9 @@ static void destroy_render_buffer(struct wlr_vk_render_buffer *buffer) {
|
|||
buffer->two_pass.blend_descriptor_set);
|
||||
}
|
||||
|
||||
vkDestroyImage(dev, buffer->bridge.image, NULL);
|
||||
vkFreeMemory(dev, buffer->bridge.memory, NULL);
|
||||
|
||||
vkDestroyImage(dev, buffer->image, NULL);
|
||||
for (size_t i = 0u; i < buffer->mem_count; ++i) {
|
||||
vkFreeMemory(dev, buffer->memories[i], NULL);
|
||||
|
|
@ -898,6 +901,65 @@ error:
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool setup_bridge(struct wlr_vk_render_buffer *buffer, const struct wlr_vk_format_props *format) {
|
||||
VkResult res;
|
||||
VkDevice dev = buffer->renderer->dev->dev;
|
||||
|
||||
buffer->bridge.image = buffer->image;
|
||||
buffer->image = 0;
|
||||
|
||||
buffer->has_bridge = true;
|
||||
|
||||
VkImageCreateInfo img_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = format->format.vk,
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.extent = { buffer->wlr_buffer->width, buffer->wlr_buffer->height, 1 },
|
||||
.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
|
||||
};
|
||||
res = vkCreateImage(dev, &img_info, NULL, &buffer->image);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkCreateImage", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
VkMemoryRequirements mem_reqs;
|
||||
vkGetImageMemoryRequirements(dev, buffer->image, &mem_reqs);
|
||||
|
||||
int mem_type = vulkan_find_mem_type(buffer->renderer->dev,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_reqs.memoryTypeBits);
|
||||
if (mem_type < 0) {
|
||||
wlr_log(WLR_ERROR, "failed to find suitable vulkan memory type");
|
||||
return false;
|
||||
}
|
||||
|
||||
VkMemoryAllocateInfo mem_alloc_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
};
|
||||
mem_alloc_info.allocationSize = mem_reqs.size;
|
||||
mem_alloc_info.memoryTypeIndex = mem_type;
|
||||
|
||||
res = vkAllocateMemory(dev, &mem_alloc_info, NULL, &buffer->bridge.memory);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkAllocateMemory", res);
|
||||
return false;
|
||||
}
|
||||
res = vkBindImageMemory(dev, buffer->image, buffer->bridge.memory, 0);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkBindImageMemory", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct wlr_vk_render_buffer *create_render_buffer(
|
||||
struct wlr_vk_renderer *renderer, struct wlr_buffer *wlr_buffer) {
|
||||
struct wlr_vk_render_buffer *buffer = calloc(1, sizeof(*buffer));
|
||||
|
|
@ -920,8 +982,10 @@ static struct wlr_vk_render_buffer *create_render_buffer(
|
|||
(const char*) &dmabuf.format, dmabuf.width, dmabuf.height);
|
||||
|
||||
bool using_mutable_srgb = false;
|
||||
bool render_needs_bridge = false;
|
||||
buffer->image = vulkan_import_dmabuf(renderer, &dmabuf,
|
||||
buffer->memories, &buffer->mem_count, true, &using_mutable_srgb);
|
||||
buffer->memories, &buffer->mem_count, true,
|
||||
&render_needs_bridge, &using_mutable_srgb);
|
||||
if (!buffer->image) {
|
||||
goto error;
|
||||
}
|
||||
|
|
@ -934,6 +998,12 @@ static struct wlr_vk_render_buffer *create_render_buffer(
|
|||
goto error;
|
||||
}
|
||||
|
||||
if (render_needs_bridge) {
|
||||
if (!setup_bridge(buffer, fmt)) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (using_mutable_srgb) {
|
||||
if (!vulkan_setup_one_pass_framebuffer(buffer, &dmabuf, true)) {
|
||||
goto error;
|
||||
|
|
|
|||
|
|
@ -504,7 +504,7 @@ static bool is_dmabuf_disjoint(const struct wlr_dmabuf_attributes *attribs) {
|
|||
VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer,
|
||||
const struct wlr_dmabuf_attributes *attribs,
|
||||
VkDeviceMemory mems[static WLR_DMABUF_MAX_PLANES], uint32_t *n_mems,
|
||||
bool for_render, bool *using_mutable_srgb) {
|
||||
bool for_render, bool *render_needs_bridge, bool *using_mutable_srgb) {
|
||||
VkResult res;
|
||||
VkDevice dev = renderer->dev->dev;
|
||||
*n_mems = 0u;
|
||||
|
|
@ -568,7 +568,7 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer,
|
|||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.extent = (VkExtent3D) { attribs->width, attribs->height, 1 },
|
||||
.usage = for_render ? vulkan_render_usage : vulkan_dma_tex_usage,
|
||||
.usage = for_render ? (mod->render_needs_bridge ? vulkan_render_bridged_usage : vulkan_render_usage) : vulkan_dma_tex_usage,
|
||||
};
|
||||
if (disjoint) {
|
||||
img_info.flags = VK_IMAGE_CREATE_DISJOINT_BIT;
|
||||
|
|
@ -719,6 +719,7 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer,
|
|||
}
|
||||
|
||||
*using_mutable_srgb = mod->has_mutable_srgb;
|
||||
*render_needs_bridge = mod->render_needs_bridge;
|
||||
return image;
|
||||
|
||||
error_image:
|
||||
|
|
@ -751,8 +752,10 @@ static struct wlr_vk_texture *vulkan_texture_from_dmabuf(
|
|||
}
|
||||
|
||||
bool using_mutable_srgb = false;
|
||||
bool render_needs_bridge = false;
|
||||
texture->image = vulkan_import_dmabuf(renderer, attribs,
|
||||
texture->memories, &texture->mem_count, false, &using_mutable_srgb);
|
||||
texture->memories, &texture->mem_count, false,
|
||||
&render_needs_bridge, &using_mutable_srgb);
|
||||
if (!texture->image) {
|
||||
goto error;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue