Skip to content

Commit 348d866

Browse files
Vulkan: Refactor swapchain code (#399)
1 parent 592c9b2 commit 348d866

File tree

7 files changed

+613
-609
lines changed

7 files changed

+613
-609
lines changed

src/Cafe/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ add_library(CemuCafe
178178
HW/Latte/Renderer/Vulkan/LatteTextureVk.h
179179
HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp
180180
HW/Latte/Renderer/Vulkan/RendererShaderVk.h
181+
HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp
182+
HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h
181183
HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp
182184
HW/Latte/Renderer/Vulkan/VKRBase.h
183185
HW/Latte/Renderer/Vulkan/VKRMemoryManager.cpp
Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
#include "SwapchainInfoVk.h"
2+
3+
#include "config/CemuConfig.h"
4+
#include "Cafe/HW/Latte/Core/Latte.h"
5+
#include "Cafe/HW/Latte/Core/LatteTiming.h"
6+
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h"
7+
8+
void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDevice)
9+
{
10+
m_physicalDevice = physicalDevice;
11+
m_logicalDevice = logicalDevice;
12+
const auto details = QuerySwapchainSupport(surface, physicalDevice);
13+
m_surfaceFormat = ChooseSurfaceFormat(details.formats);
14+
swapchainExtent = ChooseSwapExtent(details.capabilities, getSize());
15+
16+
// calculate number of swapchain presentation images
17+
uint32_t image_count = details.capabilities.minImageCount + 1;
18+
if (details.capabilities.maxImageCount > 0 && image_count > details.capabilities.maxImageCount)
19+
image_count = details.capabilities.maxImageCount;
20+
21+
VkSwapchainCreateInfoKHR create_info = CreateSwapchainCreateInfo(surface, details, m_surfaceFormat, image_count, swapchainExtent);
22+
create_info.oldSwapchain = nullptr;
23+
create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
24+
25+
VkResult result = vkCreateSwapchainKHR(logicalDevice, &create_info, nullptr, &swapchain);
26+
if (result != VK_SUCCESS)
27+
UnrecoverableError("Error attempting to create a swapchain");
28+
29+
sizeOutOfDate = false;
30+
31+
result = vkGetSwapchainImagesKHR(logicalDevice, swapchain, &image_count, nullptr);
32+
if (result != VK_SUCCESS)
33+
UnrecoverableError("Error attempting to retrieve the count of swapchain images");
34+
35+
36+
m_swapchainImages.resize(image_count);
37+
result = vkGetSwapchainImagesKHR(logicalDevice, swapchain, &image_count, m_swapchainImages.data());
38+
if (result != VK_SUCCESS)
39+
UnrecoverableError("Error attempting to retrieve swapchain images");
40+
// create default renderpass
41+
VkAttachmentDescription colorAttachment = {};
42+
colorAttachment.format = m_surfaceFormat.format;
43+
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
44+
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
45+
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
46+
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
47+
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
48+
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
49+
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
50+
51+
VkAttachmentReference colorAttachmentRef = {};
52+
colorAttachmentRef.attachment = 0;
53+
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
54+
VkSubpassDescription subpass = {};
55+
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
56+
subpass.colorAttachmentCount = 1;
57+
subpass.pColorAttachments = &colorAttachmentRef;
58+
59+
VkRenderPassCreateInfo renderPassInfo = {};
60+
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
61+
renderPassInfo.attachmentCount = 1;
62+
renderPassInfo.pAttachments = &colorAttachment;
63+
renderPassInfo.subpassCount = 1;
64+
renderPassInfo.pSubpasses = &subpass;
65+
result = vkCreateRenderPass(logicalDevice, &renderPassInfo, nullptr, &m_swapchainRenderPass);
66+
if (result != VK_SUCCESS)
67+
UnrecoverableError("Failed to create renderpass for swapchain");
68+
69+
// create swapchain image views
70+
m_swapchainImageViews.resize(m_swapchainImages.size());
71+
for (sint32 i = 0; i < m_swapchainImages.size(); i++)
72+
{
73+
VkImageViewCreateInfo createInfo = {};
74+
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
75+
createInfo.image = m_swapchainImages[i];
76+
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
77+
createInfo.format = m_surfaceFormat.format;
78+
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
79+
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
80+
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
81+
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
82+
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
83+
createInfo.subresourceRange.baseMipLevel = 0;
84+
createInfo.subresourceRange.levelCount = 1;
85+
createInfo.subresourceRange.baseArrayLayer = 0;
86+
createInfo.subresourceRange.layerCount = 1;
87+
result = vkCreateImageView(logicalDevice, &createInfo, nullptr, &m_swapchainImageViews[i]);
88+
if (result != VK_SUCCESS)
89+
UnrecoverableError("Failed to create imageviews for swapchain");
90+
}
91+
92+
// create swapchain framebuffers
93+
m_swapchainFramebuffers.resize(m_swapchainImages.size());
94+
for (size_t i = 0; i < m_swapchainImages.size(); i++)
95+
{
96+
VkImageView attachments[1];
97+
attachments[0] = m_swapchainImageViews[i];
98+
// create framebuffer
99+
VkFramebufferCreateInfo framebufferInfo = {};
100+
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
101+
framebufferInfo.renderPass = m_swapchainRenderPass;
102+
framebufferInfo.attachmentCount = 1;
103+
framebufferInfo.pAttachments = attachments;
104+
framebufferInfo.width = swapchainExtent.width;
105+
framebufferInfo.height = swapchainExtent.height;
106+
framebufferInfo.layers = 1;
107+
result = vkCreateFramebuffer(logicalDevice, &framebufferInfo, nullptr, &m_swapchainFramebuffers[i]);
108+
if (result != VK_SUCCESS)
109+
UnrecoverableError("Failed to create framebuffer for swapchain");
110+
}
111+
m_swapchainPresentSemaphores.resize(m_swapchainImages.size());
112+
// create present semaphore
113+
VkSemaphoreCreateInfo info = {};
114+
info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
115+
for (auto& semaphore : m_swapchainPresentSemaphores){
116+
if (vkCreateSemaphore(logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS)
117+
UnrecoverableError("Failed to create semaphore for swapchain present");
118+
}
119+
120+
m_acquireSemaphores.resize(m_swapchainImages.size());
121+
for (auto& semaphore : m_acquireSemaphores)
122+
{
123+
VkSemaphoreCreateInfo info = {};
124+
info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
125+
if (vkCreateSemaphore(logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS)
126+
UnrecoverableError("Failed to create semaphore for swapchain acquire");
127+
}
128+
m_acquireIndex = 0;
129+
130+
VkFenceCreateInfo fenceInfo = {};
131+
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
132+
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
133+
result = vkCreateFence(logicalDevice, &fenceInfo, nullptr, &m_imageAvailableFence);
134+
if (result != VK_SUCCESS)
135+
UnrecoverableError("Failed to create fence for swapchain");
136+
}
137+
138+
void SwapchainInfoVk::Cleanup()
139+
{
140+
m_swapchainImages.clear();
141+
142+
for (auto& sem: m_swapchainPresentSemaphores)
143+
vkDestroySemaphore(m_logicalDevice, sem, nullptr);
144+
m_swapchainPresentSemaphores.clear();
145+
146+
for (auto& itr: m_acquireSemaphores)
147+
vkDestroySemaphore(m_logicalDevice, itr, nullptr);
148+
m_acquireSemaphores.clear();
149+
150+
if (m_swapchainRenderPass)
151+
{
152+
vkDestroyRenderPass(m_logicalDevice, m_swapchainRenderPass, nullptr);
153+
m_swapchainRenderPass = nullptr;
154+
}
155+
156+
for (auto& imageView : m_swapchainImageViews)
157+
vkDestroyImageView(m_logicalDevice, imageView, nullptr);
158+
m_swapchainImageViews.clear();
159+
160+
for (auto& framebuffer : m_swapchainFramebuffers)
161+
vkDestroyFramebuffer(m_logicalDevice, framebuffer, nullptr);
162+
m_swapchainFramebuffers.clear();
163+
164+
165+
if (m_imageAvailableFence)
166+
{
167+
vkDestroyFence(m_logicalDevice, m_imageAvailableFence, nullptr);
168+
m_imageAvailableFence = nullptr;
169+
}
170+
if (swapchain)
171+
{
172+
vkDestroySwapchainKHR(m_logicalDevice, swapchain, nullptr);
173+
swapchain = VK_NULL_HANDLE;
174+
}
175+
}
176+
177+
bool SwapchainInfoVk::IsValid() const
178+
{
179+
return swapchain && m_imageAvailableFence;
180+
}
181+
182+
void SwapchainInfoVk::UnrecoverableError(const char* errMsg)
183+
{
184+
forceLog_printf("Unrecoverable error in Vulkan swapchain");
185+
forceLog_printf("Msg: %s", errMsg);
186+
throw std::runtime_error(errMsg);
187+
}
188+
189+
SwapchainInfoVk::QueueFamilyIndices SwapchainInfoVk::FindQueueFamilies(VkSurfaceKHR surface, VkPhysicalDevice device)
190+
{
191+
uint32_t queueFamilyCount = 0;
192+
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
193+
194+
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
195+
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
196+
197+
QueueFamilyIndices indices;
198+
for (int i = 0; i < (int)queueFamilies.size(); ++i)
199+
{
200+
const auto& queueFamily = queueFamilies[i];
201+
if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
202+
indices.graphicsFamily = i;
203+
204+
VkBool32 presentSupport = false;
205+
const VkResult result = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
206+
if (result != VK_SUCCESS)
207+
throw std::runtime_error(fmt::format("Error while attempting to check if a surface supports presentation: {}", result));
208+
209+
if (queueFamily.queueCount > 0 && presentSupport)
210+
indices.presentFamily = i;
211+
212+
if (indices.IsComplete())
213+
break;
214+
}
215+
216+
return indices;
217+
}
218+
219+
SwapchainInfoVk::SwapchainSupportDetails SwapchainInfoVk::QuerySwapchainSupport(VkSurfaceKHR surface, const VkPhysicalDevice& device)
220+
{
221+
SwapchainSupportDetails details;
222+
223+
VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
224+
if (result != VK_SUCCESS)
225+
{
226+
if (result != VK_ERROR_SURFACE_LOST_KHR)
227+
forceLog_printf("vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed. Error %d", (sint32)result);
228+
throw std::runtime_error(fmt::format("Unable to retrieve physical device surface capabilities: {}", result));
229+
}
230+
231+
uint32_t formatCount = 0;
232+
result = vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
233+
if (result != VK_SUCCESS)
234+
{
235+
forceLog_printf("vkGetPhysicalDeviceSurfaceFormatsKHR failed. Error %d", (sint32)result);
236+
throw std::runtime_error(fmt::format("Unable to retrieve the number of formats for a surface on a physical device: {}", result));
237+
}
238+
239+
if (formatCount != 0)
240+
{
241+
details.formats.resize(formatCount);
242+
result = vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
243+
if (result != VK_SUCCESS)
244+
{
245+
forceLog_printf("vkGetPhysicalDeviceSurfaceFormatsKHR failed. Error %d", (sint32)result);
246+
throw std::runtime_error(fmt::format("Unable to retrieve the formats for a surface on a physical device: {}", result));
247+
}
248+
}
249+
250+
uint32_t presentModeCount = 0;
251+
result = vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
252+
if (result != VK_SUCCESS)
253+
{
254+
forceLog_printf("vkGetPhysicalDeviceSurfacePresentModesKHR failed. Error %d", (sint32)result);
255+
throw std::runtime_error(fmt::format("Unable to retrieve the count of present modes for a surface on a physical device: {}", result));
256+
}
257+
258+
if (presentModeCount != 0)
259+
{
260+
details.presentModes.resize(presentModeCount);
261+
result = vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
262+
if (result != VK_SUCCESS)
263+
{
264+
forceLog_printf("vkGetPhysicalDeviceSurfacePresentModesKHR failed. Error %d", (sint32)result);
265+
throw std::runtime_error(fmt::format("Unable to retrieve the present modes for a surface on a physical device: {}", result));
266+
}
267+
}
268+
269+
return details;
270+
}
271+
272+
VkSurfaceFormatKHR SwapchainInfoVk::ChooseSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& formats) const
273+
{
274+
if (formats.size() == 1 && formats[0].format == VK_FORMAT_UNDEFINED)
275+
return{ VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
276+
277+
for (const auto& format : formats)
278+
{
279+
bool useSRGB = mainWindow ? LatteGPUState.tvBufferUsesSRGB : LatteGPUState.drcBufferUsesSRGB;
280+
281+
if (useSRGB)
282+
{
283+
if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
284+
return format;
285+
}
286+
else
287+
{
288+
if (format.format == VK_FORMAT_B8G8R8A8_UNORM && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
289+
return format;
290+
}
291+
}
292+
293+
return formats[0];
294+
}
295+
296+
VkExtent2D SwapchainInfoVk::ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, const Vector2i& size) const
297+
{
298+
if (capabilities.currentExtent.width != std::numeric_limits<uint32>::max())
299+
return capabilities.currentExtent;
300+
301+
VkExtent2D actualExtent = { (uint32)size.x, (uint32)size.y };
302+
actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width));
303+
actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height));
304+
return actualExtent;
305+
}
306+
307+
VkPresentModeKHR SwapchainInfoVk::ChoosePresentMode(const std::vector<VkPresentModeKHR>& modes)
308+
{
309+
const auto vsyncState = (VSync)GetConfig().vsync.GetValue();
310+
if (vsyncState == VSync::MAILBOX)
311+
{
312+
if (std::find(modes.cbegin(), modes.cend(), VK_PRESENT_MODE_MAILBOX_KHR) != modes.cend())
313+
return VK_PRESENT_MODE_MAILBOX_KHR;
314+
315+
forceLog_printf("Vulkan: Can't find mailbox present mode");
316+
}
317+
else if (vsyncState == VSync::Immediate)
318+
{
319+
if (std::find(modes.cbegin(), modes.cend(), VK_PRESENT_MODE_IMMEDIATE_KHR) != modes.cend())
320+
return VK_PRESENT_MODE_IMMEDIATE_KHR;
321+
322+
forceLog_printf("Vulkan: Can't find immediate present mode");
323+
}
324+
else if (vsyncState == VSync::SYNC_AND_LIMIT)
325+
{
326+
LatteTiming_EnableHostDrivenVSync();
327+
// use immediate mode if available, other wise fall back to
328+
//if (std::find(modes.cbegin(), modes.cend(), VK_PRESENT_MODE_IMMEDIATE_KHR) != modes.cend())
329+
// return VK_PRESENT_MODE_IMMEDIATE_KHR;
330+
//else
331+
// forceLog_printf("Vulkan: Present mode 'immediate' not available. Vsync might not behave as intended");
332+
return VK_PRESENT_MODE_FIFO_KHR;
333+
}
334+
335+
return VK_PRESENT_MODE_FIFO_KHR;
336+
}
337+
338+
VkSwapchainCreateInfoKHR SwapchainInfoVk::CreateSwapchainCreateInfo(VkSurfaceKHR surface, const SwapchainSupportDetails& swapchainSupport, const VkSurfaceFormatKHR& surfaceFormat, uint32 imageCount, const VkExtent2D& extent)
339+
{
340+
VkSwapchainCreateInfoKHR createInfo{};
341+
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
342+
createInfo.surface = surface;
343+
createInfo.minImageCount = imageCount;
344+
createInfo.imageFormat = surfaceFormat.format;
345+
createInfo.imageExtent = extent;
346+
createInfo.imageArrayLayers = 1;
347+
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
348+
349+
const QueueFamilyIndices indices = FindQueueFamilies(surface, m_physicalDevice);
350+
uint32_t queueFamilyIndices[] = { (uint32)indices.graphicsFamily, (uint32)indices.presentFamily };
351+
if (indices.graphicsFamily != indices.presentFamily)
352+
{
353+
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
354+
createInfo.queueFamilyIndexCount = 2;
355+
createInfo.pQueueFamilyIndices = queueFamilyIndices;
356+
}
357+
else
358+
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
359+
360+
createInfo.preTransform = swapchainSupport.capabilities.currentTransform;
361+
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
362+
createInfo.presentMode = ChoosePresentMode(swapchainSupport.presentModes);
363+
createInfo.clipped = VK_TRUE;
364+
365+
forceLogDebug_printf("vulkan presentation mode: %d", createInfo.presentMode);
366+
return createInfo;
367+
}

0 commit comments

Comments
 (0)