mirror of
https://github.com/hyprwm/wlroots-hyprland.git
synced 2024-11-29 16:05:59 +01:00
98cf38601f
This lets the renderer handle the wlr_buffer directly, just like it does in texture_from_buffer. This also allows the renderer to batch the rectangle updates, and update more than the damage region if desirable (e.g. too many rects), so can be more efficient.
744 lines
23 KiB
C
744 lines
23 KiB
C
#define _POSIX_C_SOURCE 200809L
|
|
#include <assert.h>
|
|
#include <fcntl.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <wlr/render/wlr_texture.h>
|
|
#include <wlr/render/vulkan.h>
|
|
#include <wlr/util/log.h>
|
|
#include "render/pixel_format.h"
|
|
#include "render/vulkan.h"
|
|
|
|
static const struct wlr_texture_impl texture_impl;
|
|
|
|
bool wlr_texture_is_vk(struct wlr_texture *wlr_texture) {
|
|
return wlr_texture->impl == &texture_impl;
|
|
}
|
|
|
|
struct wlr_vk_texture *vulkan_get_texture(struct wlr_texture *wlr_texture) {
|
|
assert(wlr_texture_is_vk(wlr_texture));
|
|
return (struct wlr_vk_texture *)wlr_texture;
|
|
}
|
|
|
|
static VkImageAspectFlagBits mem_plane_aspect(unsigned i) {
|
|
switch (i) {
|
|
case 0: return VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT;
|
|
case 1: return VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT;
|
|
case 2: return VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT;
|
|
case 3: return VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT;
|
|
default: abort(); // unreachable
|
|
}
|
|
}
|
|
|
|
// Will transition the texture to shaderReadOnlyOptimal layout for reading
|
|
// from fragment shader later on
|
|
static bool write_pixels(struct wlr_texture *wlr_texture,
|
|
uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x,
|
|
uint32_t src_y, uint32_t dst_x, uint32_t dst_y, const void *vdata,
|
|
VkImageLayout old_layout, VkPipelineStageFlags src_stage,
|
|
VkAccessFlags src_access) {
|
|
VkResult res;
|
|
struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture);
|
|
struct wlr_vk_renderer *renderer = texture->renderer;
|
|
VkDevice dev = texture->renderer->dev->dev;
|
|
|
|
// make sure assumptions are met
|
|
assert(src_x + width <= texture->wlr_texture.width);
|
|
assert(src_y + height <= texture->wlr_texture.height);
|
|
assert(dst_x + width <= texture->wlr_texture.width);
|
|
assert(dst_y + height <= texture->wlr_texture.height);
|
|
|
|
const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(
|
|
texture->format->drm_format);
|
|
assert(format_info);
|
|
|
|
// deferred upload by transfer; using staging buffer
|
|
// calculate maximum side needed
|
|
uint32_t bsize = 0;
|
|
unsigned bytespb = format_info->bpp / 8;
|
|
bsize += height * bytespb * width;
|
|
|
|
// get staging buffer
|
|
struct wlr_vk_buffer_span span = vulkan_get_stage_span(renderer, bsize);
|
|
if (!span.buffer || span.alloc.size != bsize) {
|
|
wlr_log(WLR_ERROR, "Failed to retrieve staging buffer");
|
|
return false;
|
|
}
|
|
|
|
void *vmap;
|
|
res = vkMapMemory(dev, span.buffer->memory, span.alloc.start,
|
|
bsize, 0, &vmap);
|
|
if (res != VK_SUCCESS) {
|
|
wlr_vk_error("vkMapMemory", res);
|
|
return false;
|
|
}
|
|
char *map = (char *)vmap;
|
|
|
|
// record staging cb
|
|
// will be executed before next frame
|
|
VkCommandBuffer cb = vulkan_record_stage_cb(renderer);
|
|
vulkan_change_layout(cb, texture->image,
|
|
old_layout, src_stage, src_access,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
VK_ACCESS_TRANSFER_WRITE_BIT);
|
|
|
|
// upload data
|
|
const char *pdata = vdata; // data iterator
|
|
|
|
uint32_t packed_stride = bytespb * width;
|
|
uint32_t buf_off = span.alloc.start + (map - (char *)vmap);
|
|
|
|
// write data into staging buffer span
|
|
pdata += stride * src_y;
|
|
pdata += bytespb * src_x;
|
|
if (src_x == 0 && width == texture->wlr_texture.width &&
|
|
stride == packed_stride) {
|
|
memcpy(map, pdata, packed_stride * height);
|
|
map += packed_stride * height;
|
|
} else {
|
|
for (unsigned i = 0u; i < height; ++i) {
|
|
memcpy(map, pdata, packed_stride);
|
|
pdata += stride;
|
|
map += packed_stride;
|
|
}
|
|
}
|
|
|
|
VkBufferImageCopy copy;
|
|
copy.imageExtent.width = width;
|
|
copy.imageExtent.height = height;
|
|
copy.imageExtent.depth = 1;
|
|
copy.imageOffset.x = dst_x;
|
|
copy.imageOffset.y = dst_y;
|
|
copy.imageOffset.z = 0;
|
|
copy.bufferOffset = buf_off;
|
|
copy.bufferRowLength = width;
|
|
copy.bufferImageHeight = height;
|
|
copy.imageSubresource.mipLevel = 0;
|
|
copy.imageSubresource.baseArrayLayer = 0;
|
|
copy.imageSubresource.layerCount = 1;
|
|
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
assert((uint32_t)(map - (char *)vmap) == bsize);
|
|
vkUnmapMemory(dev, span.buffer->memory);
|
|
|
|
vkCmdCopyBufferToImage(cb, span.buffer->buffer, texture->image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©);
|
|
vulkan_change_layout(cb, texture->image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_ACCESS_SHADER_READ_BIT);
|
|
texture->last_used = renderer->frame;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool vulkan_texture_update_from_buffer(struct wlr_texture *wlr_texture,
|
|
struct wlr_buffer *buffer, pixman_region32_t *damage) {
|
|
struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture);
|
|
|
|
void *data;
|
|
uint32_t format;
|
|
size_t stride;
|
|
if (!wlr_buffer_begin_data_ptr_access(buffer,
|
|
WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) {
|
|
return false;
|
|
}
|
|
|
|
bool ok = true;
|
|
|
|
if (format != texture->format->drm_format) {
|
|
ok = false;
|
|
goto out;
|
|
}
|
|
|
|
int rects_len = 0;
|
|
pixman_box32_t *rects = pixman_region32_rectangles(damage, &rects_len);
|
|
|
|
for (int i = 0; i < rects_len; i++) {
|
|
pixman_box32_t rect = rects[i];
|
|
uint32_t width = rect.x2 - rect.x1;
|
|
uint32_t height = rect.y2 - rect.y1;
|
|
|
|
// TODO: only map memory once
|
|
ok = write_pixels(wlr_texture, stride, width, height, rect.x1, rect.y1,
|
|
rect.x1, rect.y1, data, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
|
|
if (!ok) {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
wlr_buffer_end_data_ptr_access(buffer);
|
|
return ok;
|
|
}
|
|
|
|
void vulkan_texture_destroy(struct wlr_vk_texture *texture) {
|
|
if (!texture->renderer) {
|
|
free(texture);
|
|
return;
|
|
}
|
|
|
|
// when we recorded a command to fill this image _this_ frame,
|
|
// it has to be executed before the texture can be destroyed.
|
|
// Add it to the renderer->destroy_textures list, destroying
|
|
// _after_ the stage command buffer has exectued
|
|
if (texture->last_used == texture->renderer->frame) {
|
|
assert(texture->destroy_link.next == NULL); // not already inserted
|
|
wl_list_insert(&texture->renderer->destroy_textures,
|
|
&texture->destroy_link);
|
|
return;
|
|
}
|
|
|
|
wl_list_remove(&texture->link);
|
|
wl_list_remove(&texture->buffer_destroy.link);
|
|
|
|
VkDevice dev = texture->renderer->dev->dev;
|
|
if (texture->ds && texture->ds_pool) {
|
|
vulkan_free_ds(texture->renderer, texture->ds_pool, texture->ds);
|
|
}
|
|
|
|
vkDestroyImageView(dev, texture->image_view, NULL);
|
|
vkDestroyImage(dev, texture->image, NULL);
|
|
|
|
for (unsigned i = 0u; i < texture->mem_count; ++i) {
|
|
vkFreeMemory(dev, texture->memories[i], NULL);
|
|
}
|
|
|
|
free(texture);
|
|
}
|
|
|
|
static void vulkan_texture_unref(struct wlr_texture *wlr_texture) {
|
|
struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture);
|
|
if (texture->buffer != NULL) {
|
|
// Keep the texture around, in case the buffer is re-used later. We're
|
|
// still listening to the buffer's destroy event.
|
|
wlr_buffer_unlock(texture->buffer);
|
|
} else {
|
|
vulkan_texture_destroy(texture);
|
|
}
|
|
}
|
|
|
|
static const struct wlr_texture_impl texture_impl = {
|
|
.update_from_buffer = vulkan_texture_update_from_buffer,
|
|
.destroy = vulkan_texture_unref,
|
|
};
|
|
|
|
static struct wlr_vk_texture *vulkan_texture_create(
|
|
struct wlr_vk_renderer *renderer, uint32_t width, uint32_t height) {
|
|
struct wlr_vk_texture *texture =
|
|
calloc(1, sizeof(struct wlr_vk_texture));
|
|
if (texture == NULL) {
|
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
|
return NULL;
|
|
}
|
|
wlr_texture_init(&texture->wlr_texture, &texture_impl, width, height);
|
|
texture->renderer = renderer;
|
|
wl_list_insert(&renderer->textures, &texture->link);
|
|
wl_list_init(&texture->buffer_destroy.link);
|
|
return texture;
|
|
}
|
|
|
|
static struct wlr_texture *vulkan_texture_from_pixels(struct wlr_renderer *wlr_renderer,
|
|
uint32_t drm_fmt, uint32_t stride, uint32_t width,
|
|
uint32_t height, const void *data) {
|
|
struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer);
|
|
|
|
VkResult res;
|
|
VkDevice dev = renderer->dev->dev;
|
|
|
|
wlr_log(WLR_DEBUG, "vulkan_texture_from_pixels: %.4s, %dx%d",
|
|
(const char*) &drm_fmt, width, height);
|
|
|
|
const struct wlr_vk_format_props *fmt =
|
|
vulkan_format_props_from_drm(renderer->dev, drm_fmt);
|
|
if (fmt == NULL) {
|
|
wlr_log(WLR_ERROR, "Unsupported pixel format %"PRIx32 " (%.4s)",
|
|
drm_fmt, (const char*) &drm_fmt);
|
|
return NULL;
|
|
}
|
|
|
|
struct wlr_vk_texture *texture = vulkan_texture_create(renderer, width, height);
|
|
if (texture == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
texture->format = &fmt->format;
|
|
|
|
// create image
|
|
unsigned mem_bits = 0xFFFFFFFF;
|
|
|
|
VkImageCreateInfo img_info = {0};
|
|
img_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
img_info.imageType = VK_IMAGE_TYPE_2D;
|
|
img_info.format = texture->format->vk_format;
|
|
img_info.mipLevels = 1;
|
|
img_info.arrayLayers = 1;
|
|
img_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
img_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
img_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
img_info.extent = (VkExtent3D) { width, height, 1 };
|
|
img_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
|
|
img_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
img_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
mem_bits = vulkan_find_mem_type(renderer->dev,
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_bits);
|
|
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
res = vkCreateImage(dev, &img_info, NULL, &texture->image);
|
|
if (res != VK_SUCCESS) {
|
|
wlr_vk_error("vkCreateImage failed", res);
|
|
goto error;
|
|
}
|
|
|
|
// memory
|
|
VkMemoryRequirements mem_reqs;
|
|
vkGetImageMemoryRequirements(dev, texture->image, &mem_reqs);
|
|
|
|
VkMemoryAllocateInfo mem_info = {0};
|
|
mem_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
mem_info.allocationSize = mem_reqs.size;
|
|
mem_info.memoryTypeIndex = mem_bits & mem_reqs.memoryTypeBits;
|
|
res = vkAllocateMemory(dev, &mem_info, NULL, &texture->memories[0]);
|
|
if (res != VK_SUCCESS) {
|
|
wlr_vk_error("vkAllocatorMemory failed", res);
|
|
goto error;
|
|
}
|
|
|
|
texture->mem_count = 1;
|
|
res = vkBindImageMemory(dev, texture->image, texture->memories[0], 0);
|
|
if (res != VK_SUCCESS) {
|
|
wlr_vk_error("vkBindMemory failed", res);
|
|
goto error;
|
|
}
|
|
|
|
const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(drm_fmt);
|
|
assert(format_info);
|
|
|
|
// view
|
|
VkImageViewCreateInfo view_info = {0};
|
|
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
view_info.format = texture->format->vk_format;
|
|
view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
view_info.components.a = format_info->has_alpha
|
|
? VK_COMPONENT_SWIZZLE_IDENTITY
|
|
: VK_COMPONENT_SWIZZLE_ONE;
|
|
|
|
view_info.subresourceRange = (VkImageSubresourceRange) {
|
|
VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1
|
|
};
|
|
view_info.image = texture->image;
|
|
|
|
res = vkCreateImageView(dev, &view_info, NULL,
|
|
&texture->image_view);
|
|
if (res != VK_SUCCESS) {
|
|
wlr_vk_error("vkCreateImageView failed", res);
|
|
goto error;
|
|
}
|
|
|
|
// descriptor
|
|
texture->ds_pool = vulkan_alloc_texture_ds(renderer, &texture->ds);
|
|
if (!texture->ds_pool) {
|
|
wlr_log(WLR_ERROR, "failed to allocate descriptor");
|
|
goto error;
|
|
}
|
|
|
|
VkDescriptorImageInfo ds_img_info = {0};
|
|
ds_img_info.imageView = texture->image_view;
|
|
ds_img_info.imageLayout = layout;
|
|
|
|
VkWriteDescriptorSet ds_write = {0};
|
|
ds_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
ds_write.descriptorCount = 1;
|
|
ds_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
ds_write.dstSet = texture->ds;
|
|
ds_write.pImageInfo = &ds_img_info;
|
|
|
|
vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL);
|
|
|
|
// write data
|
|
if (!write_pixels(&texture->wlr_texture, stride,
|
|
width, height, 0, 0, 0, 0, data, VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0)) {
|
|
goto error;
|
|
}
|
|
|
|
return &texture->wlr_texture;
|
|
|
|
error:
|
|
vulkan_texture_destroy(texture);
|
|
return NULL;
|
|
}
|
|
|
|
static bool is_dmabuf_disjoint(const struct wlr_dmabuf_attributes *attribs) {
|
|
if (attribs->n_planes == 1) {
|
|
return false;
|
|
}
|
|
|
|
struct stat first_stat;
|
|
if (fstat(attribs->fd[0], &first_stat) != 0) {
|
|
wlr_log_errno(WLR_ERROR, "fstat failed");
|
|
return true;
|
|
}
|
|
|
|
for (int i = 1; i < attribs->n_planes; i++) {
|
|
struct stat plane_stat;
|
|
if (fstat(attribs->fd[i], &plane_stat) != 0) {
|
|
wlr_log_errno(WLR_ERROR, "fstat failed");
|
|
return true;
|
|
}
|
|
|
|
if (first_stat.st_ino != plane_stat.st_ino) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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) {
|
|
VkResult res;
|
|
VkDevice dev = renderer->dev->dev;
|
|
*n_mems = 0u;
|
|
|
|
wlr_log(WLR_DEBUG, "vulkan_import_dmabuf: %.4s (mod %"PRIx64"), %dx%d, %d planes",
|
|
(const char *)&attribs->format, attribs->modifier,
|
|
attribs->width, attribs->height, attribs->n_planes);
|
|
|
|
struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm(renderer->dev,
|
|
attribs->format);
|
|
if (fmt == NULL) {
|
|
wlr_log(WLR_ERROR, "Unsupported pixel format %"PRIx32 " (%.4s)",
|
|
attribs->format, (const char*) &attribs->format);
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
|
|
uint32_t plane_count = attribs->n_planes;
|
|
assert(plane_count < WLR_DMABUF_MAX_PLANES);
|
|
struct wlr_vk_format_modifier_props *mod =
|
|
vulkan_format_props_find_modifier(fmt, attribs->modifier, for_render);
|
|
if (!mod || !(mod->dmabuf_flags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT)) {
|
|
wlr_log(WLR_ERROR, "Format %"PRIx32" (%.4s) can't be used with modifier "
|
|
"%"PRIx64, attribs->format, (const char*) &attribs->format,
|
|
attribs->modifier);
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
|
|
if ((uint32_t) attribs->width > mod->max_extent.width ||
|
|
(uint32_t) attribs->height > mod->max_extent.height) {
|
|
wlr_log(WLR_ERROR, "dmabuf is too large to import");
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
|
|
if (mod->props.drmFormatModifierPlaneCount != plane_count) {
|
|
wlr_log(WLR_ERROR, "Number of planes (%d) does not match format (%d)",
|
|
plane_count, mod->props.drmFormatModifierPlaneCount);
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
|
|
// check if we have to create the image disjoint
|
|
bool disjoint = is_dmabuf_disjoint(attribs);
|
|
if (disjoint && !(mod->props.drmFormatModifierTilingFeatures
|
|
& VK_FORMAT_FEATURE_DISJOINT_BIT)) {
|
|
wlr_log(WLR_ERROR, "Format/Modifier does not support disjoint images");
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
|
|
// image
|
|
VkExternalMemoryHandleTypeFlagBits htype =
|
|
VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
|
|
|
|
VkImageCreateInfo img_info = {0};
|
|
img_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
img_info.imageType = VK_IMAGE_TYPE_2D;
|
|
img_info.format = fmt->format.vk_format;
|
|
img_info.mipLevels = 1;
|
|
img_info.arrayLayers = 1;
|
|
img_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
img_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
img_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
img_info.extent = (VkExtent3D) { attribs->width, attribs->height, 1 };
|
|
img_info.usage = for_render ?
|
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT :
|
|
VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
if (disjoint) {
|
|
img_info.flags = VK_IMAGE_CREATE_DISJOINT_BIT;
|
|
}
|
|
|
|
VkExternalMemoryImageCreateInfo eimg = {0};
|
|
eimg.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
|
|
eimg.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
|
|
img_info.pNext = &eimg;
|
|
|
|
VkSubresourceLayout plane_layouts[WLR_DMABUF_MAX_PLANES] = {0};
|
|
VkImageDrmFormatModifierExplicitCreateInfoEXT mod_info = {0};
|
|
|
|
img_info.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
|
|
for (unsigned i = 0u; i < plane_count; ++i) {
|
|
plane_layouts[i].offset = attribs->offset[i];
|
|
plane_layouts[i].rowPitch = attribs->stride[i];
|
|
plane_layouts[i].size = 0;
|
|
}
|
|
|
|
mod_info.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT;
|
|
mod_info.drmFormatModifierPlaneCount = plane_count;
|
|
mod_info.drmFormatModifier = mod->props.drmFormatModifier;
|
|
mod_info.pPlaneLayouts = plane_layouts;
|
|
eimg.pNext = &mod_info;
|
|
|
|
VkImage image;
|
|
res = vkCreateImage(dev, &img_info, NULL, &image);
|
|
if (res != VK_SUCCESS) {
|
|
wlr_vk_error("vkCreateImage", res);
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
|
|
unsigned mem_count = disjoint ? plane_count : 1u;
|
|
VkBindImageMemoryInfo bindi[WLR_DMABUF_MAX_PLANES] = {0};
|
|
VkBindImagePlaneMemoryInfo planei[WLR_DMABUF_MAX_PLANES] = {0};
|
|
|
|
for (unsigned i = 0u; i < mem_count; ++i) {
|
|
struct VkMemoryFdPropertiesKHR fdp = {0};
|
|
fdp.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR;
|
|
res = renderer->dev->api.getMemoryFdPropertiesKHR(dev, htype,
|
|
attribs->fd[i], &fdp);
|
|
if (res != VK_SUCCESS) {
|
|
wlr_vk_error("getMemoryFdPropertiesKHR", res);
|
|
goto error_image;
|
|
}
|
|
|
|
VkImageMemoryRequirementsInfo2 memri = {0};
|
|
memri.image = image;
|
|
memri.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2;
|
|
|
|
VkImagePlaneMemoryRequirementsInfo planeri = {0};
|
|
if (disjoint) {
|
|
planeri.sType = VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO;
|
|
planeri.planeAspect = mem_plane_aspect(i);
|
|
memri.pNext = &planeri;
|
|
}
|
|
|
|
VkMemoryRequirements2 memr = {0};
|
|
memr.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
|
|
|
|
vkGetImageMemoryRequirements2(dev, &memri, &memr);
|
|
int mem = vulkan_find_mem_type(renderer->dev, 0,
|
|
memr.memoryRequirements.memoryTypeBits & fdp.memoryTypeBits);
|
|
if (mem < 0) {
|
|
wlr_log(WLR_ERROR, "no valid memory type index");
|
|
goto error_image;
|
|
}
|
|
|
|
// Since importing transfers ownership of the FD to Vulkan, we have
|
|
// to duplicate it since this operation does not transfer ownership
|
|
// of the attribs to this texture. Will be closed by Vulkan on
|
|
// vkFreeMemory.
|
|
int dfd = fcntl(attribs->fd[i], F_DUPFD_CLOEXEC, 0);
|
|
if (dfd < 0) {
|
|
wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed");
|
|
goto error_image;
|
|
}
|
|
|
|
VkMemoryAllocateInfo memi = {0};
|
|
memi.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
memi.allocationSize = memr.memoryRequirements.size;
|
|
memi.memoryTypeIndex = mem;
|
|
|
|
VkImportMemoryFdInfoKHR importi = {0};
|
|
importi.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;
|
|
importi.fd = dfd;
|
|
importi.handleType = htype;
|
|
memi.pNext = &importi;
|
|
|
|
VkMemoryDedicatedAllocateInfo dedi = {0};
|
|
dedi.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;
|
|
dedi.image = image;
|
|
importi.pNext = &dedi;
|
|
|
|
res = vkAllocateMemory(dev, &memi, NULL, &mems[i]);
|
|
if (res != VK_SUCCESS) {
|
|
close(dfd);
|
|
wlr_vk_error("vkAllocateMemory failed", res);
|
|
goto error_image;
|
|
}
|
|
|
|
++(*n_mems);
|
|
|
|
// fill bind info
|
|
bindi[i].image = image;
|
|
bindi[i].memory = mems[i];
|
|
bindi[i].memoryOffset = 0;
|
|
bindi[i].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
|
|
|
|
if (disjoint) {
|
|
planei[i].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO;
|
|
planei[i].planeAspect = planeri.planeAspect;
|
|
bindi[i].pNext = &planei[i];
|
|
}
|
|
}
|
|
|
|
res = vkBindImageMemory2(dev, mem_count, bindi);
|
|
if (res != VK_SUCCESS) {
|
|
wlr_vk_error("vkBindMemory failed", res);
|
|
goto error_image;
|
|
}
|
|
|
|
return image;
|
|
|
|
error_image:
|
|
vkDestroyImage(dev, image, NULL);
|
|
for (size_t i = 0u; i < *n_mems; ++i) {
|
|
vkFreeMemory(dev, mems[i], NULL);
|
|
mems[i] = VK_NULL_HANDLE;
|
|
}
|
|
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
|
|
static struct wlr_texture *vulkan_texture_from_dmabuf(struct wlr_renderer *wlr_renderer,
|
|
struct wlr_dmabuf_attributes *attribs) {
|
|
struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer);
|
|
|
|
VkResult res;
|
|
VkDevice dev = renderer->dev->dev;
|
|
|
|
const struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm(
|
|
renderer->dev, attribs->format);
|
|
if (fmt == NULL) {
|
|
wlr_log(WLR_ERROR, "Unsupported pixel format %"PRIx32 " (%.4s)",
|
|
attribs->format, (const char*) &attribs->format);
|
|
return NULL;
|
|
}
|
|
|
|
struct wlr_vk_texture *texture = vulkan_texture_create(renderer,
|
|
attribs->width, attribs->height);
|
|
if (texture == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
texture->format = &fmt->format;
|
|
texture->image = vulkan_import_dmabuf(renderer, attribs,
|
|
texture->memories, &texture->mem_count, false);
|
|
if (!texture->image) {
|
|
goto error;
|
|
}
|
|
|
|
const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(attribs->format);
|
|
assert(format_info);
|
|
|
|
// view
|
|
VkImageViewCreateInfo view_info = {0};
|
|
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
view_info.format = texture->format->vk_format;
|
|
view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
view_info.components.a = format_info->has_alpha
|
|
? VK_COMPONENT_SWIZZLE_IDENTITY
|
|
: VK_COMPONENT_SWIZZLE_ONE;
|
|
|
|
view_info.subresourceRange = (VkImageSubresourceRange) {
|
|
VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1
|
|
};
|
|
view_info.image = texture->image;
|
|
|
|
res = vkCreateImageView(dev, &view_info, NULL, &texture->image_view);
|
|
if (res != VK_SUCCESS) {
|
|
wlr_vk_error("vkCreateImageView failed", res);
|
|
goto error;
|
|
}
|
|
|
|
// descriptor
|
|
texture->ds_pool = vulkan_alloc_texture_ds(renderer, &texture->ds);
|
|
if (!texture->ds_pool) {
|
|
wlr_log(WLR_ERROR, "failed to allocate descriptor");
|
|
goto error;
|
|
}
|
|
|
|
VkDescriptorImageInfo ds_img_info = {0};
|
|
ds_img_info.imageView = texture->image_view;
|
|
ds_img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
VkWriteDescriptorSet ds_write = {0};
|
|
ds_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
ds_write.descriptorCount = 1;
|
|
ds_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
ds_write.dstSet = texture->ds;
|
|
ds_write.pImageInfo = &ds_img_info;
|
|
|
|
vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL);
|
|
texture->dmabuf_imported = true;
|
|
|
|
return &texture->wlr_texture;
|
|
|
|
error:
|
|
vulkan_texture_destroy(texture);
|
|
return NULL;
|
|
}
|
|
|
|
static void texture_handle_buffer_destroy(struct wl_listener *listener,
|
|
void *data) {
|
|
struct wlr_vk_texture *texture =
|
|
wl_container_of(listener, texture, buffer_destroy);
|
|
vulkan_texture_destroy(texture);
|
|
}
|
|
|
|
static struct wlr_texture *vulkan_texture_from_dmabuf_buffer(
|
|
struct wlr_vk_renderer *renderer, struct wlr_buffer *buffer,
|
|
struct wlr_dmabuf_attributes *dmabuf) {
|
|
struct wlr_vk_texture *texture;
|
|
wl_list_for_each(texture, &renderer->textures, link) {
|
|
if (texture->buffer == buffer) {
|
|
wlr_buffer_lock(texture->buffer);
|
|
return &texture->wlr_texture;
|
|
}
|
|
}
|
|
|
|
struct wlr_texture *wlr_texture =
|
|
vulkan_texture_from_dmabuf(&renderer->wlr_renderer, dmabuf);
|
|
if (wlr_texture == NULL) {
|
|
return false;
|
|
}
|
|
|
|
texture = vulkan_get_texture(wlr_texture);
|
|
texture->buffer = wlr_buffer_lock(buffer);
|
|
|
|
texture->buffer_destroy.notify = texture_handle_buffer_destroy;
|
|
wl_signal_add(&buffer->events.destroy, &texture->buffer_destroy);
|
|
|
|
return &texture->wlr_texture;
|
|
}
|
|
|
|
struct wlr_texture *vulkan_texture_from_buffer(
|
|
struct wlr_renderer *wlr_renderer,
|
|
struct wlr_buffer *buffer) {
|
|
struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer);
|
|
|
|
void *data;
|
|
uint32_t format;
|
|
size_t stride;
|
|
struct wlr_dmabuf_attributes dmabuf;
|
|
if (wlr_buffer_get_dmabuf(buffer, &dmabuf)) {
|
|
return vulkan_texture_from_dmabuf_buffer(renderer, buffer, &dmabuf);
|
|
} else if (wlr_buffer_begin_data_ptr_access(buffer,
|
|
WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) {
|
|
struct wlr_texture *tex = vulkan_texture_from_pixels(wlr_renderer,
|
|
format, stride, buffer->width, buffer->height, data);
|
|
wlr_buffer_end_data_ptr_access(buffer);
|
|
return tex;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|