gstreamer/subprojects/gst-plugins-bad/gst-libs/gst/vulkan/gstvkfullscreenquad.c
Víctor Manuel Jáquez Leal e177080bec vulkan: number of memories in buffer rather than number of planes
New vulkan formats don't match the number of planes with the number of memories
attached to the buffer. This patch changes the pattern of using planes for
traverse the memories with the number of attached memories.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4351>
2023-05-19 04:26:29 +00:00

1669 lines
52 KiB
C

/*
* GStreamer Plugins Vulkan
* Copyright (C) 2019 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstvkfullscreenquad.h"
/**
* SECTION:vkfullscreenquad
* @title: GstVulkanFullScreenQuad
* @short_description: Vulkan full screen quad
* @see_also: #GstVulkanDevice, #GstVulkanImageMemory
*
* A #GstVulkanFullScreenQuad is a helper object for rendering a single input
* image to an output #GstBuffer
*/
#define GST_CAT_DEFAULT gst_vulkan_full_screen_quad_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
/* XXX: privatise this on moving to lib */
struct Vertex
{
float x, y, z;
float s, t;
};
struct _GstVulkanFullScreenQuadPrivate
{
GstBuffer *inbuf;
GstBuffer *outbuf;
GstMemory *vertices;
GstMemory *indices;
gsize n_indices;
GstMemory *uniforms;
gsize uniform_size;
GstVulkanHandle *vert;
GstVulkanHandle *frag;
VkBool32 blend_enable;
VkBlendFactor src_blend_factor;
VkBlendFactor src_alpha_blend_factor;
VkBlendFactor dst_blend_factor;
VkBlendFactor dst_alpha_blend_factor;
VkBlendOp colour_blend_op;
VkBlendOp alpha_blend_op;
gboolean enable_clear;
};
G_DEFINE_TYPE_WITH_CODE (GstVulkanFullScreenQuad, gst_vulkan_full_screen_quad,
GST_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (gst_vulkan_full_screen_quad_debug,
"vulkanfullscreenquad", 0, "vulkan fullscreen quad render");
G_ADD_PRIVATE (GstVulkanFullScreenQuad));
#define GET_PRIV(self) gst_vulkan_full_screen_quad_get_instance_private (self)
struct Vertex vertices[] = {
{-1.0f, -1.0f, 0.0f, 0.0f, 0.0f},
{1.0f, -1.0f, 0.0f, 1.0f, 0.0f},
{1.0f, 1.0f, 0.0f, 1.0f, 1.0f},
{-1.0f, 1.0f, 0.0f, 0.0f, 1.0f},
};
gushort indices[] = {
0, 1, 2, 0, 2, 3,
};
static gboolean
create_sampler (GstVulkanFullScreenQuad * self, GError ** error)
{
/* *INDENT-OFF* */
VkSamplerCreateInfo samplerInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_LINEAR,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.anisotropyEnable = VK_FALSE,
.maxAnisotropy = 1,
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
.compareEnable = VK_FALSE,
.compareOp = VK_COMPARE_OP_ALWAYS,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.mipLodBias = 0.0f,
.minLod = 0.0f,
.maxLod = 0.0f
};
/* *INDENT-ON* */
VkSampler sampler;
VkResult err;
err =
vkCreateSampler (self->queue->device->device, &samplerInfo, NULL,
&sampler);
if (gst_vulkan_error_to_g_error (err, error, "vkCreateSampler") < 0) {
return FALSE;
}
self->sampler = gst_vulkan_handle_new_wrapped (self->queue->device,
GST_VULKAN_HANDLE_TYPE_SAMPLER, (GstVulkanHandleTypedef) sampler,
gst_vulkan_handle_free_sampler, NULL);
return TRUE;
}
static GstVulkanDescriptorSet *
get_and_update_descriptor_set (GstVulkanFullScreenQuad * self,
GstVulkanImageView ** views, guint n_mems, GError ** error)
{
GstVulkanFullScreenQuadPrivate *priv = GET_PRIV (self);
GstVulkanDescriptorSet *set;
if (!self->sampler)
if (!create_sampler (self, error))
return NULL;
if (!(set =
gst_vulkan_descriptor_cache_acquire (self->descriptor_cache, error)))
return NULL;
{
VkWriteDescriptorSet writes[GST_VIDEO_MAX_PLANES + 1];
VkDescriptorImageInfo image_info[GST_VIDEO_MAX_PLANES];
VkDescriptorBufferInfo buffer_info;
int write_n = 0;
int i;
/* *INDENT-OFF* */
if (priv->uniforms) {
buffer_info = (VkDescriptorBufferInfo) {
.buffer = ((GstVulkanBufferMemory *) priv->uniforms)->buffer,
.offset = 0,
.range = priv->uniform_size
};
writes[write_n++] = (VkWriteDescriptorSet) {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = NULL,
.dstSet = set->set,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.pBufferInfo = &buffer_info
};
}
for (i = 0; i < n_mems; i++) {
image_info[i] = (VkDescriptorImageInfo) {
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = views[i]->view,
.sampler = (VkSampler) self->sampler->handle
};
writes[write_n++] = (VkWriteDescriptorSet) {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = NULL,
.dstSet = set->set,
.dstBinding = i + 1,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.pImageInfo = &image_info[i]
};
}
/* *INDENT-ON* */
vkUpdateDescriptorSets (self->queue->device->device, write_n, writes, 0,
NULL);
}
return set;
}
static gboolean
create_descriptor_set_layout (GstVulkanFullScreenQuad * self, GError ** error)
{
GstVulkanFullScreenQuadPrivate *priv = GET_PRIV (self);
VkDescriptorSetLayoutBinding bindings[GST_VIDEO_MAX_PLANES + 1] = { {0,} };
VkDescriptorSetLayoutCreateInfo layout_info;
VkDescriptorSetLayout descriptor_set_layout;
int descriptor_n = 0;
VkResult err;
int i, n_mems;
/* *INDENT-OFF* */
bindings[descriptor_n++] = (VkDescriptorSetLayoutBinding) {
.binding = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pImmutableSamplers = NULL,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT
};
n_mems = gst_buffer_n_memory (priv->inbuf);
for (i = 0; i < n_mems; i++) {
bindings[descriptor_n++] = (VkDescriptorSetLayoutBinding) {
.binding = i+1,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImmutableSamplers = NULL,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
};
};
layout_info = (VkDescriptorSetLayoutCreateInfo) {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = NULL,
.bindingCount = descriptor_n,
.pBindings = bindings
};
/* *INDENT-ON* */
err =
vkCreateDescriptorSetLayout (self->queue->device->device, &layout_info,
NULL, &descriptor_set_layout);
if (gst_vulkan_error_to_g_error (err, error,
"vkCreateDescriptorSetLayout") < 0) {
return FALSE;
}
self->descriptor_set_layout =
gst_vulkan_handle_new_wrapped (self->queue->device,
GST_VULKAN_HANDLE_TYPE_DESCRIPTOR_SET_LAYOUT,
(GstVulkanHandleTypedef) descriptor_set_layout,
gst_vulkan_handle_free_descriptor_set_layout, NULL);
return TRUE;
}
static gboolean
create_pipeline_layout (GstVulkanFullScreenQuad * self, GError ** error)
{
VkPipelineLayoutCreateInfo pipeline_layout_info;
VkPipelineLayout pipeline_layout;
VkResult err;
if (!self->descriptor_set_layout)
if (!create_descriptor_set_layout (self, error))
return FALSE;
/* *INDENT-OFF* */
pipeline_layout_info = (VkPipelineLayoutCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = NULL,
.setLayoutCount = 1,
.pSetLayouts = (VkDescriptorSetLayout *) &self->descriptor_set_layout->handle,
.pushConstantRangeCount = 0,
.pPushConstantRanges = NULL,
};
/* *INDENT-ON* */
err =
vkCreatePipelineLayout (self->queue->device->device,
&pipeline_layout_info, NULL, &pipeline_layout);
if (gst_vulkan_error_to_g_error (err, error, "vkCreatePipelineLayout") < 0) {
return FALSE;
}
self->pipeline_layout = gst_vulkan_handle_new_wrapped (self->queue->device,
GST_VULKAN_HANDLE_TYPE_PIPELINE_LAYOUT,
(GstVulkanHandleTypedef) pipeline_layout,
gst_vulkan_handle_free_pipeline_layout, NULL);
return TRUE;
}
static gboolean
create_render_pass (GstVulkanFullScreenQuad * self, GError ** error)
{
GstVulkanFullScreenQuadPrivate *priv = GET_PRIV (self);
VkAttachmentDescription color_attachments[GST_VIDEO_MAX_PLANES];
VkAttachmentReference color_attachment_refs[GST_VIDEO_MAX_PLANES];
VkRenderPassCreateInfo render_pass_info;
VkSubpassDescription subpass;
VkRenderPass render_pass;
VkResult err;
int i, n_mems;
n_mems = gst_buffer_n_memory (priv->outbuf);
for (i = 0; i < n_mems; i++) {
/* *INDENT-OFF* */
color_attachments[i] = (VkAttachmentDescription) {
.format = gst_vulkan_format_from_video_info (&self->out_info, i),
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = priv->enable_clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
/* FIXME: share this between elements to avoid pipeline barriers */
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
};
color_attachment_refs[i] = (VkAttachmentReference) {
.attachment = i,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
};
/* *INDENT-ON* */
}
/* *INDENT-OFF* */
subpass = (VkSubpassDescription) {
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.colorAttachmentCount = n_mems,
.pColorAttachments = color_attachment_refs
};
render_pass_info = (VkRenderPassCreateInfo) {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.pNext = NULL,
.attachmentCount = n_mems,
.pAttachments = color_attachments,
.subpassCount = 1,
.pSubpasses = &subpass
};
/* *INDENT-ON* */
err =
vkCreateRenderPass (self->queue->device->device, &render_pass_info, NULL,
&render_pass);
if (gst_vulkan_error_to_g_error (err, error, "vkCreateRenderPass") < 0) {
return FALSE;
}
self->render_pass = gst_vulkan_handle_new_wrapped (self->queue->device,
GST_VULKAN_HANDLE_TYPE_RENDER_PASS,
(GstVulkanHandleTypedef) render_pass,
gst_vulkan_handle_free_render_pass, NULL);
return TRUE;
}
static gboolean
create_pipeline (GstVulkanFullScreenQuad * self, GError ** error)
{
GstVulkanFullScreenQuadPrivate *priv = GET_PRIV (self);
VkVertexInputBindingDescription vertex_binding;
VkVertexInputAttributeDescription attribute_descriptions[2];
VkPipelineShaderStageCreateInfo shader_create_info[2];
VkPipelineVertexInputStateCreateInfo vertex_input_info;
VkPipelineInputAssemblyStateCreateInfo input_assembly;
VkPipelineViewportStateCreateInfo viewport_state;
VkPipelineRasterizationStateCreateInfo rasterizer;
VkPipelineMultisampleStateCreateInfo multisampling;
VkPipelineColorBlendAttachmentState
color_blend_attachments[GST_VIDEO_MAX_PLANES];
VkPipelineColorBlendStateCreateInfo color_blending;
VkGraphicsPipelineCreateInfo pipeline_create_info;
VkPipeline pipeline;
VkResult err;
if (!priv->vert || !priv->frag) {
g_set_error_literal (error, GST_VULKAN_ERROR,
VK_ERROR_INITIALIZATION_FAILED, "Missing shader information");
return FALSE;
}
if (!self->pipeline_layout)
if (!create_pipeline_layout (self, error))
return FALSE;
if (!self->render_pass)
if (!create_render_pass (self, error))
return FALSE;
/* *INDENT-OFF* */
shader_create_info[0] = (VkPipelineShaderStageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = NULL,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = (VkShaderModule) priv->vert->handle,
.pName = "main"
};
shader_create_info[1] = (VkPipelineShaderStageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = NULL,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = (VkShaderModule) priv->frag->handle,
.pName = "main"
};
/* *INDENT-OFF* */
vertex_binding = (VkVertexInputBindingDescription) {
.binding = 0,
.stride = sizeof (struct Vertex),
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX
};
attribute_descriptions[0] = (VkVertexInputAttributeDescription) {
.binding = 0,
.location = 0,
.format = VK_FORMAT_R32G32B32_SFLOAT,
.offset = G_STRUCT_OFFSET (struct Vertex, x)
};
attribute_descriptions[1] = (VkVertexInputAttributeDescription) {
.binding = 0,
.location = 1,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = G_STRUCT_OFFSET (struct Vertex, s)
};
vertex_input_info = (VkPipelineVertexInputStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pNext = NULL,
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &vertex_binding,
.vertexAttributeDescriptionCount = 2,
.pVertexAttributeDescriptions = attribute_descriptions,
};
input_assembly = (VkPipelineInputAssemblyStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.pNext = NULL,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
.primitiveRestartEnable = VK_FALSE
};
viewport_state = (VkPipelineViewportStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pNext = NULL,
.viewportCount = 1,
.pViewports = &(VkViewport) {
.x = 0.0f,
.y = 0.0f,
.width = (float) GST_VIDEO_INFO_WIDTH (&self->out_info),
.height = (float) GST_VIDEO_INFO_HEIGHT (&self->out_info),
.minDepth = 0.0f,
.maxDepth = 1.0f
},
.scissorCount = 1,
.pScissors = &(VkRect2D) {
.offset = { 0, 0 },
.extent = {
GST_VIDEO_INFO_WIDTH (&self->out_info),
GST_VIDEO_INFO_HEIGHT (&self->out_info)
}
}
};
rasterizer = (VkPipelineRasterizationStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pNext = NULL,
.depthClampEnable = VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.lineWidth = 1.0f,
.cullMode = VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_CLOCKWISE,
.depthBiasEnable = VK_FALSE
};
multisampling = (VkPipelineMultisampleStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pNext = NULL,
.sampleShadingEnable = VK_FALSE,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
};
color_blend_attachments[0] = (VkPipelineColorBlendAttachmentState) {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
.srcColorBlendFactor = priv->src_blend_factor,
.dstColorBlendFactor = priv->dst_blend_factor,
.colorBlendOp = priv->colour_blend_op,
.srcAlphaBlendFactor = priv->src_alpha_blend_factor,
.dstAlphaBlendFactor = priv->dst_alpha_blend_factor,
.alphaBlendOp = priv->alpha_blend_op,
.blendEnable = priv->blend_enable
};
color_blend_attachments[1] = (VkPipelineColorBlendAttachmentState) {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
.srcColorBlendFactor = priv->src_blend_factor,
.dstColorBlendFactor = priv->dst_blend_factor,
.colorBlendOp = priv->colour_blend_op,
.srcAlphaBlendFactor = priv->src_alpha_blend_factor,
.dstAlphaBlendFactor = priv->dst_alpha_blend_factor,
.alphaBlendOp = priv->alpha_blend_op,
.blendEnable = priv->blend_enable
};
color_blend_attachments[2] = (VkPipelineColorBlendAttachmentState) {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
.srcColorBlendFactor = priv->src_blend_factor,
.dstColorBlendFactor = priv->dst_blend_factor,
.colorBlendOp = priv->colour_blend_op,
.srcAlphaBlendFactor = priv->src_alpha_blend_factor,
.dstAlphaBlendFactor = priv->dst_alpha_blend_factor,
.alphaBlendOp = priv->alpha_blend_op,
.blendEnable = priv->blend_enable
};
color_blend_attachments[3] = (VkPipelineColorBlendAttachmentState) {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
.srcColorBlendFactor = priv->src_blend_factor,
.dstColorBlendFactor = priv->dst_blend_factor,
.colorBlendOp = priv->colour_blend_op,
.srcAlphaBlendFactor = priv->src_alpha_blend_factor,
.dstAlphaBlendFactor = priv->dst_alpha_blend_factor,
.alphaBlendOp = priv->alpha_blend_op,
.blendEnable = priv->blend_enable
};
color_blending = (VkPipelineColorBlendStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.pNext = NULL,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = gst_buffer_n_memory (priv->outbuf),
.pAttachments = color_blend_attachments,
.blendConstants = { 0.0f, 0.0f, 0.0f, 0.0f }
};
pipeline_create_info = (VkGraphicsPipelineCreateInfo) {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = NULL,
.stageCount = 2,
.pStages = shader_create_info,
.pVertexInputState = &vertex_input_info,
.pInputAssemblyState = &input_assembly,
.pViewportState = &viewport_state,
.pRasterizationState = &rasterizer,
.pMultisampleState = &multisampling,
.pColorBlendState = &color_blending,
.layout = (VkPipelineLayout) self->pipeline_layout->handle,
.renderPass = (VkRenderPass) self->render_pass->handle,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE
};
/* *INDENT-ON* */
err =
vkCreateGraphicsPipelines (self->queue->device->device, VK_NULL_HANDLE, 1,
&pipeline_create_info, NULL, &pipeline);
if (gst_vulkan_error_to_g_error (err, error, "vkCreateGraphicsPipelines") < 0) {
return FALSE;
}
self->graphics_pipeline = gst_vulkan_handle_new_wrapped (self->queue->device,
GST_VULKAN_HANDLE_TYPE_PIPELINE, (GstVulkanHandleTypedef) pipeline,
gst_vulkan_handle_free_pipeline, NULL);
return TRUE;
}
static gboolean
create_descriptor_pool (GstVulkanFullScreenQuad * self, GError ** error)
{
GstVulkanFullScreenQuadPrivate *priv = GET_PRIV (self);
VkDescriptorPoolCreateInfo pool_info;
gsize max_sets = 32; /* FIXME: don't hardcode this! */
guint n_pools = 1;
VkDescriptorPoolSize pool_sizes[2];
VkDescriptorPool pool;
GstVulkanDescriptorPool *ret;
VkResult err;
/* *INDENT-OFF* */
pool_sizes[0] = (VkDescriptorPoolSize) {
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = max_sets * gst_buffer_n_memory (priv->inbuf),
};
if (priv->uniforms) {
pool_sizes[1] = (VkDescriptorPoolSize) {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = max_sets
};
n_pools++;
}
pool_info = (VkDescriptorPoolCreateInfo) {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = NULL,
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
.poolSizeCount = n_pools,
.pPoolSizes = pool_sizes,
.maxSets = max_sets
};
/* *INDENT-ON* */
err =
vkCreateDescriptorPool (self->queue->device->device, &pool_info, NULL,
&pool);
if (gst_vulkan_error_to_g_error (err, error, "vkCreateDescriptorPool") < 0) {
return FALSE;
}
ret =
gst_vulkan_descriptor_pool_new_wrapped (self->queue->device, pool,
max_sets);
self->descriptor_cache =
gst_vulkan_descriptor_cache_new (ret, 1, &self->descriptor_set_layout);
gst_object_unref (ret);
return TRUE;
}
static gboolean
create_framebuffer (GstVulkanFullScreenQuad * self, GstVulkanImageView ** views,
guint n_mems, GError ** error)
{
VkImageView attachments[GST_VIDEO_MAX_PLANES] = { 0, };
VkFramebufferCreateInfo framebuffer_info;
VkFramebuffer framebuffer;
VkResult err;
int i;
for (i = 0; i < n_mems; i++) {
attachments[i] = views[i]->view;
}
/* *INDENT-OFF* */
framebuffer_info = (VkFramebufferCreateInfo) {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.pNext = NULL,
.renderPass = (VkRenderPass) self->render_pass->handle,
.attachmentCount = n_mems,
.pAttachments = attachments,
.width = GST_VIDEO_INFO_WIDTH (&self->out_info),
.height = GST_VIDEO_INFO_HEIGHT (&self->out_info),
.layers = 1
};
/* *INDENT-ON* */
err =
vkCreateFramebuffer (self->queue->device->device, &framebuffer_info, NULL,
&framebuffer);
if (gst_vulkan_error_to_g_error (err, error, "vkCreateFramebuffer") < 0) {
return FALSE;
}
self->framebuffer = gst_vulkan_handle_new_wrapped (self->queue->device,
GST_VULKAN_HANDLE_TYPE_FRAMEBUFFER, (GstVulkanHandleTypedef) framebuffer,
gst_vulkan_handle_free_framebuffer, NULL);
return TRUE;
}
#define LAST_FENCE_OR_ALWAYS_SIGNALLED(self,device) \
self->last_fence ? gst_vulkan_fence_ref (self->last_fence) : gst_vulkan_fence_new_always_signalled (device)
GstVulkanFence *
gst_vulkan_full_screen_quad_get_last_fence (GstVulkanFullScreenQuad * self)
{
g_return_val_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self), NULL);
return LAST_FENCE_OR_ALWAYS_SIGNALLED (self, self->queue->device);
}
#define clear_field(field,type,trash_free_func) \
static void \
G_PASTE(clear_,field) (GstVulkanFullScreenQuad * self) \
{ \
GstVulkanFence *last_fence = \
LAST_FENCE_OR_ALWAYS_SIGNALLED (self, self->queue->device); \
\
if (self->field) \
gst_vulkan_trash_list_add (self->trash_list, \
gst_vulkan_trash_list_acquire (self->trash_list, last_fence, \
trash_free_func, (type) self->field)); \
self->field = NULL; \
\
gst_vulkan_fence_unref (last_fence); \
}
#define clear_field_mini_object(field) clear_field (field,GstMiniObject *,gst_vulkan_trash_mini_object_unref);
#define clear_field_object(field) clear_field (field,GstObject *,gst_vulkan_trash_object_unref);
clear_field_mini_object (descriptor_set);
clear_field_mini_object (framebuffer);
clear_field_mini_object (sampler);
clear_field_mini_object (pipeline_layout);
clear_field_mini_object (graphics_pipeline);
clear_field_mini_object (descriptor_set_layout);
clear_field_object (cmd_pool);
clear_field_object (descriptor_cache);
static void
clear_shaders (GstVulkanFullScreenQuad * self)
{
GstVulkanFullScreenQuadPrivate *priv = GET_PRIV (self);
GstVulkanFence *last_fence =
LAST_FENCE_OR_ALWAYS_SIGNALLED (self, self->queue->device);
if (priv->vert)
gst_vulkan_trash_list_add (self->trash_list,
gst_vulkan_trash_list_acquire (self->trash_list, last_fence,
gst_vulkan_trash_mini_object_unref, (GstMiniObject *) priv->vert));
priv->vert = NULL;
if (priv->frag)
gst_vulkan_trash_list_add (self->trash_list,
gst_vulkan_trash_list_acquire (self->trash_list, last_fence,
gst_vulkan_trash_mini_object_unref, (GstMiniObject *) priv->frag));
priv->frag = NULL;
gst_vulkan_fence_unref (last_fence);
}
static void
clear_uniform_data (GstVulkanFullScreenQuad * self)
{
GstVulkanFullScreenQuadPrivate *priv = GET_PRIV (self);
GstVulkanFence *last_fence =
LAST_FENCE_OR_ALWAYS_SIGNALLED (self, self->queue->device);
if (priv->uniforms)
gst_vulkan_trash_list_add (self->trash_list,
gst_vulkan_trash_list_acquire (self->trash_list, last_fence,
gst_vulkan_trash_mini_object_unref,
(GstMiniObject *) priv->uniforms));
priv->uniforms = NULL;
priv->uniform_size = 0;
gst_vulkan_fence_unref (last_fence);
}
static void
clear_index_data (GstVulkanFullScreenQuad * self)
{
GstVulkanFullScreenQuadPrivate *priv = GET_PRIV (self);
GstVulkanFence *last_fence =
LAST_FENCE_OR_ALWAYS_SIGNALLED (self, self->queue->device);
if (priv->indices)
gst_vulkan_trash_list_add (self->trash_list,
gst_vulkan_trash_list_acquire (self->trash_list, last_fence,
gst_vulkan_trash_mini_object_unref,
(GstMiniObject *) priv->indices));
priv->indices = NULL;
priv->n_indices = 0;
gst_vulkan_fence_unref (last_fence);
}
static void
clear_vertex_data (GstVulkanFullScreenQuad * self)
{
GstVulkanFullScreenQuadPrivate *priv = GET_PRIV (self);
GstVulkanFence *last_fence =
LAST_FENCE_OR_ALWAYS_SIGNALLED (self, self->queue->device);
if (priv->vertices)
gst_vulkan_trash_list_add (self->trash_list,
gst_vulkan_trash_list_acquire (self->trash_list, last_fence,
gst_vulkan_trash_mini_object_unref,
(GstMiniObject *) priv->vertices));
priv->vertices = NULL;
gst_vulkan_fence_unref (last_fence);
}
static void
clear_render_pass (GstVulkanFullScreenQuad * self)
{
GstVulkanFence *last_fence =
LAST_FENCE_OR_ALWAYS_SIGNALLED (self, self->queue->device);
if (self->render_pass)
gst_vulkan_trash_list_add (self->trash_list,
gst_vulkan_trash_list_acquire (self->trash_list, last_fence,
gst_vulkan_trash_mini_object_unref,
(GstMiniObject *) self->render_pass));
self->render_pass = NULL;
gst_vulkan_fence_unref (last_fence);
}
static void
destroy_pipeline (GstVulkanFullScreenQuad * self)
{
GstVulkanFence *last_fence =
LAST_FENCE_OR_ALWAYS_SIGNALLED (self, self->queue->device);
clear_render_pass (self);
clear_pipeline_layout (self);
clear_graphics_pipeline (self);
clear_descriptor_set_layout (self);
gst_vulkan_fence_unref (last_fence);
gst_vulkan_trash_list_gc (self->trash_list);
}
void
gst_vulkan_full_screen_quad_init (GstVulkanFullScreenQuad * self)
{
GstVulkanFullScreenQuadPrivate *priv = GET_PRIV (self);
self->trash_list = gst_vulkan_trash_fence_list_new ();
priv->src_blend_factor = VK_BLEND_FACTOR_ONE;
priv->src_alpha_blend_factor = VK_BLEND_FACTOR_ONE;
priv->dst_blend_factor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
priv->dst_alpha_blend_factor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
priv->colour_blend_op = VK_BLEND_OP_ADD;
priv->alpha_blend_op = VK_BLEND_OP_ADD;
priv->enable_clear = TRUE;
}
/**
* gst_vulkan_full_screen_quad_new:
* @queue: a #GstVulkanQueue
*
* Returns: (transfer full): a new #GstVulkanFullScreenQuad
*
* Since: 1.18
*/
GstVulkanFullScreenQuad *
gst_vulkan_full_screen_quad_new (GstVulkanQueue * queue)
{
GstVulkanFullScreenQuad *self;
GError *error = NULL;
g_return_val_if_fail (GST_IS_VULKAN_QUEUE (queue), NULL);
self = g_object_new (GST_TYPE_VULKAN_FULL_SCREEN_QUAD, NULL);
self->queue = gst_object_ref (queue);
self->cmd_pool = gst_vulkan_queue_create_command_pool (queue, &error);
if (!self->cmd_pool)
GST_WARNING_OBJECT (self, "Failed to create command pool: %s",
error->message);
gst_object_ref_sink (self);
return self;
}
static void
gst_vulkan_full_screen_quad_finalize (GObject * object)
{
GstVulkanFullScreenQuad *self = GST_VULKAN_FULL_SCREEN_QUAD (object);
GstVulkanFullScreenQuadPrivate *priv = GET_PRIV (self);
destroy_pipeline (self);
clear_cmd_pool (self);
clear_sampler (self);
clear_framebuffer (self);
clear_descriptor_set (self);
clear_descriptor_cache (self);
clear_shaders (self);
clear_uniform_data (self);
clear_index_data (self);
clear_vertex_data (self);
gst_vulkan_trash_list_wait (self->trash_list, -1);
gst_vulkan_trash_list_gc (self->trash_list);
gst_clear_object (&self->trash_list);
gst_clear_mini_object (((GstMiniObject **) & self->last_fence));
gst_clear_object (&self->queue);
gst_clear_buffer (&priv->inbuf);
gst_clear_buffer (&priv->outbuf);
G_OBJECT_CLASS (gst_vulkan_full_screen_quad_parent_class)->finalize (object);
}
static void
gst_vulkan_full_screen_quad_class_init (GstVulkanFullScreenQuadClass * klass)
{
GObjectClass *obj_class = G_OBJECT_CLASS (klass);
obj_class->finalize = gst_vulkan_full_screen_quad_finalize;
}
/**
* gst_vulkan_full_screen_quad_set_info:
* @self: the #GstVulkanFullScreenQuad
* @in_info: the input #GstVideoInfo to set
* @out_info: the output #GstVideoInfo to set
*
* Returns: whether the information could be successfully set
*
* Since: 1.18
*/
gboolean
gst_vulkan_full_screen_quad_set_info (GstVulkanFullScreenQuad * self,
GstVideoInfo * in_info, GstVideoInfo * out_info)
{
self->out_info = *out_info;
self->in_info = *in_info;
destroy_pipeline (self);
clear_framebuffer (self);
clear_descriptor_set (self);
clear_descriptor_cache (self);
clear_uniform_data (self);
return TRUE;
}
/**
* gst_vulkan_full_screen_quad_set_input_buffer:
* @self: the #GstVulkanFullScreenQuad
* @buffer: the input #GstBuffer to set
* @error: #GError to fill on failure
*
* Returns: whether the input buffer could be changed
*
* Since: 1.18
*/
gboolean
gst_vulkan_full_screen_quad_set_input_buffer (GstVulkanFullScreenQuad * self,
GstBuffer * buffer, GError ** error)
{
GstVulkanFullScreenQuadPrivate *priv;
g_return_val_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self), FALSE);
priv = GET_PRIV (self);
gst_buffer_replace (&priv->inbuf, buffer);
clear_descriptor_set (self);
return TRUE;
}
/**
* gst_vulkan_full_screen_quad_set_output_buffer:
* @self: the #GstVulkanFullScreenQuad
* @buffer: the output #GstBuffer to set
* @error: #GError to fill on failure
*
* Returns: whether the input buffer could be changed
*
* Since: 1.18
*/
gboolean
gst_vulkan_full_screen_quad_set_output_buffer (GstVulkanFullScreenQuad * self,
GstBuffer * buffer, GError ** error)
{
GstVulkanFullScreenQuadPrivate *priv;
g_return_val_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self), FALSE);
priv = GET_PRIV (self);
gst_buffer_replace (&priv->outbuf, buffer);
clear_framebuffer (self);
return TRUE;
}
/**
* gst_vulkan_full_screen_quad_set_shaders:
* @self: the #GstVulkanFullScreenQuad
* @vert: the vertex shader to set
* @frag: the fragment shader to set
*
* Returns: whether the shaders could be set
*
* Since: 1.18
*/
gboolean
gst_vulkan_full_screen_quad_set_shaders (GstVulkanFullScreenQuad * self,
GstVulkanHandle * vert, GstVulkanHandle * frag)
{
GstVulkanFullScreenQuadPrivate *priv;
g_return_val_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self), FALSE);
g_return_val_if_fail (vert != NULL, FALSE);
g_return_val_if_fail (vert->type == GST_VULKAN_HANDLE_TYPE_SHADER, FALSE);
g_return_val_if_fail (frag != NULL, FALSE);
g_return_val_if_fail (frag->type == GST_VULKAN_HANDLE_TYPE_SHADER, FALSE);
priv = GET_PRIV (self);
clear_shaders (self);
destroy_pipeline (self);
priv->vert = gst_vulkan_handle_ref (vert);
priv->frag = gst_vulkan_handle_ref (frag);
return TRUE;
}
/**
* gst_vulkan_full_screen_quad_set_uniform_buffer:
* @self: the #GstVulkanFullScreenQuad
* @uniforms: the uniform data to set. Must be a #GstVulkanBufferMemory
* @error: a #GError to fill on failure
*
* Returns: whether the shaders could be set
*
* Since: 1.18
*/
gboolean
gst_vulkan_full_screen_quad_set_uniform_buffer (GstVulkanFullScreenQuad * self,
GstMemory * uniforms, GError ** error)
{
GstVulkanFullScreenQuadPrivate *priv;
g_return_val_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self), FALSE);
g_return_val_if_fail (uniforms == NULL
|| gst_is_vulkan_buffer_memory (uniforms), FALSE);
priv = GET_PRIV (self);
clear_uniform_data (self);
if (uniforms) {
priv->uniforms = gst_memory_ref (uniforms);
priv->uniform_size = gst_memory_get_sizes (uniforms, NULL, NULL);
}
return TRUE;
}
/**
* gst_vulkan_full_screen_quad_set_index_buffer:
* @self: the #GstVulkanFullScreenQuad
* @indices: the index data. Must be a #GstVulkanBufferMemory
* @n_indices: number of indices in @indices
* @error: #GError to fill on failure
*
* See also gst_vulkan_full_screen_quad_set_vertex_buffer()
*
* Returns: whether the index data could be set
*
* Since: 1.18
*/
gboolean
gst_vulkan_full_screen_quad_set_index_buffer (GstVulkanFullScreenQuad * self,
GstMemory * indices, gsize n_indices, GError ** error)
{
GstVulkanFullScreenQuadPrivate *priv;
g_return_val_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self), FALSE);
g_return_val_if_fail (indices == NULL
|| gst_is_vulkan_buffer_memory (indices), FALSE);
priv = GET_PRIV (self);
clear_index_data (self);
if (indices) {
priv->indices = gst_memory_ref (indices);
priv->n_indices = n_indices;
}
return TRUE;
}
/**
* gst_vulkan_full_screen_quad_set_vertex_buffer:
* @self: the #GstVulkanFullScreenQuad
* @vertices: the vertex data. Must be a #GstVulkanBufferMemory
* @error: #GError to fill on failure
*
* Returns: whether the index data could be set
*
* Since: 1.18
*/
gboolean
gst_vulkan_full_screen_quad_set_vertex_buffer (GstVulkanFullScreenQuad * self,
GstMemory * vertices, GError ** error)
{
GstVulkanFullScreenQuadPrivate *priv;
g_return_val_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self), FALSE);
g_return_val_if_fail (vertices == NULL
|| gst_is_vulkan_buffer_memory (vertices), FALSE);
priv = GET_PRIV (self);
clear_vertex_data (self);
if (vertices) {
priv->vertices = gst_memory_ref (vertices);
}
return TRUE;
}
static GstVulkanImageMemory *
peek_image_from_buffer (GstBuffer * buffer, guint i)
{
GstMemory *mem = gst_buffer_peek_memory (buffer, i);
g_return_val_if_fail (gst_is_vulkan_image_memory (mem), NULL);
return (GstVulkanImageMemory *) mem;
}
static gboolean
ensure_vertex_data (GstVulkanFullScreenQuad * self, GError ** error)
{
GstVulkanFullScreenQuadPrivate *priv = GET_PRIV (self);
GstMapInfo map_info;
if (!priv->vertices) {
priv->vertices = gst_vulkan_buffer_memory_alloc (self->queue->device,
sizeof (vertices), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
if (!gst_memory_map (priv->vertices, &map_info, GST_MAP_WRITE)) {
g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_MEMORY_MAP_FAILED,
"Failed to map memory");
goto failure;
}
memcpy (map_info.data, vertices, map_info.size);
gst_memory_unmap (priv->vertices, &map_info);
}
if (!priv->indices) {
priv->indices = gst_vulkan_buffer_memory_alloc (self->queue->device,
sizeof (indices), VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
if (!gst_memory_map (priv->indices, &map_info, GST_MAP_WRITE)) {
g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_MEMORY_MAP_FAILED,
"Failed to map memory");
goto failure;
}
memcpy (map_info.data, indices, map_info.size);
gst_memory_unmap (priv->indices, &map_info);
priv->n_indices = G_N_ELEMENTS (indices);
}
return TRUE;
failure:
if (priv->vertices)
gst_memory_unref (priv->vertices);
priv->vertices = NULL;
if (priv->indices)
gst_memory_unref (priv->indices);
priv->indices = NULL;
priv->n_indices = 0;
return FALSE;
}
/**
* gst_vulkan_full_screen_quad_draw:
* @self: the #GstVulkanFullScreenQuad
* @error: a #GError filled on error
*
* Helper function for creation and submission of a command buffer that draws
* a full screen quad. If you need to add other things to the command buffer,
* create the command buffer manually and call
* gst_vulkan_full_screen_quad_prepare_draw(),
* gst_vulkan_full_screen_quad_fill_command_buffer() and
* gst_vulkan_full_screen_quad_submit() instead.
*
* Returns: whether the draw was successful
*
* Since: 1.18
*/
gboolean
gst_vulkan_full_screen_quad_draw (GstVulkanFullScreenQuad * self,
GError ** error)
{
GstVulkanCommandBuffer *cmd = NULL;
GstVulkanFence *fence = NULL;
VkResult err;
g_return_val_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self), FALSE);
fence = gst_vulkan_device_create_fence (self->queue->device, error);
if (!fence)
goto error;
if (!gst_vulkan_full_screen_quad_prepare_draw (self, fence, error))
goto error;
if (!(cmd = gst_vulkan_command_pool_create (self->cmd_pool, error)))
goto error;
{
VkCommandBufferBeginInfo cmd_buf_info = { 0, };
/* *INDENT-OFF* */
cmd_buf_info = (VkCommandBufferBeginInfo) {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.pNext = NULL,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
.pInheritanceInfo = NULL
};
/* *INDENT-ON* */
gst_vulkan_command_buffer_lock (cmd);
err = vkBeginCommandBuffer (cmd->cmd, &cmd_buf_info);
if (gst_vulkan_error_to_g_error (err, error, "vkBeginCommandBuffer") < 0)
goto unlock_error;
}
if (!gst_vulkan_full_screen_quad_fill_command_buffer (self, cmd, fence,
error))
goto unlock_error;
err = vkEndCommandBuffer (cmd->cmd);
gst_vulkan_command_buffer_unlock (cmd);
if (gst_vulkan_error_to_g_error (err, error, "vkEndCommandBuffer") < 0)
goto error;
if (!gst_vulkan_full_screen_quad_submit (self, cmd, fence, error))
goto error;
gst_vulkan_fence_unref (fence);
return TRUE;
unlock_error:
gst_vulkan_command_buffer_unlock (cmd);
error:
gst_clear_mini_object ((GstMiniObject **) & cmd);
gst_clear_mini_object ((GstMiniObject **) & fence);
return FALSE;
}
/**
* gst_vulkan_full_screen_quad_enable_blend:
* @self: the #GstVulkanFullScreenQuad
* @enable_blend: whether to enable blending
*
* Enables blending of the input image to the output image.
*
* See also: gst_vulkan_full_screen_quad_set_blend_operation() and
* gst_vulkan_full_screen_quad_set_blend_factors().
*
* Since: 1.22
*/
void
gst_vulkan_full_screen_quad_enable_blend (GstVulkanFullScreenQuad * self,
gboolean enable_blend)
{
GstVulkanFullScreenQuadPrivate *priv;
g_return_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self));
priv = GET_PRIV (self);
if (priv->blend_enable == VK_TRUE && enable_blend)
return;
if (priv->blend_enable == VK_FALSE && !enable_blend)
return;
priv->blend_enable = enable_blend ? VK_TRUE : VK_FALSE;
clear_graphics_pipeline (self);
}
/**
* gst_vulkan_full_screen_quad_set_blend_factors:
* @self: the #GstVulkanFullScreenQuad
* @src_blend_factor: the `VkBlendFactor` for the source image for the colour
* components (RGB)
* @src_alpha_blend_factor: the `VkBlendFactor` for the source image for the
* alpha component.
* @dst_blend_factor: the `VkBlendFactor` for the destination image for the
* colour components (RGB)
* @dst_alpha_blend_factor: the `VkBlendFactor` for the destination image for
* the alpha component.
*
* You need to enable blend with gst_vulkan_full_screen_quad_enable_blend().
*
* See also: gst_vulkan_full_screen_quad_set_blend_operation().
*
* Since: 1.22
*/
void
gst_vulkan_full_screen_quad_set_blend_factors (GstVulkanFullScreenQuad * self,
VkBlendFactor src_blend_factor, VkBlendFactor dst_blend_factor,
VkBlendFactor src_alpha_blend_factor, VkBlendFactor dst_alpha_blend_factor)
{
GstVulkanFullScreenQuadPrivate *priv;
g_return_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self));
priv = GET_PRIV (self);
if (priv->src_blend_factor == src_blend_factor
&& priv->src_alpha_blend_factor == src_alpha_blend_factor
&& priv->dst_blend_factor == dst_blend_factor
&& priv->dst_alpha_blend_factor == dst_alpha_blend_factor)
return;
priv->src_blend_factor = src_blend_factor;
priv->src_alpha_blend_factor = src_alpha_blend_factor;
priv->dst_blend_factor = dst_blend_factor;
priv->dst_alpha_blend_factor = dst_alpha_blend_factor;
clear_graphics_pipeline (self);
}
/**
* gst_vulkan_full_screen_quad_set_blend_operation:
* @self: the #GstVulkanFullScreenQuad
* @colour_blend_op: the `VkBlendOp` to use for blending colour (RGB) values
* @alpha_blend_op: the `VkBlendOp` to use for blending alpha values
*
* You need to enable blend with gst_vulkan_full_screen_quad_enable_blend().
*
* See also: gst_vulkan_full_screen_quad_set_blend_factors().
*
* Since: 1.22
*/
void
gst_vulkan_full_screen_quad_set_blend_operation (GstVulkanFullScreenQuad * self,
VkBlendOp colour_blend_op, VkBlendOp alpha_blend_op)
{
GstVulkanFullScreenQuadPrivate *priv;
g_return_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self));
priv = GET_PRIV (self);
priv->colour_blend_op = colour_blend_op;
priv->alpha_blend_op = alpha_blend_op;
clear_graphics_pipeline (self);
}
/**
* gst_vulkan_full_screen_quad_enable_clear:
* @self: the #GstVulkanFullScreenQuad
* @enable_clear: whether to clear the framebuffer on load
*
* Since: 1.22
*/
void
gst_vulkan_full_screen_quad_enable_clear (GstVulkanFullScreenQuad * self,
gboolean enable_clear)
{
GstVulkanFullScreenQuadPrivate *priv;
g_return_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self));
priv = GET_PRIV (self);
if (priv->enable_clear == enable_clear)
return;
priv->enable_clear = enable_clear;
clear_graphics_pipeline (self);
clear_render_pass (self);
}
/**
* gst_vulkan_full_screen_quad_prepare_draw:
* @self: the #GstVulkanFullScreenQuad
* @fence: a #GstVulkanFence that will be signalled after submission
* @error: a #GError filled on error
*
* Returns: whether the necessary information could be generated for drawing a
* frame.
*
* Since: 1.18
*/
gboolean
gst_vulkan_full_screen_quad_prepare_draw (GstVulkanFullScreenQuad * self,
GstVulkanFence * fence, GError ** error)
{
GstVulkanFullScreenQuadPrivate *priv;
GstVulkanImageView *in_views[GST_VIDEO_MAX_PLANES] = { NULL, };
GstVulkanImageView *out_views[GST_VIDEO_MAX_PLANES] = { NULL, };
int i, n_mems;
g_return_val_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self), FALSE);
g_return_val_if_fail (fence != NULL, FALSE);
priv = GET_PRIV (self);
if (!self->graphics_pipeline)
if (!create_pipeline (self, error))
return FALSE;
if (!ensure_vertex_data (self, error))
goto error;
if (!self->descriptor_cache)
if (!create_descriptor_pool (self, error))
goto error;
if (!self->descriptor_set) {
n_mems = gst_buffer_n_memory (priv->inbuf);
for (i = 0; i < n_mems; i++) {
GstVulkanImageMemory *img_mem = peek_image_from_buffer (priv->inbuf, i);
if (!gst_is_vulkan_image_memory ((GstMemory *) img_mem)) {
g_set_error_literal (error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
"Input memory must be a GstVulkanImageMemory");
goto error;
}
in_views[i] = gst_vulkan_get_or_create_image_view (img_mem);
gst_vulkan_trash_list_add (self->trash_list,
gst_vulkan_trash_list_acquire (self->trash_list, fence,
gst_vulkan_trash_mini_object_unref,
(GstMiniObject *) in_views[i]));
}
if (!(self->descriptor_set =
get_and_update_descriptor_set (self, in_views, n_mems, error)))
goto error;
}
if (!self->framebuffer) {
n_mems = gst_buffer_n_memory (priv->outbuf);
for (i = 0; i < n_mems; i++) {
GstVulkanImageMemory *img_mem = peek_image_from_buffer (priv->outbuf, i);
if (!gst_is_vulkan_image_memory ((GstMemory *) img_mem)) {
g_set_error_literal (error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
"Output memory must be a GstVulkanImageMemory");
goto error;
}
out_views[i] = gst_vulkan_get_or_create_image_view (img_mem);
gst_vulkan_trash_list_add (self->trash_list,
gst_vulkan_trash_list_acquire (self->trash_list, fence,
gst_vulkan_trash_mini_object_unref,
(GstMiniObject *) out_views[i]));
}
if (!create_framebuffer (self, out_views, n_mems, error))
goto error;
}
if (!self->cmd_pool)
if (!(self->cmd_pool =
gst_vulkan_queue_create_command_pool (self->queue, error)))
goto error;
return TRUE;
error:
for (i = 0; i < GST_VIDEO_MAX_PLANES; i++)
gst_clear_mini_object ((GstMiniObject **) & in_views[i]);
for (i = 0; i < GST_VIDEO_MAX_PLANES; i++)
gst_clear_mini_object ((GstMiniObject **) & out_views[i]);
return FALSE;
}
/**
* gst_vulkan_full_screen_quad_fill_command_buffer:
* @self: a #GstVulkanFullScreenQuad
* @cmd: the #GstVulkanCommandBuffer to fill with commands
* @error: a #GError to fill on error
*
* Returns: whether @cmd could be filled with the necessary commands
*
* Since: 1.18
*/
gboolean
gst_vulkan_full_screen_quad_fill_command_buffer (GstVulkanFullScreenQuad * self,
GstVulkanCommandBuffer * cmd, GstVulkanFence * fence, GError ** error)
{
GstVulkanFullScreenQuadPrivate *priv;
GstVulkanImageView *in_views[GST_VIDEO_MAX_PLANES] = { NULL, };
GstVulkanImageView *out_views[GST_VIDEO_MAX_PLANES] = { NULL, };
int i;
guint n_in_mems, n_out_mems;
g_return_val_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self), FALSE);
g_return_val_if_fail (cmd != NULL, FALSE);
g_return_val_if_fail (fence != NULL, FALSE);
priv = GET_PRIV (self);
n_in_mems = gst_buffer_n_memory (priv->inbuf);
for (i = 0; i < n_in_mems; i++) {
GstVulkanImageMemory *img_mem = peek_image_from_buffer (priv->inbuf, i);
if (!gst_is_vulkan_image_memory ((GstMemory *) img_mem)) {
g_set_error_literal (error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
"Input memory must be a GstVulkanImageMemory");
goto error;
}
in_views[i] = gst_vulkan_get_or_create_image_view (img_mem);
gst_vulkan_trash_list_add (self->trash_list,
gst_vulkan_trash_list_acquire (self->trash_list, fence,
gst_vulkan_trash_mini_object_unref, (GstMiniObject *) in_views[i]));
}
n_out_mems = gst_buffer_n_memory (priv->outbuf);
for (i = 0; i < n_out_mems; i++) {
GstVulkanImageMemory *img_mem = peek_image_from_buffer (priv->outbuf, i);
if (!gst_is_vulkan_image_memory ((GstMemory *) img_mem)) {
g_set_error_literal (error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
"Output memory must be a GstVulkanImageMemory");
goto error;
}
out_views[i] = gst_vulkan_get_or_create_image_view (img_mem);
gst_vulkan_trash_list_add (self->trash_list,
gst_vulkan_trash_list_acquire (self->trash_list, fence,
gst_vulkan_trash_mini_object_unref,
(GstMiniObject *) out_views[i]));
}
for (i = 0; i < n_in_mems; i++) {
/* *INDENT-OFF* */
VkImageMemoryBarrier in_image_memory_barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = NULL,
.srcAccessMask = in_views[i]->image->barrier.parent.access_flags,
.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,
.oldLayout = in_views[i]->image->barrier.image_layout,
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
/* FIXME: implement exclusive transfers */
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = in_views[i]->image->image,
.subresourceRange = in_views[i]->image->barrier.subresource_range
};
/* *INDENT-ON* */
vkCmdPipelineBarrier (cmd->cmd,
in_views[i]->image->barrier.parent.pipeline_stages,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1,
&in_image_memory_barrier);
in_views[i]->image->barrier.parent.pipeline_stages =
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
in_views[i]->image->barrier.parent.access_flags =
in_image_memory_barrier.dstAccessMask;
in_views[i]->image->barrier.image_layout =
in_image_memory_barrier.newLayout;
}
for (i = 0; i < n_out_mems; i++) {
/* *INDENT-OFF* */
VkImageMemoryBarrier out_image_memory_barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = NULL,
.srcAccessMask = out_views[i]->image->barrier.parent.access_flags,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.oldLayout = out_views[i]->image->barrier.image_layout,
.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
/* FIXME: implement exclusive transfers */
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = out_views[i]->image->image,
.subresourceRange = out_views[i]->image->barrier.subresource_range
};
/* *INDENT-ON* */
vkCmdPipelineBarrier (cmd->cmd,
out_views[i]->image->barrier.parent.pipeline_stages,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1,
&out_image_memory_barrier);
out_views[i]->image->barrier.parent.pipeline_stages =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
out_views[i]->image->barrier.parent.access_flags =
out_image_memory_barrier.dstAccessMask;
out_views[i]->image->barrier.image_layout =
out_image_memory_barrier.newLayout;
}
{
/* *INDENT-OFF* */
VkClearValue clearColor = {{{ 0.0f, 0.0f, 0.0f, 1.0f }}};
VkClearValue clearColors[GST_VIDEO_MAX_PLANES] = {
clearColor, clearColor, clearColor, clearColor,
};
VkRenderPassBeginInfo render_pass_info = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = (VkRenderPass) self->render_pass->handle,
.framebuffer = (VkFramebuffer) self->framebuffer->handle,
.renderArea.offset = { 0, 0 },
.renderArea.extent = {
GST_VIDEO_INFO_WIDTH (&self->out_info),
GST_VIDEO_INFO_HEIGHT (&self->out_info)
},
.clearValueCount = n_out_mems,
.pClearValues = clearColors,
};
/* *INDENT-ON* */
VkDeviceSize offsets[] = { 0 };
vkCmdBindDescriptorSets (cmd->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
(VkPipelineLayout) self->pipeline_layout->handle, 0, 1,
&self->descriptor_set->set, 0, NULL);
vkCmdBeginRenderPass (cmd->cmd, &render_pass_info,
VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline (cmd->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
(VkPipeline) self->graphics_pipeline->handle);
vkCmdBindVertexBuffers (cmd->cmd, 0, 1,
&((GstVulkanBufferMemory *) priv->vertices)->buffer, offsets);
vkCmdBindIndexBuffer (cmd->cmd,
((GstVulkanBufferMemory *) priv->indices)->buffer, 0,
VK_INDEX_TYPE_UINT16);
vkCmdDrawIndexed (cmd->cmd, priv->n_indices, 1, 0, 0, 0);
vkCmdEndRenderPass (cmd->cmd);
}
return TRUE;
error:
return FALSE;
}
/**
* gst_vulkan_full_screen_quad_submit:
* @self: a #GstVulkanFullScreenQuad
* @cmd: (transfer full): a #GstVulkanCommandBuffer to submit
* @fence: a #GstVulkanFence to signal on completion
* @error: a #GError to fill on error
*
* Returns: whether @cmd could be submitted to the queue
*
* Since: 1.18
*/
gboolean
gst_vulkan_full_screen_quad_submit (GstVulkanFullScreenQuad * self,
GstVulkanCommandBuffer * cmd, GstVulkanFence * fence, GError ** error)
{
VkResult err;
g_return_val_if_fail (GST_IS_VULKAN_FULL_SCREEN_QUAD (self), FALSE);
g_return_val_if_fail (cmd != NULL, FALSE);
g_return_val_if_fail (fence != NULL, FALSE);
{
/* *INDENT-OFF* */
VkSubmitInfo submit_info = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = NULL,
.waitSemaphoreCount = 0,
.pWaitSemaphores = NULL,
.pWaitDstStageMask = NULL,
.commandBufferCount = 1,
.pCommandBuffers = &cmd->cmd,
.signalSemaphoreCount = 0,
.pSignalSemaphores = NULL,
};
/* *INDENT-ON* */
gst_vulkan_queue_submit_lock (self->queue);
err =
vkQueueSubmit (self->queue->queue, 1, &submit_info,
GST_VULKAN_FENCE_FENCE (fence));
gst_vulkan_queue_submit_unlock (self->queue);
if (gst_vulkan_error_to_g_error (err, error, "vkQueueSubmit") < 0)
goto error;
}
gst_vulkan_trash_list_add (self->trash_list,
gst_vulkan_trash_list_acquire (self->trash_list, fence,
gst_vulkan_trash_mini_object_unref, GST_MINI_OBJECT_CAST (cmd)));
gst_vulkan_trash_list_gc (self->trash_list);
if (self->last_fence)
gst_vulkan_fence_unref (self->last_fence);
self->last_fence = gst_vulkan_fence_ref (fence);
return TRUE;
error:
return FALSE;
}