1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "swapchain_vk.h"
17
18 #include <algorithm>
19 #include <cstddef>
20 #include <cstdint>
21 #include <vulkan/vulkan_core.h>
22
23 #include <base/containers/vector.h>
24 #include <base/math/mathf.h>
25 #include <base/util/formats.h>
26 #include <core/intf_engine.h>
27 #include <render/device/gpu_resource_desc.h>
28 #include <render/namespace.h>
29 #include <render/vulkan/intf_device_vk.h>
30
31 #include "util/log.h"
32 #include "vulkan/create_functions_vk.h"
33 #include "vulkan/device_vk.h"
34 #include "vulkan/validate_vk.h"
35
36 using namespace BASE_NS;
37
38 RENDER_BEGIN_NAMESPACE()
39 namespace {
GetValidDepthFormat(const DeviceVk & deviceVk)40 Format GetValidDepthFormat(const DeviceVk& deviceVk)
41 {
42 constexpr uint32_t PREFERRED_FORMAT_COUNT { 3 };
43 constexpr Format preferredFormats[PREFERRED_FORMAT_COUNT] = { BASE_FORMAT_D24_UNORM_S8_UINT, BASE_FORMAT_D32_SFLOAT,
44 BASE_FORMAT_D16_UNORM };
45 #ifndef NDEBUG
46 constexpr string_view PREFERRED_FORMAT_NAMES[PREFERRED_FORMAT_COUNT] = { "BASE_FORMAT_D24_UNORM_S8_UINT",
47 "BASE_FORMAT_D32_SFLOAT", "BASE_FORMAT_D16_UNORM" };
48 #endif
49 Format finalFormat = BASE_FORMAT_UNDEFINED;
50 const auto& devPlat = deviceVk.GetPlatformInternalDataVk();
51 for (uint32_t idx = 0; idx < PREFERRED_FORMAT_COUNT; ++idx) {
52 finalFormat = preferredFormats[idx];
53 for (const auto& supportedDepthFormat : devPlat.supportedDepthFormats) {
54 if (finalFormat == supportedDepthFormat) {
55 #ifndef NDEBUG
56 PLUGIN_LOG_D(
57 "selected CORE_DEFAULT_BACKBUFFER format: %s", string(PREFERRED_FORMAT_NAMES[idx]).c_str());
58 #endif
59 idx = PREFERRED_FORMAT_COUNT;
60 break;
61 }
62 }
63 }
64 return finalFormat;
65 }
66
67 struct ColorInfo {
68 VkFormat format { VK_FORMAT_UNDEFINED };
69 VkColorSpaceKHR colorSpace { VK_COLOR_SPACE_MAX_ENUM_KHR };
70 };
71
GetColorFormat(const uint32_t flags,const vector<VkSurfaceFormatKHR> & surfaceFormats)72 VkFormat GetColorFormat(const uint32_t flags, const vector<VkSurfaceFormatKHR>& surfaceFormats)
73 {
74 constexpr uint32_t preferredFormatCount { 4u };
75 constexpr VkFormat srgbFormats[preferredFormatCount] = { VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_B8G8R8A8_SRGB,
76 VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM };
77 constexpr VkFormat nonSrgbFormats[preferredFormatCount] = { VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM,
78 VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_B8G8R8A8_SRGB };
79
80 const bool preferSrgbFormat = (flags & SwapchainFlagBits::CORE_SWAPCHAIN_SRGB_BIT);
81 const array_view<const VkFormat> formats =
82 (preferSrgbFormat) ? array_view<const VkFormat> { srgbFormats, preferredFormatCount }
83 : array_view<const VkFormat> { nonSrgbFormats, preferredFormatCount };
84
85 // If pSurfaceFormats includes just one entry, whose value for format is VK_FORMAT_UNDEFINED,
86 // surface has no preferred format. In this case, the application can use any valid VkFormat value.
87 if (surfaceFormats[0].format == VK_FORMAT_UNDEFINED && surfaceFormats.size() == 1) {
88 return VK_FORMAT_R8G8B8A8_SRGB;
89 }
90
91 for (size_t idx = 0; idx < formats.size(); ++idx) {
92 for (size_t jdx = 0; jdx < surfaceFormats.size(); ++jdx) {
93 if (formats[idx] == surfaceFormats[jdx].format) {
94 return surfaceFormats[jdx].format;
95 }
96 }
97 }
98 return VK_FORMAT_UNDEFINED;
99 }
100
GetColorInfo(const VkPhysicalDevice physicalDevice,const VkSurfaceKHR surface,const uint32_t flags)101 ColorInfo GetColorInfo(const VkPhysicalDevice physicalDevice, const VkSurfaceKHR surface, const uint32_t flags)
102 {
103 // Pick a color format for the swapchain.
104 uint32_t surfaceFormatsCount = 0;
105 VALIDATE_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatsCount, nullptr));
106
107 vector<VkSurfaceFormatKHR> surfaceFormats(surfaceFormatsCount);
108 VALIDATE_VK_RESULT(
109 vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatsCount, surfaceFormats.data()));
110
111 ColorInfo ci;
112 ci.format = GetColorFormat(flags, surfaceFormats);
113 ci.colorSpace = VK_COLOR_SPACE_MAX_ENUM_KHR;
114 for (size_t idx = 0; idx < surfaceFormats.size(); ++idx) {
115 if (surfaceFormats[idx].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
116 ci.colorSpace = surfaceFormats[idx].colorSpace;
117 break;
118 }
119 }
120 PLUGIN_ASSERT_MSG(ci.colorSpace != VK_COLOR_SPACE_MAX_ENUM_KHR, "colorspace not correct");
121
122 PLUGIN_ASSERT_MSG(ci.format != VK_FORMAT_UNDEFINED, "colorformat not correct");
123 PLUGIN_LOG_D("swapchainColorFormat: %u swapchainColorSpace %u", ci.format, ci.colorSpace);
124
125 return ci;
126 }
127
GetPresentMode(const VkPhysicalDevice physicalDevice,const VkSurfaceKHR surface,const uint32_t flags)128 VkPresentModeKHR GetPresentMode(const VkPhysicalDevice physicalDevice, const VkSurfaceKHR surface, const uint32_t flags)
129 {
130 // Pick a present mode for the swapchain.
131 uint32_t presentModeCount;
132 VALIDATE_VK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, nullptr));
133
134 vector<VkPresentModeKHR> presentModes(presentModeCount);
135 VALIDATE_VK_RESULT(
136 vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes.data()));
137
138 VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; // FIFO must be supported by the specification.
139 if ((flags & SwapchainFlagBits::CORE_SWAPCHAIN_VSYNC_BIT) != SwapchainFlagBits::CORE_SWAPCHAIN_VSYNC_BIT) {
140 // immediate is really without vsync, but it might not be supported, so we also check for mailbox.
141 if (std::any_of(presentModes.cbegin(), presentModes.cend(),
142 [](const VkPresentModeKHR& supported) { return supported == VK_PRESENT_MODE_IMMEDIATE_KHR; })) {
143 swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
144 } else if (std::any_of(presentModes.cbegin(), presentModes.cend(),
145 [](const VkPresentModeKHR& supported) { return supported == VK_PRESENT_MODE_MAILBOX_KHR; })) {
146 swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
147 }
148 }
149
150 #if (RENDER_DEV_ENABLED == 1)
151 constexpr uint32_t strArraySize { 4 };
152 constexpr string_view presentModeStrings[strArraySize] = {
153 "VK_PRESENT_MODE_IMMEDIATE_KHR",
154 "VK_PRESENT_MODE_MAILBOX_KHR",
155 "VK_PRESENT_MODE_FIFO_KHR",
156 "VK_PRESENT_MODE_FIFO_RELAXED_KHR",
157 };
158
159 PLUGIN_LOG_I("Available swapchain present modes:");
160 for (auto const presentMode : presentModes) {
161 if (static_cast<uint32_t>(presentMode) < strArraySize) {
162 PLUGIN_LOG_I(" %s", presentModeStrings[presentMode].data());
163 }
164 }
165 PLUGIN_LOG_I("Selected swapchain present modes:");
166 if (static_cast<uint32_t>(swapchainPresentMode) < strArraySize) {
167 PLUGIN_LOG_I(" %s", presentModeStrings[swapchainPresentMode].data());
168 }
169 #else
170 PLUGIN_LOG_D("swapchainPresentMode: %x", swapchainPresentMode);
171 #endif
172
173 return swapchainPresentMode;
174 }
175
ClampSwapchainExtent(const VkSurfaceCapabilitiesKHR & surfaceCapabilities,VkExtent2D & extent)176 void ClampSwapchainExtent(const VkSurfaceCapabilitiesKHR& surfaceCapabilities, VkExtent2D& extent)
177 {
178 extent.width =
179 std::clamp(extent.width, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width);
180 extent.height =
181 std::clamp(extent.height, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height);
182
183 PLUGIN_LOG_D("swapchainExtent: %u x %u", extent.width, extent.height);
184 if ((extent.width == 0) || (extent.height == 0)) {
185 PLUGIN_LOG_E(
186 "zero sized swapchain cannot be created in vulkan (width: %u, height: %u)", extent.width, extent.height);
187 PLUGIN_LOG_E("using 1x1 swapchain");
188 PLUGIN_ASSERT(false);
189 extent.width = 1;
190 extent.height = 1;
191 }
192 }
193
GetColorDesc(const uint32_t width,const uint32_t height,const Format format,const ImageUsageFlags imageUsageFlags)194 constexpr GpuImageDesc GetColorDesc(
195 const uint32_t width, const uint32_t height, const Format format, const ImageUsageFlags imageUsageFlags)
196 {
197 return {
198 ImageType::CORE_IMAGE_TYPE_2D, // imageType
199 ImageViewType::CORE_IMAGE_VIEW_TYPE_2D, // imageViewType
200 format, // format
201 ImageTiling::CORE_IMAGE_TILING_OPTIMAL, // imageTiling
202 imageUsageFlags, // usageFlags
203 MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, // memoryPropertyFlags
204 0, // createFlags
205 EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS |
206 EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_RESET_STATE_ON_FRAME_BORDERS, // engineCreationFlags
207 width, // width
208 height, // height
209 1, // depth
210 1, // mipCount
211 1, // layerCount
212 SampleCountFlagBits::CORE_SAMPLE_COUNT_1_BIT, // sampleCountFlags
213 {}, // componentMapping
214 };
215 }
216
GetDepthDesc(const uint32_t width,const uint32_t height,const Format format)217 constexpr GpuImageDesc GetDepthDesc(const uint32_t width, const uint32_t height, const Format format)
218 {
219 return {
220 ImageType::CORE_IMAGE_TYPE_2D, // imageType
221 ImageViewType::CORE_IMAGE_VIEW_TYPE_2D, // imageViewType
222 format, // format
223 ImageTiling::CORE_IMAGE_TILING_OPTIMAL, // imageTiling
224 ImageUsageFlagBits::CORE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
225 ImageUsageFlagBits::CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT |
226 ImageUsageFlagBits::CORE_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, // usageFlags
227 MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
228 MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, // memoryPropertyFlags
229 0, // createFlags
230 EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS, // engineCreationFlags
231 width, // width
232 height, // height
233 1, // depth
234 1, // mipCount
235 1, // layerCount
236 SampleCountFlagBits::CORE_SAMPLE_COUNT_1_BIT, // sampleCountFlags
237 {}, // componentMapping
238 };
239 }
240 } // namespace
241
SwapchainVk(Device & device,const SwapchainCreateInfo & swapchainCreateInfo)242 SwapchainVk::SwapchainVk(Device& device, const SwapchainCreateInfo& swapchainCreateInfo)
243 : device_(device), flags_(swapchainCreateInfo.swapchainFlags)
244 {
245 const auto& devicePlatformData = (const DevicePlatformDataVk&)device_.GetPlatformData();
246 auto const physicalDevice = devicePlatformData.physicalDevice;
247 // check for surface creation automatically
248 if ((swapchainCreateInfo.surfaceHandle == 0) && swapchainCreateInfo.window.window) {
249 CreateFunctionsVk::Window win;
250 win.instance = swapchainCreateInfo.window.instance;
251 win.window = swapchainCreateInfo.window.window;
252 surface_ = CreateFunctionsVk::CreateSurface(devicePlatformData.instance, win);
253 ownsSurface_ = true;
254 } else {
255 surface_ = VulkanHandleCast<VkSurfaceKHR>(swapchainCreateInfo.surfaceHandle);
256 }
257
258 if (surface_ != VK_NULL_HANDLE) {
259 auto const vkDevice = devicePlatformData.device;
260
261 // Sanity check that the device can use the surface.
262 // NOTE: queuFamilyIndex hardcoded, should come via devicePlatformData?
263 VkBool32 surfaceSupported = VK_FALSE;
264 VALIDATE_VK_RESULT(vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, 0, surface_, &surfaceSupported));
265 PLUGIN_ASSERT_MSG(surfaceSupported != VK_FALSE, "physicalDevice doesn't support given surface");
266
267 const ColorInfo ci = GetColorInfo(physicalDevice, surface_, flags_);
268
269 const VkPresentModeKHR swapchainPresentMode = GetPresentMode(physicalDevice, surface_, flags_);
270 // Pick an extent, image count, and transform for the swapchain.
271 VkSurfaceCapabilitiesKHR surfaceCapabilities;
272 VALIDATE_VK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface_, &surfaceCapabilities));
273
274 // NOTE: how do we handle the special case of 0xffffffffff which means the extent should be defined by the
275 // swapchain?
276 VkExtent2D swapchainExtent = surfaceCapabilities.currentExtent;
277 ClampSwapchainExtent(surfaceCapabilities, swapchainExtent);
278 plat_.swapchainImages.width = swapchainExtent.width;
279 plat_.swapchainImages.height = swapchainExtent.height;
280
281 const DeviceConfiguration deviceConfig = device_.GetDeviceConfiguration();
282 // surfaceCapabilities.maxImageCount of zero means that there is no limit
283 const uint32_t imageCount =
284 (surfaceCapabilities.maxImageCount == 0)
285 ? (Math::max(surfaceCapabilities.minImageCount, deviceConfig.swapchainImageCount))
286 : (Math::min(surfaceCapabilities.maxImageCount,
287 Math::max(surfaceCapabilities.minImageCount, deviceConfig.swapchainImageCount)));
288 PLUGIN_LOG_D("swapchainImageCount: %u", imageCount);
289
290 const VkSurfaceTransformFlagsKHR swapchainTransform =
291 (surfaceCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
292 ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
293 : surfaceCapabilities.currentTransform;
294
295 const VkImageUsageFlags desiredUsageFlags = static_cast<VkImageUsageFlags>(swapchainCreateInfo.imageUsageFlags);
296 const VkImageUsageFlags imageUsageFlags = desiredUsageFlags & surfaceCapabilities.supportedUsageFlags;
297 PLUGIN_LOG_D("swapchain usage flags, selected: %u, desired: %u, capabilities: %u", imageUsageFlags,
298 desiredUsageFlags, surfaceCapabilities.supportedUsageFlags);
299
300 VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
301 if (surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) {
302 compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
303 }
304
305 VkSwapchainCreateInfoKHR const vkSwapchainCreateInfo {
306 VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, // sType
307 nullptr, // pNext
308 0, // flags
309 surface_, // surface
310 imageCount, // minImageCount
311 ci.format, // imageFormat
312 ci.colorSpace, // imageColorSpace
313 swapchainExtent, // imageExtent
314 1, // imageArrayLayers
315 imageUsageFlags, // imageUsage
316 VK_SHARING_MODE_EXCLUSIVE, // imageSharingMode
317 0, // queueFamilyIndexCount
318 nullptr, // pQueueFamilyIndices
319 (VkSurfaceTransformFlagBitsKHR)swapchainTransform, // preTransform
320 compositeAlpha, // compositeAlpha
321 swapchainPresentMode, // presentMode
322 VK_TRUE, // clipped
323 VK_NULL_HANDLE, // oldSwapchain
324 };
325
326 VALIDATE_VK_RESULT(vkCreateSwapchainKHR(vkDevice, &vkSwapchainCreateInfo, nullptr, &plat_.swapchain));
327
328 {
329 uint32_t realImageCount = 0;
330 VALIDATE_VK_RESULT(vkGetSwapchainImagesKHR(vkDevice, // device
331 plat_.swapchain, // swapchain
332 &realImageCount, // pSwapchainImageCount
333 nullptr)); // pSwapchainImages
334
335 PLUGIN_LOG_D("swapchain realImageCount: %u", realImageCount);
336
337 plat_.swapchainImages.images.resize(realImageCount);
338 plat_.swapchainImages.imageViews.resize(realImageCount);
339 plat_.swapchainImages.semaphores.resize(realImageCount);
340
341 VALIDATE_VK_RESULT(vkGetSwapchainImagesKHR(vkDevice, // device
342 plat_.swapchain, // swapchain
343 &realImageCount, // pSwapchainImageCount
344 plat_.swapchainImages.images.data())); // pSwapchainImages
345
346 constexpr VkComponentMapping componentMapping {
347 VK_COMPONENT_SWIZZLE_IDENTITY, // r
348 VK_COMPONENT_SWIZZLE_IDENTITY, // g
349 VK_COMPONENT_SWIZZLE_IDENTITY, // b
350 VK_COMPONENT_SWIZZLE_IDENTITY, // a
351 };
352 constexpr VkImageSubresourceRange imageSubresourceRange {
353 VK_IMAGE_ASPECT_COLOR_BIT, // aspectMask
354 0, // baseMipLevel
355 1, // levelCount
356 0, // baseArrayLayer
357 1, // layerCount
358 };
359
360 constexpr VkSemaphoreCreateFlags semaphoreCreateFlags { 0 };
361 const VkSemaphoreCreateInfo semaphoreCreateInfo {
362 VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, // sType
363 nullptr, // pNext
364 semaphoreCreateFlags, // flags
365 };
366 for (uint32_t idx = 0; idx < plat_.swapchainImages.imageViews.size(); ++idx) {
367 const VkImageViewCreateInfo imageViewCreateInfo {
368 VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // sType
369 nullptr, // pNext
370 0, // flags
371 plat_.swapchainImages.images[idx], // image
372 VK_IMAGE_VIEW_TYPE_2D, // viewType
373 ci.format, // format
374 componentMapping, // components
375 imageSubresourceRange // subresourceRange;
376 };
377 VALIDATE_VK_RESULT(vkCreateImageView(vkDevice, // device
378 &imageViewCreateInfo, // pCreateInfo
379 nullptr, // pAllocator
380 &plat_.swapchainImages.imageViews[idx])); // pView
381 VALIDATE_VK_RESULT(vkCreateSemaphore(vkDevice, // device
382 &semaphoreCreateInfo, // pCreateInfo
383 nullptr, // pAllocator
384 &plat_.swapchainImages.semaphores[idx])); // pSemaphore
385 }
386 }
387
388 desc_ = GetColorDesc(plat_.swapchainImages.width, plat_.swapchainImages.height, (Format)ci.format,
389 (ImageUsageFlags)imageUsageFlags);
390
391 if (flags_ & 0x2) {
392 const Format depthFormat = GetValidDepthFormat((const DeviceVk&)device_);
393 descDepthBuffer_ = GetDepthDesc(plat_.swapchainImages.width, plat_.swapchainImages.height, depthFormat);
394 }
395 } else {
396 PLUGIN_LOG_E("Invalid surface in swapchain creation");
397 }
398 }
399
~SwapchainVk()400 SwapchainVk::~SwapchainVk()
401 {
402 const auto& devicePlatformData = (const DevicePlatformDataVk&)device_.GetPlatformData();
403 const VkDevice device = devicePlatformData.device;
404 for (auto const imageView : plat_.swapchainImages.imageViews) {
405 if (imageView) {
406 vkDestroyImageView(device, // device
407 imageView, // imageView
408 nullptr); // pAllocator
409 }
410 }
411 for (const auto semaphore : plat_.swapchainImages.semaphores) {
412 if (semaphore) {
413 vkDestroySemaphore(device, // device
414 semaphore, // semaphore
415 nullptr); // pAllocator
416 }
417 }
418
419 CreateFunctionsVk::DestroySwapchain(device, plat_.swapchain);
420 if (ownsSurface_ && surface_) {
421 CreateFunctionsVk::DestroySurface(devicePlatformData.instance, surface_);
422 }
423 }
424
GetPlatformData() const425 const SwapchainPlatformDataVk& SwapchainVk::GetPlatformData() const
426 {
427 return plat_;
428 }
429
GetDesc() const430 const GpuImageDesc& SwapchainVk::GetDesc() const
431 {
432 return desc_;
433 }
434
GetDescDepthBuffer() const435 const GpuImageDesc& SwapchainVk::GetDescDepthBuffer() const
436 {
437 return descDepthBuffer_;
438 }
439
GetFlags() const440 uint32_t SwapchainVk::GetFlags() const
441 {
442 return flags_;
443 }
444
GetSurfaceTransformFlags() const445 SurfaceTransformFlags SwapchainVk::GetSurfaceTransformFlags() const
446 {
447 return surfaceTransformFlags_;
448 }
449
GetSurfaceHandle() const450 uint64_t SwapchainVk::GetSurfaceHandle() const
451 {
452 return VulkanHandleCast<uint64_t>(surface_);
453 }
454
GetNextAcquireSwapchainSemaphoreIndex() const455 uint32_t SwapchainVk::GetNextAcquireSwapchainSemaphoreIndex() const
456 {
457 currSemaphoreIdx_ = (currSemaphoreIdx_ + 1) % plat_.swapchainImages.semaphores.size();
458 return currSemaphoreIdx_;
459 }
460 RENDER_END_NAMESPACE()
461