Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/GPU/Vulkan/VulkanFrameData.cpp
3187 views
1
#include <mutex>
2
3
#include "VulkanFrameData.h"
4
#include "Common/Log.h"
5
#include "Common/StringUtils.h"
6
7
#if 0 // def _DEBUG
8
#define VLOG(...) NOTICE_LOG(Log::G3D, __VA_ARGS__)
9
#else
10
#define VLOG(...)
11
#endif
12
13
void CachedReadback::Destroy(VulkanContext *vulkan) {
14
if (buffer) {
15
vulkan->Delete().QueueDeleteBufferAllocation(buffer, allocation);
16
}
17
bufferSize = 0;
18
}
19
20
void FrameData::Init(VulkanContext *vulkan, int index) {
21
this->index = index;
22
VkDevice device = vulkan->GetDevice();
23
24
static const VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
25
VkResult res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &acquireSemaphore);
26
_dbg_assert_(res == VK_SUCCESS);
27
28
VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
29
cmd_pool_info.queueFamilyIndex = vulkan->GetGraphicsQueueFamilyIndex();
30
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
31
res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &cmdPoolInit);
32
_dbg_assert_(res == VK_SUCCESS);
33
res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &cmdPoolMain);
34
_dbg_assert_(res == VK_SUCCESS);
35
36
VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
37
cmd_alloc.commandPool = cmdPoolInit;
38
cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
39
cmd_alloc.commandBufferCount = 1;
40
res = vkAllocateCommandBuffers(device, &cmd_alloc, &initCmd);
41
_dbg_assert_(res == VK_SUCCESS);
42
cmd_alloc.commandPool = cmdPoolMain;
43
res = vkAllocateCommandBuffers(device, &cmd_alloc, &mainCmd);
44
res = vkAllocateCommandBuffers(device, &cmd_alloc, &presentCmd);
45
_dbg_assert_(res == VK_SUCCESS);
46
47
vulkan->SetDebugName(initCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("initCmd%d", index).c_str());
48
vulkan->SetDebugName(mainCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("mainCmd%d", index).c_str());
49
vulkan->SetDebugName(presentCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("presentCmd%d", index).c_str());
50
51
// Creating the frame fence with true so they can be instantly waited on the first frame
52
fence = vulkan->CreateFence(true);
53
vulkan->SetDebugName(fence, VK_OBJECT_TYPE_FENCE, StringFromFormat("fence%d", index).c_str());
54
readyForFence = true;
55
56
VkQueryPoolCreateInfo query_ci{ VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO };
57
query_ci.queryCount = MAX_TIMESTAMP_QUERIES;
58
query_ci.queryType = VK_QUERY_TYPE_TIMESTAMP;
59
res = vkCreateQueryPool(device, &query_ci, nullptr, &profile.queryPool);
60
}
61
62
void FrameData::Destroy(VulkanContext *vulkan) {
63
VkDevice device = vulkan->GetDevice();
64
vkDestroyCommandPool(device, cmdPoolInit, nullptr);
65
vkDestroyCommandPool(device, cmdPoolMain, nullptr);
66
vkDestroyFence(device, fence, nullptr);
67
vkDestroyQueryPool(device, profile.queryPool, nullptr);
68
vkDestroySemaphore(device, acquireSemaphore, nullptr);
69
70
readbacks_.IterateMut([=](const ReadbackKey &key, CachedReadback *value) {
71
value->Destroy(vulkan);
72
delete value;
73
});
74
readbacks_.Clear();
75
}
76
77
void FrameData::AcquireNextImage(VulkanContext *vulkan) {
78
_dbg_assert_(!hasAcquired);
79
80
// Get the index of the next available swapchain image, and a semaphore to block command buffer execution on.
81
VkResult res = vkAcquireNextImageKHR(vulkan->GetDevice(), vulkan->GetSwapchain(), UINT64_MAX, acquireSemaphore, (VkFence)VK_NULL_HANDLE, &curSwapchainImage);
82
switch (res) {
83
case VK_SUCCESS:
84
hasAcquired = true;
85
break;
86
case VK_SUBOPTIMAL_KHR:
87
hasAcquired = true;
88
// Hopefully the resize will happen shortly. Ignore - one frame might look bad or something.
89
WARN_LOG(Log::G3D, "VK_SUBOPTIMAL_KHR returned - ignoring");
90
break;
91
case VK_ERROR_OUT_OF_DATE_KHR:
92
case VK_TIMEOUT:
93
case VK_NOT_READY:
94
// We do not set hasAcquired here!
95
WARN_LOG(Log::G3D, "%s returned from AcquireNextImage - processing the frame, but not presenting", VulkanResultToString(res));
96
skipSwap = true;
97
break;
98
case VK_ERROR_SURFACE_LOST_KHR:
99
ERROR_LOG(Log::G3D, "%s returned from AcquireNextImage - ignoring, but this better be during shutdown", VulkanResultToString(res));
100
skipSwap = true;
101
break;
102
default:
103
// Weird, shouldn't get any other values. Maybe lost device?
104
_assert_msg_(false, "vkAcquireNextImageKHR failed! result=%s", VulkanResultToString(res));
105
break;
106
}
107
}
108
109
VkResult FrameData::QueuePresent(VulkanContext *vulkan, FrameDataShared &shared) {
110
_dbg_assert_(hasAcquired);
111
hasAcquired = false;
112
_dbg_assert_(!skipSwap);
113
114
VkSwapchainKHR swapchain = vulkan->GetSwapchain();
115
VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
116
present.swapchainCount = 1;
117
present.pSwapchains = &swapchain;
118
present.pImageIndices = &curSwapchainImage;
119
present.pWaitSemaphores = &shared.swapchainImages_[curSwapchainImage].renderingCompleteSemaphore;
120
present.waitSemaphoreCount = 1;
121
122
// Can't move these into the if.
123
VkPresentIdKHR presentID{ VK_STRUCTURE_TYPE_PRESENT_ID_KHR };
124
VkPresentTimesInfoGOOGLE presentGOOGLE{ VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE };
125
126
uint64_t frameId = this->frameId;
127
VkPresentTimeGOOGLE presentTimeGOOGLE{ (uint32_t)frameId, 0 }; // it's ok to truncate this. it'll wrap around and work (if we ever reach 4 billion frames..)
128
129
if (shared.measurePresentTime) {
130
if (vulkan->Extensions().KHR_present_id && vulkan->GetDeviceFeatures().enabled.presentId.presentId) {
131
ChainStruct(present, &presentID);
132
presentID.pPresentIds = &frameId;
133
presentID.swapchainCount = 1;
134
} else if (vulkan->Extensions().GOOGLE_display_timing) {
135
ChainStruct(present, &presentGOOGLE);
136
presentGOOGLE.pTimes = &presentTimeGOOGLE;
137
presentGOOGLE.swapchainCount = 1;
138
}
139
}
140
141
return vkQueuePresentKHR(vulkan->GetGraphicsQueue(), &present);
142
}
143
144
VkCommandBuffer FrameData::GetInitCmd(VulkanContext *vulkan) {
145
if (!hasInitCommands) {
146
VkCommandBufferBeginInfo begin = {
147
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
148
nullptr,
149
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
150
};
151
vkResetCommandPool(vulkan->GetDevice(), cmdPoolInit, 0);
152
VkResult res = vkBeginCommandBuffer(initCmd, &begin);
153
if (res != VK_SUCCESS) {
154
return VK_NULL_HANDLE;
155
}
156
157
// Good spot to reset the query pool.
158
if (profile.enabled) {
159
vkCmdResetQueryPool(initCmd, profile.queryPool, 0, MAX_TIMESTAMP_QUERIES);
160
vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, profile.queryPool, 0);
161
}
162
163
hasInitCommands = true;
164
}
165
return initCmd;
166
}
167
168
void FrameData::Submit(VulkanContext *vulkan, FrameSubmitType type, FrameDataShared &sharedData) {
169
VkCommandBuffer cmdBufs[3];
170
int numCmdBufs = 0;
171
172
VkFence fenceToTrigger = VK_NULL_HANDLE;
173
174
if (hasInitCommands) {
175
if (profile.enabled) {
176
// Pre-allocated query ID 1 - end of init cmdbuf.
177
vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, profile.queryPool, 1);
178
}
179
180
VkResult res = vkEndCommandBuffer(initCmd);
181
cmdBufs[numCmdBufs++] = initCmd;
182
183
_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (init)! result=%s", VulkanResultToString(res));
184
hasInitCommands = false;
185
}
186
187
if ((hasMainCommands || hasPresentCommands) && type == FrameSubmitType::Sync) {
188
fenceToTrigger = sharedData.readbackFence;
189
}
190
191
if (hasMainCommands) {
192
VkResult res = vkEndCommandBuffer(mainCmd);
193
_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (main)! result=%s", VulkanResultToString(res));
194
195
cmdBufs[numCmdBufs++] = mainCmd;
196
hasMainCommands = false;
197
}
198
199
if (hasPresentCommands) {
200
_dbg_assert_(type != FrameSubmitType::Pending);
201
VkResult res = vkEndCommandBuffer(presentCmd);
202
203
_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (present)! result=%s", VulkanResultToString(res));
204
205
cmdBufs[numCmdBufs++] = presentCmd;
206
hasPresentCommands = false;
207
}
208
209
if (type == FrameSubmitType::FinishFrame) {
210
_dbg_assert_(!fenceToTrigger);
211
fenceToTrigger = fence;
212
}
213
214
if (!numCmdBufs && fenceToTrigger == VK_NULL_HANDLE) {
215
// Nothing to do.
216
return;
217
}
218
219
VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
220
VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
221
if (type == FrameSubmitType::FinishFrame && !skipSwap) {
222
_dbg_assert_(hasAcquired);
223
submit_info.waitSemaphoreCount = 1;
224
submit_info.pWaitSemaphores = &acquireSemaphore;
225
submit_info.pWaitDstStageMask = waitStage;
226
}
227
submit_info.commandBufferCount = (uint32_t)numCmdBufs;
228
submit_info.pCommandBuffers = cmdBufs;
229
if (type == FrameSubmitType::FinishFrame && !skipSwap) {
230
submit_info.signalSemaphoreCount = 1;
231
submit_info.pSignalSemaphores = &sharedData.swapchainImages_[curSwapchainImage].renderingCompleteSemaphore;
232
}
233
234
VkResult res;
235
if (fenceToTrigger == fence) {
236
VLOG("Doing queue submit, fencing frame %d", this->index);
237
// The fence is waited on by the main thread, they are not allowed to access it simultaneously.
238
res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger);
239
if (sharedData.useMultiThreading) {
240
std::lock_guard<std::mutex> lock(fenceMutex);
241
readyForFence = true;
242
fenceCondVar.notify_one();
243
}
244
} else {
245
VLOG("Doing queue submit, fencing something (%p)", fenceToTrigger);
246
res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger);
247
}
248
249
if (res == VK_ERROR_DEVICE_LOST) {
250
_assert_msg_(false, "Lost the Vulkan device in vkQueueSubmit! If this happens again, switch Graphics Backend away from Vulkan");
251
} else {
252
_assert_msg_(res == VK_SUCCESS, "vkQueueSubmit failed (main)! result=%s", VulkanResultToString(res));
253
}
254
255
if (type == FrameSubmitType::Sync) {
256
// Hard stall of the GPU, not ideal, but necessary so the CPU has the contents of the readback.
257
vkWaitForFences(vulkan->GetDevice(), 1, &sharedData.readbackFence, true, UINT64_MAX);
258
vkResetFences(vulkan->GetDevice(), 1, &sharedData.readbackFence);
259
syncDone = true;
260
}
261
}
262
263
void FrameDataShared::Init(VulkanContext *vulkan, bool useMultiThreading, bool measurePresentTime) {
264
// This fence is used for synchronizing readbacks. Does not need preinitialization.
265
readbackFence = vulkan->CreateFence(false);
266
vulkan->SetDebugName(readbackFence, VK_OBJECT_TYPE_FENCE, "readbackFence");
267
268
this->useMultiThreading = useMultiThreading;
269
this->measurePresentTime = measurePresentTime;
270
}
271
272
void FrameDataShared::Destroy(VulkanContext *vulkan) {
273
VkDevice device = vulkan->GetDevice();
274
vkDestroyFence(device, readbackFence, nullptr);
275
}
276
277