mirror of
				https://gitlab.freedesktop.org/wlroots/wlroots.git
				synced 2025-11-03 09:01:40 -05:00 
			
		
		
		
	Remove debug logs when a texture is created, since this happens pretty often. Use drmGetFormatName() and drmGetFormatModifierName() to log DRM formats and modifiers.
		
			
				
	
	
		
			824 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			824 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#define _POSIX_C_SOURCE 200809L
 | 
						|
#include <assert.h>
 | 
						|
#include <drm_fourcc.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 <xf86drm.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_vk_texture *texture,
 | 
						|
		uint32_t stride, const pixman_region32_t *region, const void *vdata,
 | 
						|
		VkImageLayout old_layout, VkPipelineStageFlags src_stage,
 | 
						|
		VkAccessFlags src_access) {
 | 
						|
	VkResult res;
 | 
						|
	struct wlr_vk_renderer *renderer = texture->renderer;
 | 
						|
	VkDevice dev = texture->renderer->dev->dev;
 | 
						|
 | 
						|
	const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(texture->format->drm);
 | 
						|
	assert(format_info);
 | 
						|
 | 
						|
	uint32_t bsize = 0;
 | 
						|
	unsigned bytespb = format_info->bpp / 8;
 | 
						|
 | 
						|
	// deferred upload by transfer; using staging buffer
 | 
						|
	// calculate maximum side needed
 | 
						|
	int rects_len = 0;
 | 
						|
	const pixman_box32_t *rects = pixman_region32_rectangles(region, &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;
 | 
						|
 | 
						|
		// make sure assumptions are met
 | 
						|
		assert((uint32_t)rect.x2 <= texture->wlr_texture.width);
 | 
						|
		assert((uint32_t)rect.y2 <= texture->wlr_texture.height);
 | 
						|
 | 
						|
		bsize += height * bytespb * width;
 | 
						|
	}
 | 
						|
 | 
						|
	VkBufferImageCopy *copies = calloc((size_t)rects_len, sizeof(*copies));
 | 
						|
	if (!copies) {
 | 
						|
		wlr_log(WLR_ERROR, "Failed to allocate image copy parameters");
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	// get staging buffer
 | 
						|
	struct wlr_vk_buffer_span span = vulkan_get_stage_span(renderer, bsize, bytespb);
 | 
						|
	if (!span.buffer || span.alloc.size != bsize) {
 | 
						|
		wlr_log(WLR_ERROR, "Failed to retrieve staging buffer");
 | 
						|
		free(copies);
 | 
						|
		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);
 | 
						|
		free(copies);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	char *map = (char *)vmap;
 | 
						|
 | 
						|
	// upload data
 | 
						|
 | 
						|
	uint32_t buf_off = span.alloc.start + (map - (char *)vmap);
 | 
						|
	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;
 | 
						|
		uint32_t src_x = rect.x1;
 | 
						|
		uint32_t src_y = rect.y1;
 | 
						|
 | 
						|
		uint32_t packed_stride = bytespb * width;
 | 
						|
 | 
						|
		// write data into staging buffer span
 | 
						|
		const char *pdata = vdata; // data iterator
 | 
						|
		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;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		copies[i] = (VkBufferImageCopy) {
 | 
						|
			.imageExtent.width = width,
 | 
						|
			.imageExtent.height = height,
 | 
						|
			.imageExtent.depth = 1,
 | 
						|
			.imageOffset.x = src_x,
 | 
						|
			.imageOffset.y = src_y,
 | 
						|
			.imageOffset.z = 0,
 | 
						|
			.bufferOffset = buf_off,
 | 
						|
			.bufferRowLength = width,
 | 
						|
			.bufferImageHeight = height,
 | 
						|
			.imageSubresource.mipLevel = 0,
 | 
						|
			.imageSubresource.baseArrayLayer = 0,
 | 
						|
			.imageSubresource.layerCount = 1,
 | 
						|
			.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
 | 
						|
		};
 | 
						|
 | 
						|
 | 
						|
		buf_off += height * packed_stride;
 | 
						|
	}
 | 
						|
 | 
						|
	assert((uint32_t)(map - (char *)vmap) == bsize);
 | 
						|
	vkUnmapMemory(dev, span.buffer->memory);
 | 
						|
 | 
						|
	// 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);
 | 
						|
 | 
						|
	vkCmdCopyBufferToImage(cb, span.buffer->buffer, texture->image,
 | 
						|
		VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (uint32_t)rects_len, copies);
 | 
						|
	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_cb = renderer->stage.cb;
 | 
						|
 | 
						|
	free(copies);
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
static bool vulkan_texture_update_from_buffer(struct wlr_texture *wlr_texture,
 | 
						|
		struct wlr_buffer *buffer, const 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) {
 | 
						|
		ok = false;
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	ok = write_pixels(texture, stride, damage, data, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
 | 
						|
		VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
 | 
						|
 | 
						|
out:
 | 
						|
	wlr_buffer_end_data_ptr_access(buffer);
 | 
						|
	return ok;
 | 
						|
}
 | 
						|
 | 
						|
void vulkan_texture_destroy(struct wlr_vk_texture *texture) {
 | 
						|
	if (texture->buffer != NULL) {
 | 
						|
		wlr_addon_finish(&texture->buffer_addon);
 | 
						|
		texture->buffer = NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	// 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_cb != NULL) {
 | 
						|
		assert(texture->destroy_link.next == NULL); // not already inserted
 | 
						|
		wl_list_insert(&texture->last_used_cb->destroy_textures,
 | 
						|
			&texture->destroy_link);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	wl_list_remove(&texture->link);
 | 
						|
 | 
						|
	VkDevice dev = texture->renderer->dev->dev;
 | 
						|
	if (texture->ds && texture->ds_pool) {
 | 
						|
		vulkan_free_ds(texture->renderer, texture->ds_pool, texture->ds);
 | 
						|
	}
 | 
						|
 | 
						|
	for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) {
 | 
						|
		if (texture->foreign_semaphores[i] != VK_NULL_HANDLE) {
 | 
						|
			vkDestroySemaphore(dev, texture->foreign_semaphores[i], NULL);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	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, &renderer->wlr_renderer,
 | 
						|
		&texture_impl, width, height);
 | 
						|
	texture->renderer = renderer;
 | 
						|
	wl_list_insert(&renderer->textures, &texture->link);
 | 
						|
	return texture;
 | 
						|
}
 | 
						|
 | 
						|
static struct wlr_texture *vulkan_texture_from_pixels(
 | 
						|
		struct wlr_vk_renderer *renderer, uint32_t drm_fmt, uint32_t stride,
 | 
						|
		uint32_t width, uint32_t height, const void *data) {
 | 
						|
	VkResult res;
 | 
						|
	VkDevice dev = renderer->dev->dev;
 | 
						|
 | 
						|
	const struct wlr_vk_format_props *fmt =
 | 
						|
		vulkan_format_props_from_drm(renderer->dev, drm_fmt);
 | 
						|
	if (fmt == NULL && fmt->format.is_ycbcr) {
 | 
						|
		char *format_name = drmGetFormatName(drm_fmt);
 | 
						|
		wlr_log(WLR_ERROR, "Unsupported pixel format %s (0x%08"PRIX32")",
 | 
						|
			format_name, drm_fmt);
 | 
						|
		free(format_name);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	struct wlr_vk_texture *texture = vulkan_texture_create(renderer, width, height);
 | 
						|
	if (texture == NULL) {
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	texture->format = &fmt->format;
 | 
						|
 | 
						|
	// create image
 | 
						|
	VkImageCreateInfo img_info = {
 | 
						|
		.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
 | 
						|
		.imageType = VK_IMAGE_TYPE_2D,
 | 
						|
		.format = texture->format->vk,
 | 
						|
		.mipLevels = 1,
 | 
						|
		.arrayLayers = 1,
 | 
						|
		.samples = VK_SAMPLE_COUNT_1_BIT,
 | 
						|
		.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
 | 
						|
		.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
 | 
						|
		.extent = (VkExtent3D) { width, height, 1 },
 | 
						|
		.tiling = VK_IMAGE_TILING_OPTIMAL,
 | 
						|
		.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
 | 
						|
	};
 | 
						|
 | 
						|
	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);
 | 
						|
 | 
						|
	int mem_type_index = vulkan_find_mem_type(renderer->dev,
 | 
						|
		VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_reqs.memoryTypeBits);
 | 
						|
	if (mem_type_index == -1) {
 | 
						|
		wlr_log(WLR_ERROR, "failed to find suitable vulkan memory type");
 | 
						|
		goto error;
 | 
						|
	}
 | 
						|
 | 
						|
	VkMemoryAllocateInfo mem_info = {
 | 
						|
		.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
 | 
						|
		.allocationSize = mem_reqs.size,
 | 
						|
		.memoryTypeIndex = mem_type_index,
 | 
						|
	};
 | 
						|
 | 
						|
	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);
 | 
						|
	texture->has_alpha = format_info->has_alpha;
 | 
						|
 | 
						|
	// view
 | 
						|
	VkImageViewCreateInfo view_info = {
 | 
						|
		.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
 | 
						|
		.viewType = VK_IMAGE_VIEW_TYPE_2D,
 | 
						|
		.format = texture->format->vk,
 | 
						|
		.components.r = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
						|
		.components.g = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
						|
		.components.b = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
						|
		.components.a = texture->has_alpha
 | 
						|
			? VK_COMPONENT_SWIZZLE_IDENTITY
 | 
						|
			: VK_COMPONENT_SWIZZLE_ONE,
 | 
						|
 | 
						|
		.subresourceRange = (VkImageSubresourceRange) {
 | 
						|
			.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
 | 
						|
			.baseMipLevel = 0,
 | 
						|
			.levelCount = 1,
 | 
						|
			.baseArrayLayer = 0,
 | 
						|
			.layerCount = 1,
 | 
						|
		},
 | 
						|
		.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, renderer->ds_layout, &texture->ds);
 | 
						|
	if (!texture->ds_pool) {
 | 
						|
		wlr_log(WLR_ERROR, "failed to allocate descriptor");
 | 
						|
		goto error;
 | 
						|
	}
 | 
						|
 | 
						|
	VkDescriptorImageInfo ds_img_info = {
 | 
						|
		.imageView = texture->image_view,
 | 
						|
		.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
 | 
						|
	};
 | 
						|
 | 
						|
	VkWriteDescriptorSet ds_write = {
 | 
						|
		.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
 | 
						|
		.descriptorCount = 1,
 | 
						|
		.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
 | 
						|
		.dstSet = texture->ds,
 | 
						|
		.pImageInfo = &ds_img_info,
 | 
						|
	};
 | 
						|
 | 
						|
	vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL);
 | 
						|
 | 
						|
	// write data
 | 
						|
	pixman_region32_t region;
 | 
						|
	pixman_region32_init_rect(®ion, 0, 0, width, height);
 | 
						|
	if (!write_pixels(texture, stride, ®ion, 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;
 | 
						|
 | 
						|
	struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm(renderer->dev,
 | 
						|
		attribs->format);
 | 
						|
	if (fmt == NULL) {
 | 
						|
		char *format_name = drmGetFormatName(attribs->format);
 | 
						|
		wlr_log(WLR_ERROR, "Unsupported pixel format %s (0x%08"PRIX32")",
 | 
						|
			format_name, attribs->format);
 | 
						|
		free(format_name);
 | 
						|
		return VK_NULL_HANDLE;
 | 
						|
	}
 | 
						|
 | 
						|
	uint32_t plane_count = attribs->n_planes;
 | 
						|
	assert(plane_count < WLR_DMABUF_MAX_PLANES);
 | 
						|
	const struct wlr_vk_format_modifier_props *mod =
 | 
						|
		vulkan_format_props_find_modifier(fmt, attribs->modifier, for_render);
 | 
						|
	if (!mod) {
 | 
						|
		char *format_name = drmGetFormatName(attribs->format);
 | 
						|
		char *modifier_name = drmGetFormatModifierName(attribs->modifier);
 | 
						|
		wlr_log(WLR_ERROR, "Format %s (0x%08"PRIX32") can't be used with modifier "
 | 
						|
			"%s (0x%016"PRIX64")", format_name, attribs->format,
 | 
						|
			modifier_name, attribs->modifier);
 | 
						|
		free(format_name);
 | 
						|
		free(modifier_name);
 | 
						|
		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, "DMA-BUF 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 = {
 | 
						|
		.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
 | 
						|
		.imageType = VK_IMAGE_TYPE_2D,
 | 
						|
		.format = fmt->format.vk,
 | 
						|
		.mipLevels = 1,
 | 
						|
		.arrayLayers = 1,
 | 
						|
		.samples = VK_SAMPLE_COUNT_1_BIT,
 | 
						|
		.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
 | 
						|
		.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
 | 
						|
		.extent = (VkExtent3D) { attribs->width, attribs->height, 1 },
 | 
						|
		.usage = for_render ?
 | 
						|
			VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT :
 | 
						|
			VK_IMAGE_USAGE_SAMPLED_BIT,
 | 
						|
	};
 | 
						|
	if (disjoint) {
 | 
						|
		img_info.flags = VK_IMAGE_CREATE_DISJOINT_BIT;
 | 
						|
	}
 | 
						|
 | 
						|
	VkExternalMemoryImageCreateInfo eimg = {
 | 
						|
		.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
 | 
						|
		.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
 | 
						|
	};
 | 
						|
	img_info.pNext = &eimg;
 | 
						|
 | 
						|
	VkSubresourceLayout plane_layouts[WLR_DMABUF_MAX_PLANES] = {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;
 | 
						|
	}
 | 
						|
 | 
						|
	VkImageDrmFormatModifierExplicitCreateInfoEXT mod_info = {
 | 
						|
		.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT,
 | 
						|
		.drmFormatModifierPlaneCount = plane_count,
 | 
						|
		.drmFormatModifier = mod->props.drmFormatModifier,
 | 
						|
		.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) {
 | 
						|
		VkMemoryFdPropertiesKHR fdp = {
 | 
						|
			.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR,
 | 
						|
		};
 | 
						|
		res = renderer->dev->api.vkGetMemoryFdPropertiesKHR(dev, htype,
 | 
						|
			attribs->fd[i], &fdp);
 | 
						|
		if (res != VK_SUCCESS) {
 | 
						|
			wlr_vk_error("getMemoryFdPropertiesKHR", res);
 | 
						|
			goto error_image;
 | 
						|
		}
 | 
						|
 | 
						|
		VkImageMemoryRequirementsInfo2 memri = {
 | 
						|
			.image = image,
 | 
						|
			.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2,
 | 
						|
		};
 | 
						|
 | 
						|
		VkImagePlaneMemoryRequirementsInfo planeri;
 | 
						|
		if (disjoint) {
 | 
						|
			planeri = (VkImagePlaneMemoryRequirementsInfo){
 | 
						|
				.sType = VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO,
 | 
						|
				.planeAspect = mem_plane_aspect(i),
 | 
						|
			};
 | 
						|
			memri.pNext = &planeri;
 | 
						|
		}
 | 
						|
 | 
						|
		VkMemoryRequirements2 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 = {
 | 
						|
			.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
 | 
						|
			.allocationSize = memr.memoryRequirements.size,
 | 
						|
			.memoryTypeIndex = mem,
 | 
						|
		};
 | 
						|
 | 
						|
		VkImportMemoryFdInfoKHR importi = {
 | 
						|
			.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
 | 
						|
			.fd = dfd,
 | 
						|
			.handleType = htype,
 | 
						|
		};
 | 
						|
		memi.pNext = &importi;
 | 
						|
 | 
						|
		VkMemoryDedicatedAllocateInfo dedi = {
 | 
						|
			.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
 | 
						|
			.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_vk_texture *vulkan_texture_from_dmabuf(
 | 
						|
		struct wlr_vk_renderer *renderer,
 | 
						|
		struct wlr_dmabuf_attributes *attribs) {
 | 
						|
	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) {
 | 
						|
		char *format_name = drmGetFormatName(attribs->format);
 | 
						|
		wlr_log(WLR_ERROR, "Unsupported pixel format %s (0x%08"PRIX32")",
 | 
						|
			format_name, attribs->format);
 | 
						|
		free(format_name);
 | 
						|
		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;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!fmt->format.is_ycbcr) {
 | 
						|
		const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(attribs->format);
 | 
						|
		assert(format_info);
 | 
						|
		texture->has_alpha = format_info->has_alpha;
 | 
						|
	}
 | 
						|
 | 
						|
	// view
 | 
						|
	VkImageViewCreateInfo view_info = {
 | 
						|
		.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
 | 
						|
		.viewType = VK_IMAGE_VIEW_TYPE_2D,
 | 
						|
		.format = texture->format->vk,
 | 
						|
		.components.r = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
						|
		.components.g = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
						|
		.components.b = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
						|
		.components.a = texture->has_alpha
 | 
						|
			? VK_COMPONENT_SWIZZLE_IDENTITY
 | 
						|
			: VK_COMPONENT_SWIZZLE_ONE,
 | 
						|
 | 
						|
		.subresourceRange = (VkImageSubresourceRange) {
 | 
						|
			.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
 | 
						|
			.baseMipLevel = 0,
 | 
						|
			.levelCount = 1,
 | 
						|
			.baseArrayLayer = 0,
 | 
						|
			.layerCount = 1,
 | 
						|
		},
 | 
						|
		.image = texture->image,
 | 
						|
	};
 | 
						|
 | 
						|
	VkDescriptorSetLayout ds_layout = renderer->ds_layout;
 | 
						|
	VkSamplerYcbcrConversionInfo ycbcr_conversion_info;
 | 
						|
	if (fmt->format.is_ycbcr) {
 | 
						|
		ds_layout = renderer->nv12_ds_layout;
 | 
						|
		ycbcr_conversion_info = (VkSamplerYcbcrConversionInfo){
 | 
						|
			.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
 | 
						|
			.conversion = renderer->nv12_conversion,
 | 
						|
		};
 | 
						|
		view_info.pNext = &ycbcr_conversion_info;
 | 
						|
	}
 | 
						|
 | 
						|
	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, ds_layout, &texture->ds);
 | 
						|
	if (!texture->ds_pool) {
 | 
						|
		wlr_log(WLR_ERROR, "failed to allocate descriptor");
 | 
						|
		goto error;
 | 
						|
	}
 | 
						|
 | 
						|
	VkDescriptorImageInfo ds_img_info = {
 | 
						|
		.imageView = texture->image_view,
 | 
						|
		.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
 | 
						|
	};
 | 
						|
 | 
						|
	VkWriteDescriptorSet ds_write = {
 | 
						|
		.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
 | 
						|
		.descriptorCount = 1,
 | 
						|
		.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
 | 
						|
		.dstSet = texture->ds,
 | 
						|
		.pImageInfo = &ds_img_info,
 | 
						|
	};
 | 
						|
 | 
						|
	vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL);
 | 
						|
	texture->dmabuf_imported = true;
 | 
						|
 | 
						|
	return texture;
 | 
						|
 | 
						|
error:
 | 
						|
	vulkan_texture_destroy(texture);
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static void texture_handle_buffer_destroy(struct wlr_addon *addon) {
 | 
						|
	struct wlr_vk_texture *texture =
 | 
						|
		wl_container_of(addon, texture, buffer_addon);
 | 
						|
	// We might keep the texture around, waiting for pending command buffers to
 | 
						|
	// complete before free'ing descriptor sets.
 | 
						|
	vulkan_texture_destroy(texture);
 | 
						|
}
 | 
						|
 | 
						|
static const struct wlr_addon_interface buffer_addon_impl = {
 | 
						|
	.name = "wlr_vk_texture",
 | 
						|
	.destroy = texture_handle_buffer_destroy,
 | 
						|
};
 | 
						|
 | 
						|
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_addon *addon =
 | 
						|
		wlr_addon_find(&buffer->addons, renderer, &buffer_addon_impl);
 | 
						|
	if (addon != NULL) {
 | 
						|
		struct wlr_vk_texture *texture =
 | 
						|
			wl_container_of(addon, texture, buffer_addon);
 | 
						|
		wlr_buffer_lock(texture->buffer);
 | 
						|
		return &texture->wlr_texture;
 | 
						|
	}
 | 
						|
 | 
						|
	struct wlr_vk_texture *texture = vulkan_texture_from_dmabuf(renderer, dmabuf);
 | 
						|
	if (texture == NULL) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	texture->buffer = wlr_buffer_lock(buffer);
 | 
						|
	wlr_addon_init(&texture->buffer_addon, &buffer->addons, renderer,
 | 
						|
		&buffer_addon_impl);
 | 
						|
 | 
						|
	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(renderer,
 | 
						|
			format, stride, buffer->width, buffer->height, data);
 | 
						|
		wlr_buffer_end_data_ptr_access(buffer);
 | 
						|
		return tex;
 | 
						|
	} else {
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void wlr_vk_texture_get_image_attribs(struct wlr_texture *texture,
 | 
						|
		struct wlr_vk_image_attribs *attribs) {
 | 
						|
	struct wlr_vk_texture *vk_texture = vulkan_get_texture(texture);
 | 
						|
	attribs->image = vk_texture->image;
 | 
						|
	attribs->format = vk_texture->format->vk;
 | 
						|
	attribs->layout = vk_texture->transitioned ?
 | 
						|
		VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
 | 
						|
}
 | 
						|
 | 
						|
bool wlr_vk_texture_has_alpha(struct wlr_texture *texture) {
 | 
						|
	struct wlr_vk_texture *vk_texture = vulkan_get_texture(texture);
 | 
						|
	return vk_texture->has_alpha;
 | 
						|
}
 |