mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-25 09:40:37 +00:00
2d31aba78d
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1506>
1482 lines
46 KiB
C
1482 lines
46 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;
|
|
};
|
|
|
|
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, 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 < GST_VIDEO_INFO_N_PLANES (&self->in_info); 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)
|
|
{
|
|
VkDescriptorSetLayoutBinding bindings[GST_VIDEO_MAX_PLANES + 1] = { {0,} };
|
|
VkDescriptorSetLayoutCreateInfo layout_info;
|
|
VkDescriptorSetLayout descriptor_set_layout;
|
|
int descriptor_n = 0;
|
|
VkResult err;
|
|
int i;
|
|
|
|
/* *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
|
|
};
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->in_info); 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)
|
|
{
|
|
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;
|
|
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->out_info); 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 = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
|
.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 = GST_VIDEO_INFO_N_PLANES (&self->out_info),
|
|
.pColorAttachments = color_attachment_refs
|
|
};
|
|
|
|
render_pass_info = (VkRenderPassCreateInfo) {
|
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
|
.pNext = NULL,
|
|
.attachmentCount = GST_VIDEO_INFO_N_PLANES (&self->out_info),
|
|
.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,
|
|
.blendEnable = VK_FALSE
|
|
};
|
|
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,
|
|
.blendEnable = VK_FALSE
|
|
};
|
|
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,
|
|
.blendEnable = VK_FALSE
|
|
};
|
|
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,
|
|
.blendEnable = VK_FALSE
|
|
};
|
|
|
|
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_VIDEO_INFO_N_PLANES (&self->out_info),
|
|
.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_VIDEO_INFO_N_PLANES (&self->in_info),
|
|
};
|
|
|
|
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,
|
|
GError ** error)
|
|
{
|
|
VkImageView attachments[GST_VIDEO_MAX_PLANES] = { 0, };
|
|
VkFramebufferCreateInfo framebuffer_info;
|
|
VkFramebuffer framebuffer;
|
|
VkResult err;
|
|
int i;
|
|
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->out_info); 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 = GST_VIDEO_INFO_N_PLANES (&self->out_info),
|
|
.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)
|
|
{
|
|
self->trash_list = gst_vulkan_trash_fence_list_new ();
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
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);
|
|
|
|
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_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;
|
|
|
|
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) {
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->in_info); 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, error)))
|
|
goto error;
|
|
}
|
|
|
|
if (!self->framebuffer) {
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->out_info); 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, 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;
|
|
|
|
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);
|
|
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->in_info); 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]));
|
|
}
|
|
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->out_info); 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 < GST_VIDEO_INFO_N_PLANES (&self->in_info); 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 = 0,
|
|
.dstQueueFamilyIndex = 0,
|
|
.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 < GST_VIDEO_INFO_N_PLANES (&self->out_info); 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 = 0,
|
|
.dstQueueFamilyIndex = 0,
|
|
.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 = GST_VIDEO_INFO_N_PLANES (&self->out_info),
|
|
.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;
|
|
}
|