]> git.tdb.fi Git - libs/gl.git/blob - source/backends/vulkan/commands_backend.cpp
Rework multisample resolve to use resolve attachments
[libs/gl.git] / source / backends / vulkan / commands_backend.cpp
1 #include <msp/core/hash.h>
2 #include <msp/graphics/vulkancontext_platform.h>
3 #include "batch.h"
4 #include "commands_backend.h"
5 #include "device.h"
6 #include "error.h"
7 #include "framebuffer.h"
8 #include "frameformat.h"
9 #include "pipelinestate.h"
10 #include "rect.h"
11 #include "renderpass.h"
12 #include "semaphore.h"
13 #include "swapchaintexture.h"
14 #include "vulkan.h"
15
16 using namespace std;
17
18 namespace Msp {
19 namespace GL {
20
21 VulkanCommands::VulkanCommands():
22         device(Device::get_current())
23 { }
24
25 VulkanCommands::~VulkanCommands()
26 {
27         const VulkanFunctions &vk = device.get_functions();
28
29         vk.QueueWaitIdle();
30 }
31
32 void VulkanCommands::begin_buffer(VkRenderPass render_pass)
33 {
34         if(frame_index>=command_pools.size())
35                 throw invalid_operation("VulkanCommands::begin_buffer");
36
37         const VulkanFunctions &vk = device.get_functions();
38
39         CommandPool &current_pool = command_pools[frame_index];
40         if(!current_pool.in_use)
41         {
42                 current_pool.fence.reset();
43                 current_pool.in_use = true;
44         }
45
46         CommandBuffers &buffers = (render_pass ? current_pool.secondary : current_pool.primary);
47
48         if(buffers.next_buffer>=buffers.buffers.size())
49         {
50                 VkCommandBufferAllocateInfo alloc_info = { };
51                 alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
52                 alloc_info.commandPool = handle_cast<::VkCommandPool>(current_pool.pool);
53                 alloc_info.level = (render_pass ? VK_COMMAND_BUFFER_LEVEL_SECONDARY : VK_COMMAND_BUFFER_LEVEL_PRIMARY);
54                 alloc_info.commandBufferCount = 1;
55
56                 VkCommandBuffer buffer;
57                 vk.AllocateCommandBuffers(alloc_info, &buffer);
58                 buffers.buffers.push_back(buffer);
59         }
60
61         VkCommandBuffer buffer = buffers.buffers[buffers.next_buffer++];
62         (render_pass ? pass_buffer : primary_buffer) = buffer;
63
64         VkCommandBufferBeginInfo begin_info = { };
65         begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
66         begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
67         if(render_pass)
68                 begin_info.flags |= VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
69
70         VkCommandBufferInheritanceInfo inherit_info = { };
71         if(render_pass)
72         {
73                 inherit_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
74                 inherit_info.renderPass = handle_cast<::VkRenderPass>(render_pass);
75                 inherit_info.subpass = 0;
76
77                 begin_info.pInheritanceInfo = &inherit_info;
78         }
79
80         vk.BeginCommandBuffer(buffer, begin_info);
81         last_pipeline = 0;
82 }
83
84 void VulkanCommands::begin_render_pass(bool clear, const ClearValue *clear_values)
85 {
86         framebuffer = pipeline_state->get_framebuffer();
87         if(!framebuffer)
88                 throw invalid_operation("VulkanCommands::begin_render_pass");
89
90         viewport = pipeline_state->get_viewport();
91
92         if(!primary_buffer)
93                 begin_buffer(0);
94
95         fb_is_swapchain = false;
96         unsigned n_attachments = framebuffer->get_format().size();
97         for(unsigned i=0; (!fb_is_swapchain && i<n_attachments); ++i)
98                 if(dynamic_cast<const SwapChainTexture *>(framebuffer->get_attachment(i)))
99                         fb_is_swapchain = true;
100
101         framebuffer->refresh();
102
103         RenderPass render_pass;
104         render_pass.framebuffer = framebuffer;
105         render_pass.render_area = viewport;
106         render_pass.clear = clear;
107         render_pass.clear_values = clear_values;
108         render_pass.to_present = fb_is_swapchain;
109         render_pass.update(device);
110
111         discard_fb_contents = render_pass.discard_fb_contents;
112
113         begin_buffer(render_pass.handle);
114         render_pass.fill_begin_info(pass_begin_info);
115 }
116
117 void VulkanCommands::end_render_pass()
118 {
119         const VulkanFunctions &vk = device.get_functions();
120         VulkanCommandRecorder vkCmd(vk, primary_buffer);
121
122         vk.EndCommandBuffer(pass_buffer);
123
124         device.get_transfer_queue().dispatch_transfers(vkCmd);
125
126         Synchronizer &sync = device.get_synchronizer();
127         sync.reset();
128         if(!fb_is_swapchain)
129                 framebuffer->prepare_image_layouts(discard_fb_contents);
130         sync.barrier(vkCmd);
131
132         const VkRenderPassBeginInfo &begin_info = *reinterpret_cast<const VkRenderPassBeginInfo *>(pass_begin_info.data());
133         vkCmd.BeginRenderPass(begin_info, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
134         vkCmd.ExecuteCommands(1, &pass_buffer);
135         vkCmd.EndRenderPass();
136
137         framebuffer = 0;
138         viewport = Rect::max();
139         pass_buffer = 0;
140 }
141
142 void VulkanCommands::begin_frame(unsigned index)
143 {
144         const VulkanFunctions &vk = device.get_functions();
145
146         frame_index = index%device.get_n_frames_in_flight();
147         if(frame_index>=command_pools.size())
148         {
149                 command_pools.reserve(frame_index+1);
150                 for(unsigned i=command_pools.size(); i<frame_index+1; ++i)
151                         command_pools.emplace_back(device);
152         }
153
154         CommandPool &current_pool = command_pools[frame_index];
155         if(current_pool.in_use)
156         {
157                 current_pool.fence.wait();
158                 vk.ResetCommandPool(current_pool.pool, 0);
159                 current_pool.in_use = false;
160                 current_pool.primary.next_buffer = 0;
161                 current_pool.secondary.next_buffer = 0;
162         }
163
164         device.get_descriptor_pool().begin_frame();
165 }
166
167 void VulkanCommands::submit_frame(Semaphore *wait_sem, Semaphore *signal_sem)
168 {
169         if(!primary_buffer)
170                 return;
171
172         const VulkanFunctions &vk = device.get_functions();
173         ::VkSemaphore vk_wait_sem = (wait_sem ? handle_cast<::VkSemaphore>(wait_sem->handle) : 0);
174         ::VkSemaphore vk_signal_sem = (signal_sem ? handle_cast<::VkSemaphore>(signal_sem->handle) : 0);
175         VkPipelineStageFlags wait_stages = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
176
177         if(framebuffer)
178                 end_render_pass();
179
180         vk.EndCommandBuffer(primary_buffer);
181
182         VkSubmitInfo submit_info = { };
183         submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
184         submit_info.waitSemaphoreCount = (wait_sem ? 1 : 0);
185         submit_info.pWaitSemaphores = &vk_wait_sem;
186         submit_info.pWaitDstStageMask = &wait_stages;
187         submit_info.commandBufferCount = 1;
188         submit_info.pCommandBuffers = handle_cast<::VkCommandBuffer *>(&primary_buffer);
189         submit_info.signalSemaphoreCount = (signal_sem ? 1 : 0);
190         submit_info.pSignalSemaphores = &vk_signal_sem;
191
192         vk.QueueSubmit(1, &submit_info, command_pools[frame_index].fence.handle);
193
194         primary_buffer = 0;
195 }
196
197 void VulkanCommands::use_pipeline(const PipelineState *ps)
198 {
199         if(!pipeline_state || !ps || ps->get_framebuffer()!=framebuffer || ps->get_viewport()!=viewport)
200                 if(framebuffer)
201                         end_render_pass();
202
203         pipeline_state = ps;
204 }
205
206 void VulkanCommands::clear(const ClearValue *values)
207 {
208         if(framebuffer)
209                 throw invalid_operation("VulkanCommands::clear");
210
211         begin_render_pass(true, values);
212 }
213
214 void VulkanCommands::draw(const Batch &batch)
215 {
216         draw_instanced(batch, 1);
217 }
218
219 void VulkanCommands::draw_instanced(const Batch &batch, unsigned count)
220 {
221         if(!pipeline_state)
222                 throw invalid_operation("VulkanCommands::draw_instanced");
223
224         if(!framebuffer)
225                  begin_render_pass(false, 0);
226
227         VulkanCommandRecorder vkCmd(device.get_functions(), pass_buffer);
228
229         pipeline_state->refresh();
230         pipeline_state->apply(vkCmd, last_pipeline, frame_index, fb_is_swapchain);
231         last_pipeline = pipeline_state;
232         unsigned first_index = batch.get_offset()/batch.get_index_size();
233         vkCmd.DrawIndexed(batch.size(), count, first_index, 0, 0);
234 }
235
236 void VulkanCommands::dispatch(unsigned count_x, unsigned count_y, unsigned count_z)
237 {
238         if(!pipeline_state)
239                 throw invalid_operation("VulkanCommands::draw_instanced");
240
241         if(framebuffer)
242                 end_render_pass();
243
244         VulkanCommandRecorder vkCmd(device.get_functions(), primary_buffer);
245
246         pipeline_state->refresh();
247         pipeline_state->synchronize_resources();
248         device.get_synchronizer().barrier(vkCmd);
249         pipeline_state->apply(vkCmd, 0, frame_index, false);
250         vkCmd.Dispatch(count_x, count_y, count_z);
251 }
252
253 void VulkanCommands::resolve_multisample()
254 {
255         throw logic_error("VulkanCommands::resolve_multisample is unimplemented");
256 }
257
258 void VulkanCommands::begin_query(const QueryPool &, unsigned)
259 {
260         throw logic_error("VulkanCommands::begin_query is unimplemented");
261 }
262
263 void VulkanCommands::end_query(const QueryPool &, unsigned)
264 {
265         throw logic_error("VulkanCommands::end_query is unimplemented");
266 }
267
268
269 VulkanCommands::CommandPool::CommandPool(Device &d):
270         device(d),
271         fence(true)
272 {
273         const VulkanFunctions &vk = device.get_functions();
274
275         VkCommandPoolCreateInfo pool_info = { };
276         pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
277         pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
278         pool_info.queueFamilyIndex = device.get_context().get_private().graphics_queue_family;
279
280         vk.CreateCommandPool(pool_info, pool);
281 }
282
283 VulkanCommands::CommandPool::CommandPool(CommandPool &&other):
284         device(other.device),
285         pool(other.pool),
286         fence(move(other.fence)),
287         in_use(other.in_use)
288 {
289         other.pool = 0;
290 }
291
292 VulkanCommands::CommandPool::~CommandPool()
293 {
294         const VulkanFunctions &vk = device.get_functions();
295
296         if(pool)
297                 vk.DestroyCommandPool(pool);
298 }
299
300 } // namespace GL
301 } // namespace Msp