UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
vulkan_pipelines.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2021-2023 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include "vulkan_pipelines.hpp"
39
40#include <cstdint>
41#include <cmath>
42#include <fstream>
43#include <vector>
44
45using namespace vulkan_display;
46using namespace vulkan_display_detail;
47
48namespace{
49
50vk::ShaderModule create_shader(
51
52 const std::string& file_path,
53 const vk::Device& device)
54{
55 std::ifstream file(file_path, std::ios::binary);
56 if(!file.is_open()){
57 throw VulkanError{"Failed to open file:"s + file_path};
58 }
59 file.seekg(0, std::ios_base::end);
60 auto size = file.tellg();
61 assert(size % 4 == 0);
62 file.seekg(0);
63 std::vector<std::uint32_t> shader_code(size / 4);
64 file.read(reinterpret_cast<char*>(shader_code.data()), static_cast<std::streamsize>(size));
65
66 if(!file.good()){
67 throw VulkanError{"Error reading from file:"s + file_path};
68 }
69
70 vk::ShaderModuleCreateInfo shader_info;
71 shader_info
72 .setCodeSize(shader_code.size() * 4)
73 .setPCode(shader_code.data());
74 return device.createShaderModule(shader_info);
75}
76
77vk::SamplerYcbcrConversion createYCbCrConversion(vk::Device device, vk::Format format){
78 vk::SamplerYcbcrConversion yCbCr_conversion = nullptr;
79 if (is_yCbCr_format(format)) {
80 vk::SamplerYcbcrConversionCreateInfo conversion_info;
81 conversion_info
82 .setFormat(format)
83 .setYcbcrModel(vk::SamplerYcbcrModelConversion::eYcbcr709)
84 .setYcbcrRange(vk::SamplerYcbcrRange::eItuNarrow)
85 .setComponents({})
86 .setChromaFilter(vk::Filter::eLinear)
87 .setXChromaOffset(vk::ChromaLocation::eMidpoint)
88 .setYChromaOffset(vk::ChromaLocation::eMidpoint)
89 .setForceExplicitReconstruction(false);
90 yCbCr_conversion = device.createSamplerYcbcrConversion(conversion_info);
91 }
92 return yCbCr_conversion;
93}
94
95vk::Sampler create_sampler(vk::Device device, vk::SamplerYcbcrConversion yCbCr_conversion, vk::Filter filter) {
96 vk::SamplerYcbcrConversionInfo yCbCr_info{ yCbCr_conversion };
97
98 vk::SamplerCreateInfo sampler_info;
99 sampler_info
100 .setAddressModeU(vk::SamplerAddressMode::eClampToEdge)
101 .setAddressModeV(vk::SamplerAddressMode::eClampToEdge)
102 .setAddressModeW(vk::SamplerAddressMode::eClampToEdge)
103 .setMagFilter(filter)
104 .setMinFilter(filter)
105 .setAnisotropyEnable(false)
106 .setUnnormalizedCoordinates(false)
107 .setPNext(yCbCr_conversion ? &yCbCr_info : nullptr);
108 return device.createSampler(sampler_info);
109}
110
111struct Binding{
112 vk::DescriptorType type;
113 vk::ShaderStageFlags stages;
114 vk::Sampler sampler = nullptr;
115};
116
117vk::DescriptorSetLayout create_descriptor_set_layout(vk::Device device, uint32_t first_binding, std::vector<Binding> bindings) {
118 std::vector<vk::DescriptorSetLayoutBinding> descriptor_set_layout_bindings(bindings.size());
119 for(size_t i = 0; i < bindings.size(); i++){
120 descriptor_set_layout_bindings[i]
121 .setBinding(first_binding + i)
122 .setDescriptorCount(1)
123 .setDescriptorType(bindings[i].type)
124 .setStageFlags(bindings[i].stages)
125 .setPImmutableSamplers(&bindings[i].sampler);
126 }
127 vk::DescriptorSetLayoutCreateInfo descriptor_set_layout_info{};
128 descriptor_set_layout_info
129 .setBindingCount(descriptor_set_layout_bindings.size())
130 .setPBindings(descriptor_set_layout_bindings.data());
131
132 return device.createDescriptorSetLayout(descriptor_set_layout_info);
133}
134
135vk::PipelineLayout create_compute_pipeline_layout(vk::Device device, const std::vector<vk::DescriptorSetLayout>& descriptor_set_layout){
136 vk::PushConstantRange push_constant_range{};
137 push_constant_range
138 .setOffset(0)
139 .setSize(sizeof(ImageSize))
140 .setStageFlags(vk::ShaderStageFlagBits::eCompute);
141
142 vk::PipelineLayoutCreateInfo pipeline_layout_info;
143 pipeline_layout_info
144 .setSetLayoutCount(descriptor_set_layout.size())
145 .setPSetLayouts(descriptor_set_layout.data())
146 .setPushConstantRangeCount(1)
147 .setPPushConstantRanges(&push_constant_range);
148
149 return device.createPipelineLayout(pipeline_layout_info);
150}
151
152vk::Pipeline create_compute_pipeline(vk::Device device, vk::PipelineLayout pipeline_layout, vk::ShaderModule shader){
153 vk::PipelineShaderStageCreateInfo shader_stage_info;
154 shader_stage_info
155 .setModule(shader)
156 .setPName("main")
157 .setStage(vk::ShaderStageFlagBits::eCompute);
158
159 vk::ComputePipelineCreateInfo pipeline_info{};
160 pipeline_info
161 .setStage(shader_stage_info)
162 .setLayout(pipeline_layout);
163
164 vk::Pipeline compute_pipeline;
165 auto result = device.createComputePipelines(nullptr, 1, &pipeline_info, nullptr, &compute_pipeline);
166 if(result != vk::Result::eSuccess){
167 throw VulkanError{"Pipeline cannot be created."};
168 }
169 return compute_pipeline;
170}
171
172} //namespace
173
174namespace vulkan_display_detail {
175
176void ConversionPipeline::create(vk::Device device, const std::string& shader_path, vk::Format format){
177 assert(!valid);
178 valid = true;
179
180 yCbCr_conversion = createYCbCrConversion(device, format);
181 sampler = create_sampler(device, yCbCr_conversion, vk::Filter::eNearest);
182
183 compute_shader = create_shader(shader_path, device);
184 source_desc_set_layout = create_descriptor_set_layout(device, 0, {
185 {vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eCompute, sampler}
186 });
187 destination_desc_set_layout = create_descriptor_set_layout(device, 1, {
188 {vk::DescriptorType::eStorageImage, vk::ShaderStageFlagBits::eCompute}
189 });
190 pipeline_layout = create_compute_pipeline_layout(device,
192 pipeline = create_compute_pipeline(device, pipeline_layout, compute_shader);
193}
194
195void ConversionPipeline::destroy(vk::Device device){
196 if(valid){
197 valid = false;
198 if (yCbCr_conversion){
199 device.destroy(yCbCr_conversion);
200 }
201 device.destroy(sampler);
202 device.destroy(compute_shader);
203 device.destroy(pipeline_layout);
204 device.destroy(pipeline);
205 device.destroy(source_desc_set_layout);
206 device.destroy(destination_desc_set_layout);
207 }
208}
209
210void ConversionPipeline::record_commands(vk::CommandBuffer cmd_buffer, ImageSize image_size,
211 std::array<vk::DescriptorSet, 2> descriptor_sets)
212{
213 cmd_buffer.pushConstants(pipeline_layout, vk::ShaderStageFlagBits::eCompute,
214 0, sizeof(image_size), &image_size);
215
216 cmd_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline);
217 cmd_buffer.bindDescriptorSets(vk::PipelineBindPoint::eCompute, pipeline_layout, 0, descriptor_sets, nullptr);
218 cmd_buffer.dispatch((image_size.width + 16 - 1) / 16, (image_size.height + 16 - 1) / 16, 1);
219}
220
221} //namespace vulkan_display_detail
222
223namespace{
224
225vk::RenderPass create_render_pass(vk::Device device, vk::Format swapchain_image_format) {
226 vk::RenderPassCreateInfo render_pass_info;
227
228 vk::AttachmentDescription color_attachment;
229 color_attachment
230 .setFormat(swapchain_image_format)
231 .setSamples(vk::SampleCountFlagBits::e1)
232 .setLoadOp(vk::AttachmentLoadOp::eClear)
233 .setStoreOp(vk::AttachmentStoreOp::eStore)
234 .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
235 .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
236 .setInitialLayout(vk::ImageLayout::eUndefined)
237 .setFinalLayout(vk::ImageLayout::ePresentSrcKHR);
238 render_pass_info
239 .setAttachmentCount(1)
240 .setPAttachments(&color_attachment);
241
242 vk::AttachmentReference attachment_reference;
243 attachment_reference
244 .setAttachment(0)
245 .setLayout(vk::ImageLayout::eColorAttachmentOptimal);
246 vk::SubpassDescription subpass;
247 subpass
248 .setPipelineBindPoint(vk::PipelineBindPoint::eGraphics)
249 .setColorAttachmentCount(1)
250 .setPColorAttachments(&attachment_reference);
251 render_pass_info
252 .setSubpassCount(1)
253 .setPSubpasses(&subpass);
254
255 vk::SubpassDependency subpass_dependency{};
256 subpass_dependency
257 .setSrcSubpass(VK_SUBPASS_EXTERNAL)
258 .setDstSubpass(0)
259 .setSrcStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
260 .setDstStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
261 .setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite);
262 render_pass_info
263 .setDependencyCount(1)
264 .setPDependencies(&subpass_dependency);
265
266 return device.createRenderPass(render_pass_info);
267}
268
269vk::PipelineLayout create_render_pipeline_layout(vk::Device device, vk::DescriptorSetLayout descriptor_set_layout){
270 vk::PipelineLayoutCreateInfo pipeline_layout_info{};
271
272 vk::PushConstantRange push_constants;
273 push_constants
274 .setOffset(0)
275 .setSize(sizeof(RenderArea))
276 .setStageFlags(vk::ShaderStageFlagBits::eFragment);
277 pipeline_layout_info
278 .setPushConstantRangeCount(1)
279 .setPPushConstantRanges(&push_constants)
280 .setSetLayoutCount(1)
281 .setPSetLayouts(&descriptor_set_layout);
282 return device.createPipelineLayout(pipeline_layout_info);
283}
284
285
286vk::Pipeline create_render_pipeline(vk::Device device, vk::PipelineLayout render_pipeline_layout, vk::RenderPass render_pass,
287 vk::ShaderModule vertex_shader, vk::ShaderModule fragment_shader)
288{
289 vk::GraphicsPipelineCreateInfo pipeline_info{};
290
291 std::array<vk::PipelineShaderStageCreateInfo, 2> shader_stages_infos;
292 shader_stages_infos[0]
293 .setModule(vertex_shader)
294 .setPName("main")
295 .setStage(vk::ShaderStageFlagBits::eVertex);
296 shader_stages_infos[1]
297 .setModule(fragment_shader)
298 .setPName("main")
299 .setStage(vk::ShaderStageFlagBits::eFragment);
300 pipeline_info
301 .setStageCount(static_cast<uint32_t>(shader_stages_infos.size()))
302 .setPStages(shader_stages_infos.data());
303
304 vk::PipelineVertexInputStateCreateInfo vertex_input_state_info{};
305 pipeline_info.setPVertexInputState(&vertex_input_state_info);
306
307 vk::PipelineInputAssemblyStateCreateInfo input_assembly_state_info{};
308 input_assembly_state_info.setTopology(vk::PrimitiveTopology::eTriangleList);
309 pipeline_info.setPInputAssemblyState(&input_assembly_state_info);
310
311 vk::PipelineViewportStateCreateInfo viewport_state_info;
312 viewport_state_info
313 .setScissorCount(1)
314 .setViewportCount(1);
315 pipeline_info.setPViewportState(&viewport_state_info);
316
317 vk::PipelineRasterizationStateCreateInfo rasterization_info{};
318 rasterization_info
319 .setPolygonMode(vk::PolygonMode::eFill)
320 .setLineWidth(1.f);
321 pipeline_info.setPRasterizationState(&rasterization_info);
322
323 vk::PipelineMultisampleStateCreateInfo multisample_info;
324 multisample_info
325 .setSampleShadingEnable(false)
326 .setRasterizationSamples(vk::SampleCountFlagBits::e1);
327 pipeline_info.setPMultisampleState(&multisample_info);
328
329 using ColorFlags = vk::ColorComponentFlagBits;
330 vk::PipelineColorBlendAttachmentState color_blend_attachment{};
331 color_blend_attachment
332 .setBlendEnable(false)
333 .setColorWriteMask(ColorFlags::eR | ColorFlags::eG | ColorFlags::eB | ColorFlags::eA);
334 vk::PipelineColorBlendStateCreateInfo color_blend_info{};
335 color_blend_info
336 .setAttachmentCount(1)
337 .setPAttachments(&color_blend_attachment);
338 pipeline_info.setPColorBlendState(&color_blend_info);
339
340 std::array dynamic_states{ vk::DynamicState::eViewport, vk::DynamicState::eScissor };
341 vk::PipelineDynamicStateCreateInfo dynamic_state_info{};
342 dynamic_state_info
343 .setDynamicStateCount(static_cast<uint32_t>(dynamic_states.size()))
344 .setPDynamicStates(dynamic_states.data());
345 pipeline_info.setPDynamicState(&dynamic_state_info);
346
347 pipeline_info
348 .setLayout(render_pipeline_layout)
349 .setRenderPass(render_pass);
350
351 vk::Pipeline render_pipeline;
352 auto result = device.createGraphicsPipelines(nullptr, 1, &pipeline_info, nullptr, &render_pipeline);
353 if(result != vk::Result::eSuccess){
354 throw VulkanError{"Pipeline cannot be created."};
355 }
356 return render_pipeline;
357}
358
359} //namespace
360
361namespace vulkan_display_detail {
362
363void RenderPipeline::create(VulkanContext& context, const std::string& path_to_shaders){
364 assert(!valid);
365 valid = true;
366
367 auto device = context.get_device();
368 vertex_shader = create_shader(path_to_shaders + "/render.vert.spv", device);
369 fragment_shader = create_shader(path_to_shaders + "/render.frag.spv", device);
370 render_pass = create_render_pass(device, context.get_swapchain_image_format());
371
372 vk::ClearColorValue clear_color_value{};
373 clear_color_value.setFloat32({ 0.01f, 0.01f, 0.01f, 1.0f });
374 clear_color.setColor(clear_color_value);
375}
376
377void RenderPipeline::destroy(vk::Device device){
378 if(valid){
379 valid = false;
380 device.destroy(pipeline);
381 device.destroy(pipeline_layout);
382 device.destroy(image_desc_set_layout);
383 device.destroy(render_pass);
384 device.destroy(fragment_shader);
385 device.destroy(vertex_shader);
386 device.destroy(sampler);
388 device.destroy(yCbCr_conversion);
389 }
390 }
391}
392
393void RenderPipeline::update_render_area(vk::Extent2D render_area_size, vk::Extent2D image_size){
394 double wnd_aspect = static_cast<double>(render_area_size.width) / render_area_size.height;
395 double img_aspect = static_cast<double>(image_size.width) / image_size.height;
396
397 this->render_area_siza = render_area_size;
398
399 if (wnd_aspect > img_aspect) {
400 render_area.height = render_area_size.height;
401 render_area.width = static_cast<uint32_t>(std::round(render_area_size.height * img_aspect));
402 render_area.x = (render_area_size.width - render_area.width) / 2;
403 render_area.y = 0;
404 } else {
405 render_area.width = render_area_size.width;
406 render_area.height = static_cast<uint32_t>(std::round(render_area_size.width / img_aspect));
407 render_area.x = 0;
408 render_area.y = (render_area_size.height - render_area.height) / 2;
409 }
410
412 .setX(static_cast<float>(render_area.x))
413 .setY(static_cast<float>(render_area.y))
414 .setWidth(static_cast<float>(render_area.width))
415 .setHeight(static_cast<float>(render_area.height))
416 .setMinDepth(0.f)
417 .setMaxDepth(1.f);
418 scissor
419 .setOffset({ static_cast<int32_t>(render_area.x), static_cast<int32_t>(render_area.y) })
420 .setExtent({ render_area.width, render_area.height });
421}
422
423void RenderPipeline::reconfigure(vk::Device device, vk::Format format){
424 device.destroy(pipeline);
425 device.destroy(pipeline_layout);
426 device.destroy(image_desc_set_layout);
427 device.destroy(sampler);
428 if (yCbCr_conversion){
429 device.destroy(yCbCr_conversion);
430 }
431
432 yCbCr_conversion = createYCbCrConversion(device, format);
433 sampler = create_sampler(device, yCbCr_conversion, vk::Filter::eLinear);
434 image_desc_set_layout = create_descriptor_set_layout(device, 0, {
435 {vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment, sampler}
436 });
437 pipeline_layout = create_render_pipeline_layout(device, image_desc_set_layout);
438 pipeline = create_render_pipeline(device, pipeline_layout, render_pass, vertex_shader, fragment_shader);
439}
440
441void RenderPipeline::record_commands(vk::CommandBuffer cmd_buffer, vk::DescriptorSet image, vk::Framebuffer framebuffer){
442 vk::RenderPassBeginInfo render_pass_begin_info;
443 render_pass_begin_info
444 .setRenderPass(render_pass)
445 .setRenderArea(vk::Rect2D{ {0,0}, render_area_siza })
446 .setClearValueCount(1)
447 .setPClearValues(&clear_color)
448 .setFramebuffer(framebuffer);
449 cmd_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline);
450
451 cmd_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
452
453 cmd_buffer.setScissor(0, scissor);
454 cmd_buffer.setViewport(0, viewport);
455 cmd_buffer.pushConstants(pipeline_layout, vk::ShaderStageFlagBits::eFragment, 0, sizeof(render_area), &render_area);
456 cmd_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
457 pipeline_layout, 0, image, nullptr);
458 cmd_buffer.draw(3, 1, 0, 0);
459
460 cmd_buffer.endRenderPass();
461}
462
463} //namespace vulkan_display_detail
static af_result_code filter(void *state, struct audio_frame **frame)
vk::DescriptorSetLayout destination_desc_set_layout
void create(vk::Device device, const std::string &shader_path, vk::Format format)
void record_commands(vk::CommandBuffer cmd_buffer, ImageSize image_size, std::array< vk::DescriptorSet, 2 > descriptor_set)
vk::SamplerYcbcrConversion yCbCr_conversion
void record_commands(vk::CommandBuffer cmd_buffer, vk::DescriptorSet image, vk::Framebuffer framebuffer)
void update_render_area(vk::Extent2D render_area_siza, vk::Extent2D image_size)
void create(VulkanContext &context, const std::string &path_to_shaders)
void reconfigure(vk::Device device, vk::Format sampler)
vk::DescriptorSetLayout image_desc_set_layout
constexpr bool is_yCbCr_format(vk::Format format)
Option::OptType type
Definition settings.cpp:194