mirror of
https://gitlab.freedesktop.org/wayland/weston.git
synced 2026-05-05 19:18:13 +02:00
vulkan-renderer: support ycbcr shm
Initial implementation for ycbcr shm buffers. This requires copying multiplanar shm buffers to Vulkan images, which was so far unimplemented in vulkan-renderer. Signed-off-by: Erico Nunes <nunes.erico@gmail.com>
This commit is contained in:
parent
238221e833
commit
3665661a09
3 changed files with 316 additions and 12 deletions
|
|
@ -697,7 +697,7 @@ static const struct pixel_format_info pixel_format_table[] = {
|
|||
COLOR_MODEL(YUV),
|
||||
.num_planes = 1,
|
||||
.hsub = 2,
|
||||
VULKAN_FORMAT(VK_FORMAT_B8G8R8G8_422_UNORM),
|
||||
// VULKAN_FORMAT(VK_FORMAT_B8G8R8G8_422_UNORM),
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(YVYU),
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ struct vulkan_renderer {
|
|||
PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR get_xcb_presentation_support;
|
||||
#endif
|
||||
|
||||
PFN_vkBindImageMemory2KHR bind_image_memory2;
|
||||
PFN_vkGetImageMemoryRequirements2KHR get_image_memory_requirements2;
|
||||
PFN_vkGetMemoryFdPropertiesKHR get_memory_fd_properties;
|
||||
PFN_vkGetSemaphoreFdKHR get_semaphore_fd;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,9 @@
|
|||
|
||||
#include <xf86drm.h> /* Physical device drm */
|
||||
|
||||
#define ALIGN(value, alignment) (((value) + alignment - 1) & ~(alignment - 1))
|
||||
#define MAX_PLANES 3
|
||||
|
||||
enum vulkan_debug_mode {
|
||||
DEBUG_MODE_NONE = 0,
|
||||
DEBUG_MODE_FRAGMENT,
|
||||
|
|
@ -2886,6 +2889,101 @@ create_texture_image(struct vulkan_renderer *vr,
|
|||
create_image_view(vr->dev, texture->image, pixel_format->vulkan_format, NULL, &texture->image_view);
|
||||
}
|
||||
|
||||
static void
|
||||
update_texture_image_nv12(struct vulkan_renderer *vr,
|
||||
struct vulkan_renderer_texture_image *texture,
|
||||
VkImageLayout expected_layout,
|
||||
const struct pixel_format_info *pixel_format,
|
||||
uint32_t buffer_width, uint32_t buffer_height,
|
||||
const void * const pixels)
|
||||
{
|
||||
uint32_t plane_count = pixel_format_get_plane_count(pixel_format);
|
||||
|
||||
// TODO
|
||||
const uint32_t plane_bpp_nv12[] = {
|
||||
sizeof(uint8_t),
|
||||
sizeof(uint16_t),
|
||||
};
|
||||
|
||||
assert(plane_count <= MAX_PLANES);
|
||||
|
||||
VkDeviceSize buffer_size = 0;
|
||||
for (uint32_t i = 0; i < plane_count; i++) {
|
||||
uint32_t hsub = pixel_format_hsub(pixel_format, i);
|
||||
uint32_t vsub = pixel_format_vsub(pixel_format, i);
|
||||
|
||||
buffer_size += (buffer_width/hsub) * (buffer_height/vsub) * plane_bpp_nv12[i];
|
||||
}
|
||||
|
||||
VkResult result;
|
||||
|
||||
assert(pixels);
|
||||
|
||||
vkWaitForFences(vr->dev, 1, &texture->upload_fence, VK_TRUE, UINT64_MAX);
|
||||
vkResetFences(vr->dev, 1, &texture->upload_fence);
|
||||
|
||||
memcpy(texture->staging_map, pixels, (size_t)buffer_size);
|
||||
|
||||
const VkCommandBufferBeginInfo begin_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
};
|
||||
|
||||
VkCommandBuffer cmd_buffer = texture->upload_cmd;
|
||||
|
||||
result = vkBeginCommandBuffer(cmd_buffer, &begin_info);
|
||||
check_vk_success(result, "vkBeginCommandBuffer");
|
||||
|
||||
transition_image_layout(cmd_buffer, texture->image,
|
||||
expected_layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
|
||||
VkBufferImageCopy regions[MAX_PLANES] = { 0 };
|
||||
uint32_t buffer_offset = 0;
|
||||
for (uint32_t i = 0; i < plane_count; i++) {
|
||||
uint32_t hsub = pixel_format_hsub(pixel_format, i);
|
||||
uint32_t vsub = pixel_format_vsub(pixel_format, i);
|
||||
|
||||
const VkOffset3D image_offset = { 0, 0, 0 };
|
||||
const VkExtent3D image_extent = { buffer_width/hsub, buffer_height/vsub, 1 };
|
||||
const VkBufferImageCopy region = {
|
||||
.bufferOffset = buffer_offset,
|
||||
.bufferRowLength = 0, // these are 0 for tightly packed data (match imageExtent texels)
|
||||
.bufferImageHeight = 0, // these are 0 for tightly packed data (match imageExtent texels)
|
||||
.imageSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT << i,
|
||||
.imageSubresource.mipLevel = 0,
|
||||
.imageSubresource.baseArrayLayer = 0,
|
||||
.imageSubresource.layerCount = 1,
|
||||
.imageOffset = image_offset,
|
||||
.imageExtent = image_extent,
|
||||
};
|
||||
|
||||
regions[i] = region;
|
||||
|
||||
buffer_offset = buffer_offset + (buffer_width/hsub) * (buffer_height/vsub) * plane_bpp_nv12[i];
|
||||
}
|
||||
|
||||
vkCmdCopyBufferToImage(cmd_buffer, texture->staging_buffer, texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, plane_count, regions);
|
||||
|
||||
transition_image_layout(cmd_buffer, texture->image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
|
||||
result = vkEndCommandBuffer(cmd_buffer);
|
||||
check_vk_success(result, "vkEndCommandBuffer");
|
||||
|
||||
const VkSubmitInfo submit_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &cmd_buffer,
|
||||
};
|
||||
|
||||
result = vkQueueSubmit(vr->queue, 1, &submit_info, texture->upload_fence);
|
||||
check_vk_success(result, "vkQueueSubmit");
|
||||
}
|
||||
|
||||
static void
|
||||
vulkan_renderer_flush_damage(struct weston_paint_node *pnode)
|
||||
{
|
||||
|
|
@ -2926,19 +3024,28 @@ vulkan_renderer_flush_damage(struct weston_paint_node *pnode)
|
|||
|
||||
data = wl_shm_buffer_get_data(buffer->shm_buffer);
|
||||
|
||||
// TODO
|
||||
if (buffer->pixel_format->color_model == COLOR_MODEL_YUV)
|
||||
vb->needs_full_upload = true;
|
||||
|
||||
if (vb->needs_full_upload || quirks->force_full_upload) {
|
||||
wl_shm_buffer_begin_access(buffer->shm_buffer);
|
||||
if (buffer->pixel_format->color_model != COLOR_MODEL_YUV) {
|
||||
for (int j = 0; j < vb->num_textures; j++) {
|
||||
int hsub = pixel_format_hsub(buffer->pixel_format, j);
|
||||
int vsub = pixel_format_vsub(buffer->pixel_format, j);
|
||||
void *pixels = data + vb->offset[j];
|
||||
uint32_t buffer_width = buffer->width / hsub;
|
||||
uint32_t buffer_height = buffer->height / vsub;
|
||||
|
||||
for (int j = 0; j < vb->num_textures; j++) {
|
||||
int hsub = pixel_format_hsub(buffer->pixel_format, j);
|
||||
int vsub = pixel_format_vsub(buffer->pixel_format, j);
|
||||
void *pixels = data + vb->offset[j];
|
||||
uint32_t buffer_width = buffer->width / hsub;
|
||||
uint32_t buffer_height = buffer->height / vsub;
|
||||
|
||||
update_texture_image_all(vr, &vb->texture, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
buffer->pixel_format, buffer_width, buffer_height,
|
||||
vb->pitch, pixels);
|
||||
update_texture_image_all(vr, &vb->texture, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
buffer->pixel_format, buffer_width, buffer_height,
|
||||
vb->pitch, pixels);
|
||||
}
|
||||
} else { // YUV
|
||||
update_texture_image_nv12(vr, &vb->texture, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
buffer->pixel_format, buffer->width, buffer->height,
|
||||
data);
|
||||
}
|
||||
wl_shm_buffer_end_access(buffer->shm_buffer);
|
||||
goto done;
|
||||
|
|
@ -2990,6 +3097,152 @@ handle_buffer_destroy(struct wl_listener *listener, void *data)
|
|||
destroy_buffer_state(vb);
|
||||
}
|
||||
|
||||
static void
|
||||
create_image_nv12(struct vulkan_renderer *vr,
|
||||
uint32_t width, uint32_t height,
|
||||
struct vulkan_renderer_texture_image *texture,
|
||||
VkFormat format, VkImageTiling tiling,
|
||||
VkImageUsageFlags usage, VkMemoryPropertyFlags properties)
|
||||
{
|
||||
assert(vulkan_device_has(vr, EXTENSION_KHR_BIND_MEMORY_2));
|
||||
assert(vulkan_device_has(vr, EXTENSION_KHR_GET_MEMORY_REQUIREMENTS_2));
|
||||
|
||||
VkResult result;
|
||||
|
||||
const VkFenceCreateInfo fence_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
|
||||
};
|
||||
result = vkCreateFence(vr->dev, &fence_info, NULL, &texture->upload_fence);
|
||||
check_vk_success(result, "vkCreateFence");
|
||||
|
||||
const VkCommandBufferAllocateInfo cmd_alloc_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandPool = vr->cmd_pool,
|
||||
.commandBufferCount = 1,
|
||||
};
|
||||
result = vkAllocateCommandBuffers(vr->dev, &cmd_alloc_info, &texture->upload_cmd);
|
||||
check_vk_success(result, "vkAllocateCommandBuffers");
|
||||
|
||||
const VkImageCreateInfo image_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = format,
|
||||
.extent.width = width,
|
||||
.extent.height = height,
|
||||
.extent.depth = 1,
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = tiling,
|
||||
.usage = usage,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.flags = VK_IMAGE_CREATE_DISJOINT_BIT,
|
||||
};
|
||||
|
||||
result = vkCreateImage(vr->dev, &image_info, NULL, &texture->image);
|
||||
check_vk_success(result, "vkCreateImage");
|
||||
|
||||
/* TODO assumes 2 planes */
|
||||
VkImagePlaneMemoryRequirementsInfo plane_req_info0 = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO,
|
||||
.planeAspect = VK_IMAGE_ASPECT_PLANE_0_BIT
|
||||
};
|
||||
VkImageMemoryRequirementsInfo2 mem_reqs_info0 = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2,
|
||||
.image = texture->image,
|
||||
};
|
||||
pnext(&mem_reqs_info0, &plane_req_info0);
|
||||
|
||||
VkMemoryRequirements2 mem_reqs0 = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
|
||||
};
|
||||
vr->get_image_memory_requirements2(vr->dev, &mem_reqs_info0, &mem_reqs0);
|
||||
|
||||
VkImagePlaneMemoryRequirementsInfo plane_req_info1 = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO,
|
||||
.planeAspect = VK_IMAGE_ASPECT_PLANE_1_BIT
|
||||
};
|
||||
VkImageMemoryRequirementsInfo2 mem_reqs_info1 = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2,
|
||||
.image = texture->image,
|
||||
};
|
||||
pnext(&mem_reqs_info1, &plane_req_info1);
|
||||
|
||||
VkMemoryRequirements2 mem_reqs1 = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
|
||||
};
|
||||
vr->get_image_memory_requirements2(vr->dev, &mem_reqs_info1, &mem_reqs1);
|
||||
|
||||
VkDeviceSize size0 = mem_reqs0.memoryRequirements.size;
|
||||
|
||||
VkDeviceSize size1 = mem_reqs1.memoryRequirements.size;
|
||||
VkDeviceSize align1 = mem_reqs1.memoryRequirements.alignment;
|
||||
|
||||
VkDeviceSize offset0 = 0;
|
||||
VkDeviceSize offset1 = ALIGN(offset0 + size0, align1);
|
||||
VkDeviceSize total_size = ALIGN(offset1 + size1, 4096);
|
||||
|
||||
uint32_t allowed_bits = mem_reqs0.memoryRequirements.memoryTypeBits &
|
||||
mem_reqs1.memoryRequirements.memoryTypeBits;
|
||||
assert(allowed_bits != 0);
|
||||
|
||||
int memory_type = find_memory_type(vr, allowed_bits, properties);
|
||||
assert(memory_type >= 0);
|
||||
|
||||
const VkMemoryAllocateInfo alloc_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = total_size,
|
||||
.memoryTypeIndex = memory_type,
|
||||
};
|
||||
result = vkAllocateMemory(vr->dev, &alloc_info, NULL, &texture->memory);
|
||||
check_vk_success(result, "vkAllocateMemory");
|
||||
|
||||
/* TODO assumes 2 planes */
|
||||
VkBindImagePlaneMemoryInfo bind_plane0 = {
|
||||
.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO,
|
||||
.planeAspect = VK_IMAGE_ASPECT_PLANE_0_BIT,
|
||||
};
|
||||
VkBindImageMemoryInfo bind_info0 = {
|
||||
.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO,
|
||||
.image = texture->image,
|
||||
.memory = texture->memory,
|
||||
.memoryOffset = offset0,
|
||||
};
|
||||
pnext(&bind_info0, &bind_plane0);
|
||||
|
||||
VkBindImagePlaneMemoryInfo bind_plane1 = {
|
||||
.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO,
|
||||
.planeAspect = VK_IMAGE_ASPECT_PLANE_1_BIT,
|
||||
};
|
||||
VkBindImageMemoryInfo bind_info1 = {
|
||||
.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO,
|
||||
.image = texture->image,
|
||||
.memory = texture->memory,
|
||||
.memoryOffset = offset1,
|
||||
};
|
||||
pnext(&bind_info1, &bind_plane1);
|
||||
|
||||
const VkBindImageMemoryInfo bind_info[2] = {
|
||||
bind_info0,
|
||||
bind_info1,
|
||||
};
|
||||
|
||||
result = vr->bind_image_memory2(vr->dev, 2, bind_info);
|
||||
check_vk_success(result, "vkBindImageMemory2");
|
||||
|
||||
VkDeviceSize image_size = total_size;
|
||||
create_buffer(vr, image_size,
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||
&texture->staging_buffer, &texture->staging_memory);
|
||||
|
||||
result = vkMapMemory(vr->dev, texture->staging_memory, 0, image_size, 0, &texture->staging_map);
|
||||
check_vk_success(result, "vkMapMemory");
|
||||
}
|
||||
|
||||
static void
|
||||
vulkan_renderer_attach_shm(struct weston_surface *surface, struct weston_buffer *buffer)
|
||||
{
|
||||
|
|
@ -3006,8 +3259,53 @@ vulkan_renderer_attach_shm(struct weston_surface *surface, struct weston_buffer
|
|||
|
||||
int bpp = buffer->pixel_format->bpp;
|
||||
|
||||
num_planes = pixel_format_get_plane_count(buffer->pixel_format);
|
||||
|
||||
if (num_planes > 1) {
|
||||
if (pixel_format_is_opaque(buffer->pixel_format))
|
||||
pipeline_variant = PIPELINE_VARIANT_RGBX;
|
||||
else
|
||||
pipeline_variant = PIPELINE_VARIANT_RGBA;
|
||||
|
||||
vb = xzalloc(sizeof(*vb));
|
||||
vb->vr = vr;
|
||||
|
||||
wl_list_init(&vb->destroy_listener.link);
|
||||
pixman_region32_init(&vb->texture_damage);
|
||||
|
||||
vb->pipeline_variant = pipeline_variant;
|
||||
ARRAY_COPY(vb->offset, offset);
|
||||
ARRAY_COPY(vb->vulkan_format, vulkan_format);
|
||||
vb->needs_full_upload = true;
|
||||
vb->num_textures = num_planes;
|
||||
|
||||
vs->buffer = vb;
|
||||
vs->surface = surface;
|
||||
|
||||
if (buffer->pixel_format->color_model == COLOR_MODEL_YUV &&
|
||||
vulkan_device_has(vr, EXTENSION_KHR_SAMPLER_YCBCR_CONVERSION))
|
||||
create_texture_ycbcr_conv(vr, buffer->pixel_format->vulkan_format, &vb->ycbcr_conv);
|
||||
|
||||
create_image_nv12(vr, buffer->width, buffer->height, &vb->texture,
|
||||
buffer->pixel_format->vulkan_format, VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
|
||||
// TODO
|
||||
if (buffer->pixel_format->color_model == COLOR_MODEL_YUV &&
|
||||
vulkan_device_has(vr, EXTENSION_KHR_SAMPLER_YCBCR_CONVERSION))
|
||||
create_image_view(vr->dev, vb->texture.image, buffer->pixel_format->vulkan_format, &vb->ycbcr_conv, &vb->texture.image_view);
|
||||
|
||||
create_texture_sampler(vr, &vb->sampler_nearest, &vb->ycbcr_conv, VK_FILTER_NEAREST);
|
||||
create_texture_sampler(vr, &vb->sampler_linear, &vb->ycbcr_conv, VK_FILTER_LINEAR);
|
||||
|
||||
create_vs_ubo_buffer(vr, &vb->vs_ubo_buffer, &vb->vs_ubo_memory, &vb->vs_ubo_map);
|
||||
create_fs_ubo_buffer(vr, &vb->fs_ubo_buffer, &vb->fs_ubo_memory, &vb->fs_ubo_map);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
assert(pixel_format_get_plane_count(buffer->pixel_format) == 1);
|
||||
num_planes = 1;
|
||||
|
||||
if (pixel_format_is_opaque(buffer->pixel_format))
|
||||
pipeline_variant = PIPELINE_VARIANT_RGBX;
|
||||
|
|
@ -4358,6 +4656,11 @@ vulkan_renderer_setup_device_extensions(struct vulkan_renderer *vr)
|
|||
if (vulkan_device_has(vr, EXTENSION_KHR_SAMPLER_YCBCR_CONVERSION)) {
|
||||
load_device_proc(vr, "vkCreateSamplerYcbcrConversionKHR", &vr->create_sampler_ycbcr_conversion);
|
||||
}
|
||||
|
||||
// VK_KHR_bind_memory2
|
||||
if (vulkan_device_has(vr, EXTENSION_KHR_BIND_MEMORY_2)) {
|
||||
load_device_proc(vr, "vkBindImageMemory2KHR", &vr->bind_image_memory2);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue