Line data Source code
1 : /* Copyright (C) 2023 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "SubmitScheduler.h"
21 :
22 : #include "renderer/backend/vulkan/Device.h"
23 : #include "renderer/backend/vulkan/RingCommandContext.h"
24 : #include "renderer/backend/vulkan/SwapChain.h"
25 : #include "renderer/backend/vulkan/Utilities.h"
26 :
27 : namespace Renderer
28 : {
29 :
30 : namespace Backend
31 : {
32 :
33 : namespace Vulkan
34 : {
35 :
36 0 : CSubmitScheduler::CSubmitScheduler(
37 0 : CDevice* device, const uint32_t queueFamilyIndex, VkQueue queue)
38 0 : : m_Device(device), m_Queue(queue)
39 : {
40 0 : constexpr size_t numberOfFences = NUMBER_OF_FRAMES_IN_FLIGHT;
41 0 : m_Fences.reserve(numberOfFences);
42 0 : for (size_t index = 0; index < numberOfFences; ++index)
43 : {
44 0 : VkFenceCreateInfo fenceCreateInfo{};
45 0 : fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
46 0 : VkFence fence = VK_NULL_HANDLE;
47 0 : ENSURE_VK_SUCCESS(vkCreateFence(
48 : m_Device->GetVkDevice(), &fenceCreateInfo, nullptr, &fence));
49 0 : m_Fences.push_back({fence, INVALID_SUBMIT_HANDLE});
50 : }
51 :
52 0 : for (FrameObject& frameObject : m_FrameObjects)
53 : {
54 0 : VkSemaphoreCreateInfo semaphoreCreateInfo{};
55 0 : semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
56 0 : ENSURE_VK_SUCCESS(vkCreateSemaphore(
57 : device->GetVkDevice(), &semaphoreCreateInfo, nullptr, &frameObject.acquireImageSemaphore));
58 :
59 0 : ENSURE_VK_SUCCESS(vkCreateSemaphore(
60 : device->GetVkDevice(), &semaphoreCreateInfo, nullptr, &frameObject.submitDone));
61 : }
62 :
63 0 : m_AcquireCommandContext = std::make_unique<CRingCommandContext>(
64 0 : device, NUMBER_OF_FRAMES_IN_FLIGHT, queueFamilyIndex, *this);
65 0 : m_PresentCommandContext = std::make_unique<CRingCommandContext>(
66 0 : device, NUMBER_OF_FRAMES_IN_FLIGHT, queueFamilyIndex, *this);
67 0 : }
68 :
69 0 : CSubmitScheduler::~CSubmitScheduler()
70 : {
71 0 : VkDevice device = m_Device->GetVkDevice();
72 :
73 0 : for (Fence& fence : m_Fences)
74 0 : if (fence.value != VK_NULL_HANDLE)
75 0 : vkDestroyFence(device, fence.value, nullptr);
76 :
77 0 : for (FrameObject& frameObject : m_FrameObjects)
78 : {
79 0 : if (frameObject.acquireImageSemaphore != VK_NULL_HANDLE)
80 0 : vkDestroySemaphore(device, frameObject.acquireImageSemaphore, nullptr);
81 :
82 0 : if (frameObject.submitDone != VK_NULL_HANDLE)
83 0 : vkDestroySemaphore(device, frameObject.submitDone, nullptr);
84 : }
85 0 : }
86 :
87 0 : bool CSubmitScheduler::AcquireNextImage(CSwapChain& swapChain)
88 : {
89 0 : FrameObject& frameObject = m_FrameObjects[m_FrameID % m_FrameObjects.size()];
90 0 : if (!swapChain.AcquireNextImage(frameObject.acquireImageSemaphore))
91 0 : return false;
92 0 : swapChain.SubmitCommandsAfterAcquireNextImage(*m_AcquireCommandContext);
93 :
94 0 : m_NextWaitSemaphore = frameObject.acquireImageSemaphore;
95 0 : m_NextWaitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
96 0 : m_AcquireCommandContext->Flush();
97 0 : return true;
98 : }
99 :
100 0 : void CSubmitScheduler::Present(CSwapChain& swapChain)
101 : {
102 0 : FrameObject& frameObject = m_FrameObjects[m_FrameID % m_FrameObjects.size()];
103 0 : swapChain.SubmitCommandsBeforePresent(*m_PresentCommandContext);
104 0 : m_NextSubmitSignalSemaphore = frameObject.submitDone;
105 0 : m_PresentCommandContext->Flush();
106 0 : Flush();
107 0 : swapChain.Present(frameObject.submitDone, m_Queue);
108 0 : }
109 :
110 0 : CSubmitScheduler::SubmitHandle CSubmitScheduler::Submit(VkCommandBuffer commandBuffer)
111 : {
112 0 : m_SubmittedCommandBuffers.emplace_back(commandBuffer);
113 0 : return m_CurrentHandle;
114 : }
115 :
116 0 : void CSubmitScheduler::WaitUntilFree(const SubmitHandle handle)
117 : {
118 : // We haven't submitted the current handle.
119 0 : if (handle == m_CurrentHandle)
120 0 : Flush();
121 :
122 0 : VkDevice device = m_Device->GetVkDevice();
123 0 : while (!m_SubmittedHandles.empty() && handle >= m_SubmittedHandles.front().value)
124 : {
125 0 : Fence& fence = m_Fences[m_SubmittedHandles.front().fenceIndex];
126 0 : ENSURE(fence.inUse);
127 0 : m_SubmittedHandles.pop();
128 0 : ENSURE_VK_SUCCESS(vkWaitForFences(device, 1, &fence.value, VK_TRUE, std::numeric_limits<uint64_t>::max()));
129 0 : ENSURE_VK_SUCCESS(vkResetFences(device, 1, &fence.value));
130 0 : fence.inUse = false;
131 0 : fence.lastUsedHandle = INVALID_SUBMIT_HANDLE;
132 : }
133 0 : }
134 :
135 0 : void CSubmitScheduler::Flush()
136 : {
137 0 : ENSURE(!m_SubmittedCommandBuffers.empty());
138 :
139 0 : Fence& fence = m_Fences[m_FenceIndex];
140 0 : if (fence.inUse)
141 0 : WaitUntilFree(fence.lastUsedHandle);
142 0 : fence.lastUsedHandle = m_CurrentHandle;
143 0 : fence.inUse = true;
144 0 : m_SubmittedHandles.push({m_CurrentHandle, m_FenceIndex});
145 0 : ++m_CurrentHandle;
146 0 : m_FenceIndex = (m_FenceIndex + 1) % m_Fences.size();
147 :
148 0 : VkSubmitInfo submitInfo{};
149 0 : submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
150 0 : if (m_NextWaitSemaphore != VK_NULL_HANDLE)
151 : {
152 0 : submitInfo.waitSemaphoreCount = 1;
153 0 : submitInfo.pWaitSemaphores = &m_NextWaitSemaphore;
154 0 : submitInfo.pWaitDstStageMask = &m_NextWaitDstStageMask;
155 : }
156 0 : if (m_NextSubmitSignalSemaphore != VK_NULL_HANDLE)
157 : {
158 0 : submitInfo.signalSemaphoreCount = 1;
159 0 : submitInfo.pSignalSemaphores = &m_NextSubmitSignalSemaphore;
160 : }
161 0 : submitInfo.commandBufferCount = m_SubmittedCommandBuffers.size();
162 0 : submitInfo.pCommandBuffers = m_SubmittedCommandBuffers.data();
163 :
164 0 : ENSURE_VK_SUCCESS(vkQueueSubmit(m_Queue, 1, &submitInfo, fence.value));
165 :
166 0 : m_NextWaitSemaphore = VK_NULL_HANDLE;
167 0 : m_NextWaitDstStageMask = 0;
168 0 : m_NextSubmitSignalSemaphore = VK_NULL_HANDLE;
169 :
170 0 : m_SubmittedCommandBuffers.clear();
171 0 : }
172 :
173 : } // namespace Vulkan
174 :
175 : } // namespace Backend
176 :
177 3 : } // namespace Renderer
|