Rendering Engine 0.2.17
Modular Graphics Rendering Engine | v0.2.17
Loading...
Searching...
No Matches
vulkan_renderer.cpp
Go to the documentation of this file.
1#include "version.h"
2#include "vulkan_renderer.hpp"
8#include "utility.hpp"
9#include "material.hpp"
10#include "texture_cache.hpp"
11#include "logger.hpp"
12
13#include <stdexcept>
14#include <set>
15#include <algorithm>
16#include <array>
17
18namespace rendering_engine
19{
20
21#if defined(NDEBUG)
22 const bool enableValidationLayers = false;
23#else
24 const bool enableValidationLayers = true;
25#endif
26
27template<>
28std::vector<VkVertexInputAttributeDescription>
29VulkanRenderer::GetAttributeDescriptions<Vertex2D>();
30
31template<>
32std::vector<VkVertexInputAttributeDescription>
33VulkanRenderer::GetAttributeDescriptions<VertexPositionColorTexture>();
34
35template<>
36std::vector<VkVertexInputAttributeDescription>
37VulkanRenderer::GetAttributeDescriptions<VertexPositionColorTextureNormalTangent>();
38
40 :
41 mWindowSystem{windowSystem},
42 mCurrentFrame{0}
43{}
44
46{
47 CreateInstance();
48 SetupDebugMessenger();
49 CreateSurface();
50 PickPhysicalDevice();
51 CreateLogicalDevice();
52
53 CreateSwapChain();
54 CreateImageViews();
55 CreateRenderPass();
56
57 CreateCommandPool();
58
59 CreateColorResources();
60 CreateDepthResources();
61
62 CreateFramebuffers();
63
64 CreateCommandBuffers();
65 CreateFrameSyncObjects();
66 CreateSwapchainSyncObjects();
67}
68
70{
71 vkWaitForFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame], VK_TRUE, UINT64_MAX);
72
73 uint32_t imageIndex;
74 VkResult result = vkAcquireNextImageKHR(mLogicalDevice, mSwapChain, UINT64_MAX, mImageAvailableSemaphores[mCurrentFrame], VK_NULL_HANDLE, &imageIndex);
75
76 if (result == VK_ERROR_OUT_OF_DATE_KHR)
77 {
78 LOG_WARNING("Swapchain out of date. Triggering recreation.");
79 RecreateSwapChain();
80 return;
81 }
82 else
83 {
84 if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
85 {
86 LOG_ERROR("failed to acquire swap chain image!");
87 throw std::runtime_error("failed to acquire swap chain image!");
88 }
89 }
90
91 vkResetFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame]);
92
93 vkResetCommandBuffer(mCommandBuffers[mCurrentFrame], /*VkCommandBufferResetFlagBits*/ 0);
94 RecordCommandBuffer(mCommandBuffers[mCurrentFrame], imageIndex);
95
96 VkSubmitInfo submitInfo{};
97 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
98
99 VkSemaphore waitSemaphores[] = { mImageAvailableSemaphores[mCurrentFrame] };
100 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
101 submitInfo.waitSemaphoreCount = 1;
102 submitInfo.pWaitSemaphores = waitSemaphores;
103 submitInfo.pWaitDstStageMask = waitStages;
104
105 submitInfo.commandBufferCount = 1;
106 submitInfo.pCommandBuffers = &mCommandBuffers[mCurrentFrame];
107
108 VkSemaphore signalSemaphores[] = { mRenderFinishedSemaphores[imageIndex] };
109 submitInfo.signalSemaphoreCount = 1;
110 submitInfo.pSignalSemaphores = signalSemaphores;
111
112 if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mInFlightFences[mCurrentFrame]) != VK_SUCCESS)
113 {
114 LOG_ERROR("failed to submit draw command buffer!");
115 throw std::runtime_error("failed to submit draw command buffer!");
116 }
117
118 VkPresentInfoKHR presentInfo{};
119 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
120
121 presentInfo.waitSemaphoreCount = 1;
122 presentInfo.pWaitSemaphores = signalSemaphores;
123
124 VkSwapchainKHR swapChains[] = { mSwapChain };
125 presentInfo.swapchainCount = 1;
126 presentInfo.pSwapchains = swapChains;
127
128 presentInfo.pImageIndices = &imageIndex;
129
130 result = vkQueuePresentKHR(mPresentQueue, &presentInfo);
131
132 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || mWindowSystem.IsFramebufferResized())
133 {
134 mWindowSystem.ResetFramebufferResizedFlag();
135 RecreateSwapChain();
136 }
137 else
138 {
139 if (result != VK_SUCCESS)
140 {
141 LOG_ERROR("failed to present swap chain image!");
142 throw std::runtime_error("failed to present swap chain image!");
143 }
144 }
145
146 mCurrentFrame = (mCurrentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
147}
148
150{
151 vkWaitForFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame], VK_TRUE, UINT64_MAX);
152
153 VkResult result = vkAcquireNextImageKHR(mLogicalDevice, mSwapChain, UINT64_MAX, mImageAvailableSemaphores[mCurrentFrame], VK_NULL_HANDLE, &mImageIndex);
154
155 if (result == VK_ERROR_OUT_OF_DATE_KHR)
156 {
157 RecreateSwapChain();
158 return false;
159 }
160 else
161 {
162 if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
163 {
164 LOG_ERROR("failed to acquire swap chain image!");
165 throw std::runtime_error("failed to acquire swap chain image!");
166 }
167 }
168
169 if (mImagesInFlight[mImageIndex] != VK_NULL_HANDLE)
170 {
171 vkWaitForFences(mLogicalDevice, 1, &mImagesInFlight[mImageIndex], VK_TRUE, UINT64_MAX);
172 }
173 mImagesInFlight[mImageIndex] = mInFlightFences[mCurrentFrame];
174
175 vkResetFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame]);
176 ++mFrameSerial;
177 ProcessDeferredDestruction();
178
179 vkResetCommandBuffer(mCommandBuffers[mCurrentFrame], /*VkCommandBufferResetFlagBits*/ 0);
180
181 return true;
182}
183
185{
186 VkCommandBufferBeginInfo beginInfo{};
187 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
188 //beginInfo.flags = 0; // Optional
189 //beginInfo.pInheritanceInfo = nullptr; // Optional
190
191 if (vkBeginCommandBuffer(mCommandBuffers[mCurrentFrame], &beginInfo) != VK_SUCCESS)
192 {
193 LOG_ERROR("failed to begin recording command buffer!");
194 throw std::runtime_error("failed to begin recording command buffer!");
195 }
196
197 VkRenderPassBeginInfo renderPassInfo{};
198 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
199 renderPassInfo.renderPass = mRenderPass;
200 renderPassInfo.framebuffer = mSwapChainFramebuffers[mImageIndex];
201
202 renderPassInfo.renderArea.offset = { 0, 0 };
203 renderPassInfo.renderArea.extent = mSwapChainExtent;
204
205 std::array<VkClearValue, 2> clearValues{};
206 clearValues[0].color = { {mDefaultColor.r, mDefaultColor.g, mDefaultColor.b, 1.0f} };
207 clearValues[1].depthStencil = { 1.0f, 0 };
208 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
209 renderPassInfo.pClearValues = clearValues.data();
210
211 vkCmdBeginRenderPass(mCommandBuffers[mCurrentFrame], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
212}
213
215{
216 vkCmdEndRenderPass(mCommandBuffers[mCurrentFrame]);
217}
218
220{
221 if (vkEndCommandBuffer(mCommandBuffers[mCurrentFrame]) != VK_SUCCESS)
222 {
223 LOG_ERROR("failed to record command buffer!");
224 throw std::runtime_error("failed to record command buffer!");
225 }
226
227 VkSubmitInfo submitInfo{};
228 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
229
230 VkSemaphore waitSemaphores[] = { mImageAvailableSemaphores[mCurrentFrame] };
231 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
232 submitInfo.waitSemaphoreCount = 1;
233 submitInfo.pWaitSemaphores = waitSemaphores;
234 submitInfo.pWaitDstStageMask = waitStages;
235
236 submitInfo.commandBufferCount = 1;
237 submitInfo.pCommandBuffers = &mCommandBuffers[mCurrentFrame];
238
239 VkSemaphore signalSemaphores[] = { mRenderFinishedSemaphores[mImageIndex] };
240 submitInfo.signalSemaphoreCount = 1;
241 submitInfo.pSignalSemaphores = signalSemaphores;
242
243 if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mInFlightFences[mCurrentFrame]) != VK_SUCCESS)
244 {
245 LOG_ERROR("failed to submit draw command buffer!");
246 throw std::runtime_error("failed to submit draw command buffer!");
247 }
248
249 VkPresentInfoKHR presentInfo{};
250 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
251
252 presentInfo.waitSemaphoreCount = 1;
253 presentInfo.pWaitSemaphores = signalSemaphores;
254
255 VkSwapchainKHR swapChains[] = { mSwapChain };
256 presentInfo.swapchainCount = 1;
257 presentInfo.pSwapchains = swapChains;
258
259 presentInfo.pImageIndices = &mImageIndex;
260
261 VkResult result = vkQueuePresentKHR(mPresentQueue, &presentInfo);
262
263 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || mWindowSystem.IsFramebufferResized())
264 {
265 mWindowSystem.ResetFramebufferResizedFlag();
266 RecreateSwapChain();
267 }
268 else
269 {
270 if (result != VK_SUCCESS)
271 {
272 LOG_ERROR("failed to present swap chain image!");
273 throw std::runtime_error("failed to present swap chain image!");
274 }
275 }
276
277 mCurrentFrame = (mCurrentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
278}
279
281{
282 vkDeviceWaitIdle(mLogicalDevice);
283}
284
286{
287 LOG_INFO("Shutting down Vulkan renderer...");
288 vkDeviceWaitIdle(mLogicalDevice);
289
290 mFrameSerial = std::numeric_limits<uint64_t>::max();
291 ProcessDeferredDestruction();
292
293 CleanupSwapChain();
294
295 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
296 {
297 vkDestroySemaphore(mLogicalDevice, mImageAvailableSemaphores[i], nullptr);
298 vkDestroyFence(mLogicalDevice, mInFlightFences[i], nullptr);
299 }
300
301 vkDestroyCommandPool(mLogicalDevice, mCommandPool, nullptr);
302
303 vkDestroyDevice(mLogicalDevice, nullptr);
305 {
306 DestroyDebugUtilsMessengerEXT(mInstance, mDebugMessenger, nullptr);
307 }
308 vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
309 vkDestroyInstance(mInstance, nullptr);
310 LOG_INFO("Vulkan renderer shutdown complete.");
311}
312
314{
315 if (std::find(mObservers.begin(), mObservers.end(), notifier) == mObservers.end())
316 {
317 mObservers.push_back(notifier);
318 }
319}
320
322{
323 mObservers.erase(std::remove(mObservers.begin(), mObservers.end(), notifier), mObservers.end());
324}
325
330
335
340
341void VulkanRenderer::SetDefaultColor(float r, float g, float b)
342{
343 mDefaultColor = glm::vec3(r, g, b);
344}
345
350
351void VulkanRenderer::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory)
352{
353 VkBufferCreateInfo bufferInfo{};
354 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
355 bufferInfo.size = size;
356
357 bufferInfo.usage = usage;
358 bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
359
360 if (vkCreateBuffer(mLogicalDevice, &bufferInfo, nullptr, &buffer) != VK_SUCCESS)
361 {
362 LOG_ERROR("failed to create vertex buffer!");
363 throw std::runtime_error("failed to create vertex buffer!");
364 }
365
366 VkMemoryRequirements memRequirements;
367 vkGetBufferMemoryRequirements(mLogicalDevice, buffer, &memRequirements);
368
369 VkMemoryAllocateInfo allocInfo{};
370 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
371 allocInfo.allocationSize = memRequirements.size;
372 allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties);
373
374 if (vkAllocateMemory(mLogicalDevice, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS)
375 {
376 LOG_ERROR("failed to allocate vertex buffer memory!");
377 throw std::runtime_error("failed to allocate vertex buffer memory!");
378 }
379
380 vkBindBufferMemory(mLogicalDevice, buffer, bufferMemory, 0);
381}
382
383void VulkanRenderer::CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
384{
385 auto commandBuffer = BeginSingleTimeCommand();
386
387 VkBufferCopy copyRegion{};
388 copyRegion.srcOffset = 0; // Optional
389 copyRegion.dstOffset = 0; // Optional
390 copyRegion.size = size;
391 vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
392
393 EndSingleTimeCommand(commandBuffer);
394}
395
397{
398 return mLogicalDevice;
399}
400
401void VulkanRenderer::TransitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, std::uint32_t mipmapLevels)
402{
403 VkCommandBuffer commandBuffer = BeginSingleTimeCommand();
404
405 VkImageMemoryBarrier barrier{};
406 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
407 barrier.oldLayout = oldLayout;
408 barrier.newLayout = newLayout;
409
410 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
411 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
412
413 barrier.image = image;
414 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
415 barrier.subresourceRange.baseMipLevel = 0;
416 barrier.subresourceRange.levelCount = mipmapLevels;
417 barrier.subresourceRange.baseArrayLayer = 0;
418 barrier.subresourceRange.layerCount = 1;
419
420 VkPipelineStageFlags sourceStage;
421 VkPipelineStageFlags destinationStage;
422
423 if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
424 {
425 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
426
427 if (HasStencilComponent(format))
428 {
429 barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
430 }
431 }
432 else
433 {
434 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
435 }
436
437 if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
438 barrier.srcAccessMask = 0;
439 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
440
441 sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
442 destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
443 }
444 else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
445 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
446 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
447
448 sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
449 destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
450 }
451 else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
452 barrier.srcAccessMask = 0;
453 barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
454
455 sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
456 destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
457 }
458 else {
459 throw std::invalid_argument("unsupported layout transition!");
460 }
461
462 vkCmdPipelineBarrier(
463 commandBuffer,
464 sourceStage, destinationStage,
465 0,
466 0, nullptr,
467 0, nullptr,
468 1, &barrier
469 );
470
471 EndSingleTimeCommand(commandBuffer);
472}
473
474void VulkanRenderer::CopyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
475{
476 VkCommandBuffer commandBuffer = BeginSingleTimeCommand();
477
478 VkBufferImageCopy region{};
479 region.bufferOffset = 0;
480 region.bufferRowLength = 0;
481 region.bufferImageHeight = 0;
482
483 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
484 region.imageSubresource.mipLevel = 0;
485 region.imageSubresource.baseArrayLayer = 0;
486 region.imageSubresource.layerCount = 1;
487
488 region.imageOffset = { 0, 0, 0 };
489 region.imageExtent = {
490 width,
491 height,
492 1
493 };
494
495 vkCmdCopyBufferToImage(
496 commandBuffer,
497 buffer,
498 image,
499 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
500 1,
501 &region
502 );
503
504 EndSingleTimeCommand(commandBuffer);
505}
506
507void VulkanRenderer::GenerateMipmaps(VkImage image, VkFormat imageFormat, int32_t texWidth, int32_t texHeight, uint32_t mipLevels)
508{
509 // Check if image format supports linear blitting
510 VkFormatProperties formatProperties;
511 vkGetPhysicalDeviceFormatProperties(mPhysicalDevice, imageFormat, &formatProperties);
512
513 if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT))
514 {
515 LOG_ERROR("texture image format does not support linear blitting!");
516 throw std::runtime_error("texture image format does not support linear blitting!");
517 }
518
519 VkCommandBuffer commandBuffer = BeginSingleTimeCommand();
520
521 VkImageMemoryBarrier barrier{};
522 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
523 barrier.image = image;
524 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
525 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
526 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
527 barrier.subresourceRange.baseArrayLayer = 0;
528 barrier.subresourceRange.layerCount = 1;
529 barrier.subresourceRange.levelCount = 1;
530
531 int32_t mipWidth = texWidth;
532 int32_t mipHeight = texHeight;
533
534 for (uint32_t i = 1; i < mipLevels; i++)
535 {
536 barrier.subresourceRange.baseMipLevel = i - 1;
537 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
538 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
539 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
540 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
541
542 vkCmdPipelineBarrier(commandBuffer,
543 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
544 0, nullptr,
545 0, nullptr,
546 1, &barrier);
547
548 VkImageBlit blit{};
549 blit.srcOffsets[0] = { 0, 0, 0 };
550 blit.srcOffsets[1] = { mipWidth, mipHeight, 1 };
551 blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
552 blit.srcSubresource.mipLevel = i - 1;
553 blit.srcSubresource.baseArrayLayer = 0;
554 blit.srcSubresource.layerCount = 1;
555 blit.dstOffsets[0] = { 0, 0, 0 };
556 blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 };
557 blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
558 blit.dstSubresource.mipLevel = i;
559 blit.dstSubresource.baseArrayLayer = 0;
560 blit.dstSubresource.layerCount = 1;
561
562 vkCmdBlitImage(commandBuffer,
563 image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
564 image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
565 1, &blit,
566 VK_FILTER_LINEAR);
567
568 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
569 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
570 barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
571 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
572
573 vkCmdPipelineBarrier(commandBuffer,
574 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
575 0, nullptr,
576 0, nullptr,
577 1, &barrier);
578
579 if (mipWidth > 1) mipWidth /= 2;
580 if (mipHeight > 1) mipHeight /= 2;
581 }
582
583 barrier.subresourceRange.baseMipLevel = mipLevels - 1;
584 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
585 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
586 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
587 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
588
589 vkCmdPipelineBarrier(commandBuffer,
590 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
591 0, nullptr,
592 0, nullptr,
593 1, &barrier);
594
595 EndSingleTimeCommand(commandBuffer);
596}
597
599{
600 switch (deferredItem.type)
601 {
602 case DeferredType::Buffer: if (deferredItem.buffer == VK_NULL_HANDLE) return; break;
603 case DeferredType::Memory: if (deferredItem.memory == VK_NULL_HANDLE) return; break;
604 case DeferredType::DescriptorPool: if (deferredItem.descriptorPool == VK_NULL_HANDLE) return; break;
605 default: break;
606 }
607 deferredItem.retireFrame = mFrameSerial + MAX_FRAMES_IN_FLIGHT;
608 mDeferredQueue.push_back(deferredItem);
609}
610
611void VulkanRenderer::CreateInstance()
612{
613 //Check validation layers.
614 if (enableValidationLayers && !CheckValidationLayerSupport())
615 {
616 LOG_ERROR("validation layers requested, but not available!");
617 throw std::runtime_error("validation layers requested, but not available!");
618 }
619
620 VkApplicationInfo appInfo{};
621 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
622 appInfo.pApplicationName = mWindowSystem.GetApplication().GetScreenSettings().name.c_str();
623 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
624 appInfo.pEngineName = "Rendering Engine";
625 appInfo.engineVersion = VK_MAKE_VERSION(
626 RENDERING_ENGINE_VERSION_MAJOR,
627 RENDERING_ENGINE_VERSION_MINOR,
628 RENDERING_ENGINE_VERSION_PATCH
629 );
630 appInfo.apiVersion = VK_API_VERSION_1_0;
631
632 VkInstanceCreateInfo createInfo{};
633 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
634 createInfo.pApplicationInfo = &appInfo;
635
636 auto extensionsNames = GetRequiredExtensions();
637 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensionsNames.size());
638 createInfo.ppEnabledExtensionNames = extensionsNames.data();
639
640 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
642 {
643 createInfo.enabledLayerCount = static_cast<uint32_t>(mValidationLayers.size());
644 createInfo.ppEnabledLayerNames = mValidationLayers.data();
645
646 PopulateDebugMessengerCreateInfo(debugCreateInfo);
647 createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo;
648 }
649 else
650 {
651 createInfo.enabledLayerCount = 0;
652
653 createInfo.pNext = nullptr;
654 }
655
656 if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS)
657 {
658 LOG_ERROR("failed to create instance!");
659 throw std::runtime_error("failed to create instance!");
660 }
661 LOG_INFO("Vulkan instance created.");
662}
663
664bool VulkanRenderer::CheckValidationLayerSupport()
665{
666 uint32_t layerCount;
667 vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
668
669 std::vector<VkLayerProperties> availableLayers(layerCount);
670 vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
671
672 for (const char* layerName : mValidationLayers)
673 {
674 bool layerFound = false;
675
676 for (const auto& layerProperties : availableLayers)
677 {
678 if (strcmp(layerName, layerProperties.layerName) == 0)
679 {
680 layerFound = true;
681 break;
682 }
683 }
684
685 if (!layerFound)
686 {
687 return false;
688 }
689 }
690
691 return true;
692}
693
694std::vector<const char*> VulkanRenderer::GetRequiredExtensions()
695{
696 uint32_t glfwExtensionCount = 0;
697 const char** glfwExtensions;
698 glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
699
700 std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
701
703 {
704 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
705 }
706
707 return extensions;
708}
709
710void VulkanRenderer::PopulateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo)
711{
712 createInfo = {};
713 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
714 createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
715 createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
716 createInfo.pfnUserCallback = DebugCallback;
717}
718
719VKAPI_ATTR VkBool32 VKAPI_CALL VulkanRenderer::DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData)
720{
721 static_cast<void>(messageType);
722 static_cast<void>(pUserData);
723
724 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
725 {
726 LOG_ERROR(std::string("Vulkan validation: ") + pCallbackData->pMessage);
727 }
728 else
729 {
730 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
731 {
732 LOG_WARNING(std::string("Vulkan validation: ") + pCallbackData->pMessage);
733 }
734 else
735 {
736 LOG_DEBUG(std::string("Vulkan validation: ") + pCallbackData->pMessage);
737 }
738 }
739
740 return VK_FALSE;
741}
742
743void VulkanRenderer::SetupDebugMessenger()
744{
746 {
747 return;
748 }
749
750 VkDebugUtilsMessengerCreateInfoEXT createInfo{};
751 PopulateDebugMessengerCreateInfo(createInfo);
752
753 if (CreateDebugUtilsMessengerEXT(mInstance, &createInfo, nullptr, &mDebugMessenger) != VK_SUCCESS)
754 {
755 LOG_ERROR("failed to set up debug messenger!");
756 throw std::runtime_error("failed to set up debug messenger!");
757 }
758}
759
760VkResult VulkanRenderer::CreateDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerCreateInfoEXT const* pCreateInfo, VkAllocationCallbacks const* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger)
761{
762 auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
763 if (func != nullptr)
764 {
765 return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
766 }
767 else
768 {
769 return VK_ERROR_EXTENSION_NOT_PRESENT;
770 }
771}
772
773void VulkanRenderer::DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator)
774{
775 auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
776 if (func != nullptr)
777 {
778 func(instance, debugMessenger, pAllocator);
779 }
780}
781
782void VulkanRenderer::CreateSurface()
783{
784 GLFWwindow* window = static_cast<GLFWwindow*>(mWindowSystem.GetNativeHandle());
785 if (glfwCreateWindowSurface(mInstance, window, nullptr, &mSurface) != VK_SUCCESS)
786 {
787 LOG_ERROR("failed to create window surface!");
788 throw std::runtime_error("failed to create window surface!");
789 }
790}
791
792void VulkanRenderer::PickPhysicalDevice()
793{
794 uint32_t deviceCount = 0;
795 vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr);
796
797 if (deviceCount == 0)
798 {
799 LOG_ERROR("failed to find GPUs with Vulkan support!");
800 throw std::runtime_error("failed to find GPUs with Vulkan support!");
801 }
802
803 std::vector<VkPhysicalDevice> devices(deviceCount);
804 vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data());
805
806 for (const auto& device : devices)
807 {
808 if (IsDeviceSuitable(device)) {
809 mPhysicalDevice = device;
810 vkGetPhysicalDeviceFeatures(mPhysicalDevice, &mPhysDevSupportedFeatures);
811 vkGetPhysicalDeviceProperties(mPhysicalDevice, &mPhysicalDeviceProperties);
812 mMSAASamples = CheckMaxUsableSampleCount();
813 break;
814 }
815 }
816
817 if (mPhysicalDevice == VK_NULL_HANDLE)
818 {
819 LOG_ERROR("Failed to find a suitable GPU!");
820 throw std::runtime_error("failed to find a suitable GPU!");
821 }
822 LOG_INFO(std::string("Selected GPU: ") +
823 mPhysicalDeviceProperties.deviceName);
824}
825
826bool VulkanRenderer::IsDeviceSuitable(VkPhysicalDevice device)
827{
828 // Next features could be used for rating and checkink proper device
829
830 //VkPhysicalDeviceProperties deviceProperties;
831 //vkGetPhysicalDeviceProperties(device, &deviceProperties);
832
833 // VkPhysicalDeviceFeatures supportedFeatures;
834 // vkGetPhysicalDeviceFeatures(device, &supportedFeatures);
835
836 //return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
837 // deviceFeatures.geometryShader;
838
839 QueueFamilyIndices indices = FindQueueFamilies(device);
840
841 bool extensionsSupported = CheckDeviceExtensionSupport(device);
842
843 bool swapChainAdequate = false;
844 if (extensionsSupported)
845 {
846 auto const swapChainSupport = QuerySwapChainSupport(device);
847 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
848 }
849 return indices.IsComplete() && extensionsSupported && swapChainAdequate;
850}
851
852QueueFamilyIndices VulkanRenderer::FindQueueFamilies(VkPhysicalDevice device)
853{
854 QueueFamilyIndices indices;
855 // Logic to find queue family indices to populate struct with
856 uint32_t queueFamilyCount = 0;
857 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
858
859 std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
860 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
861
862 uint32_t i = 0;
863 for (const auto& queueFamily : queueFamilies)
864 {
865 if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
866 {
867 indices.graphicsFamily = i;
868 }
869
870 VkBool32 presentSupport = false;
871 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, mSurface, &presentSupport);
872 if (presentSupport)
873 {
874 indices.presentFamily = i;
875 }
876 if (indices.IsComplete())
877 {
878 break;
879 }
880
881 i++;
882 }
883 return indices;
884}
885
886bool VulkanRenderer::CheckDeviceExtensionSupport(VkPhysicalDevice physicalDevice)
887{
888 uint32_t extensionCount;
889 vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr);
890
891 std::vector<VkExtensionProperties> availableExtensions(extensionCount);
892 vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, availableExtensions.data());
893
894 std::set<std::string> requiredExtensions(mDeviceExtensions.begin(), mDeviceExtensions.end());
895
896 for (const auto& extension : availableExtensions)
897 {
898 requiredExtensions.erase(extension.extensionName);
899 }
900
901 return requiredExtensions.empty();
902}
903
904SwapChainSupportDetails VulkanRenderer::QuerySwapChainSupport(VkPhysicalDevice device)
905{
906 SwapChainSupportDetails scsDetails;
907
908 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, mSurface, &scsDetails.capabilities);
909
910 uint32_t formatCount;
911 vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, nullptr);
912
913 if (formatCount != 0)
914 {
915 scsDetails.formats.resize(formatCount);
916 vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, scsDetails.formats.data());
917 }
918
919 uint32_t presentModeCount;
920 vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, nullptr);
921
922 if (presentModeCount != 0)
923 {
924 scsDetails.presentModes.resize(presentModeCount);
925 vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, scsDetails.presentModes.data());
926 }
927
928 return scsDetails;
929}
930
931VkSampleCountFlagBits VulkanRenderer::CheckMaxUsableSampleCount()
932{
933 VkPhysicalDeviceProperties physicalDeviceProperties;
934 vkGetPhysicalDeviceProperties(mPhysicalDevice, &physicalDeviceProperties);
935
936 VkSampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts & physicalDeviceProperties.limits.framebufferDepthSampleCounts;
937 if (counts & VK_SAMPLE_COUNT_64_BIT) { return VK_SAMPLE_COUNT_64_BIT; }
938 if (counts & VK_SAMPLE_COUNT_32_BIT) { return VK_SAMPLE_COUNT_32_BIT; }
939 if (counts & VK_SAMPLE_COUNT_16_BIT) { return VK_SAMPLE_COUNT_16_BIT; }
940 if (counts & VK_SAMPLE_COUNT_8_BIT) { return VK_SAMPLE_COUNT_8_BIT; }
941 if (counts & VK_SAMPLE_COUNT_4_BIT) { return VK_SAMPLE_COUNT_4_BIT; }
942 if (counts & VK_SAMPLE_COUNT_2_BIT) { return VK_SAMPLE_COUNT_2_BIT; }
943
944 return VK_SAMPLE_COUNT_1_BIT;
945}
946
947void VulkanRenderer::CreateLogicalDevice()
948{
949 QueueFamilyIndices indices = FindQueueFamilies(mPhysicalDevice);
950
951 std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
952 std::set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
953
954 float queuePriority = 1.0f;
955 for (uint32_t queueFamily : uniqueQueueFamilies)
956 {
957 VkDeviceQueueCreateInfo queueCreateInfo{};
958 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
959 queueCreateInfo.queueFamilyIndex = queueFamily;
960 queueCreateInfo.queueCount = 1;
961 queueCreateInfo.pQueuePriorities = &queuePriority;
962 queueCreateInfos.push_back(queueCreateInfo);
963 }
964
965 VkPhysicalDeviceFeatures deviceFeatures{};
966 if (mPhysDevSupportedFeatures.samplerAnisotropy)
967 {
968 deviceFeatures.samplerAnisotropy = VK_TRUE;
969 }
970 else
971 {
972 deviceFeatures.samplerAnisotropy = VK_FALSE;
973 }
974
975 if (mPhysDevSupportedFeatures.sampleRateShading)
976 {
977 deviceFeatures.sampleRateShading = VK_TRUE;
978 }
979 else
980 {
981 deviceFeatures.sampleRateShading = VK_FALSE;
982 }
983
984 VkDeviceCreateInfo createInfo{};
985 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
986
987 createInfo.pQueueCreateInfos = queueCreateInfos.data();
988 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
989
990 createInfo.pEnabledFeatures = &deviceFeatures;
991
992 createInfo.enabledExtensionCount = static_cast<uint32_t>(mDeviceExtensions.size());
993 createInfo.ppEnabledExtensionNames = mDeviceExtensions.data();
994
996 {
997 createInfo.enabledLayerCount = static_cast<uint32_t>(mValidationLayers.size());
998 createInfo.ppEnabledLayerNames = mValidationLayers.data();
999 }
1000 else
1001 {
1002 createInfo.enabledLayerCount = 0;
1003 }
1004
1005 if (vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mLogicalDevice) != VK_SUCCESS)
1006 {
1007 LOG_ERROR("failed to create logical device!");
1008 throw std::runtime_error("failed to create logical device!");
1009 }
1010
1011 vkGetDeviceQueue(mLogicalDevice, indices.graphicsFamily.value(), 0, &mGraphicsQueue);
1012 vkGetDeviceQueue(mLogicalDevice, indices.presentFamily.value(), 0, &mPresentQueue);
1013 LOG_INFO("Logical device created.");
1014}
1015
1016void VulkanRenderer::CreateSwapChain()
1017{
1018 SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(mPhysicalDevice);
1019
1020 VkSurfaceFormatKHR surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.formats);
1021 // TO Disable VSync VkPresentModeKHR presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
1022 VkPresentModeKHR presentMode = ChooseSwapPresentMode(swapChainSupport.presentModes);
1023 VkExtent2D extent = ChooseSwapExtent(swapChainSupport.capabilities);
1024
1025 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
1026 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount)
1027 {
1028 imageCount = swapChainSupport.capabilities.maxImageCount;
1029 }
1030
1031 VkSwapchainCreateInfoKHR createInfo{};
1032 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1033 createInfo.surface = mSurface;
1034
1035 createInfo.minImageCount = imageCount;
1036 createInfo.imageFormat = surfaceFormat.format;
1037 createInfo.imageColorSpace = surfaceFormat.colorSpace;
1038 createInfo.imageExtent = extent;
1039 createInfo.imageArrayLayers = 1;
1040 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1041
1042 QueueFamilyIndices indices = FindQueueFamilies(mPhysicalDevice);
1043 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1044
1045 if (indices.graphicsFamily != indices.presentFamily) {
1046 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
1047 createInfo.queueFamilyIndexCount = 2;
1048 createInfo.pQueueFamilyIndices = queueFamilyIndices;
1049 }
1050 else
1051 {
1052 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1053 createInfo.queueFamilyIndexCount = 0; // Optional
1054 createInfo.pQueueFamilyIndices = nullptr; // Optional
1055 }
1056
1057 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
1058 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1059 createInfo.presentMode = presentMode;
1060 createInfo.clipped = VK_TRUE;
1061 createInfo.oldSwapchain = VK_NULL_HANDLE;
1062
1063 if (vkCreateSwapchainKHR(mLogicalDevice, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS)
1064 {
1065 LOG_ERROR("failed to create swap chain!");
1066 throw std::runtime_error("failed to create swap chain!");
1067 }
1068
1069 vkGetSwapchainImagesKHR(mLogicalDevice, mSwapChain, &imageCount, nullptr);
1070 mSwapChainImages.resize(imageCount);
1071 vkGetSwapchainImagesKHR(mLogicalDevice, mSwapChain, &imageCount, mSwapChainImages.data());
1072
1073 mSwapChainImageFormat = surfaceFormat.format;
1074 mSwapChainExtent = extent;
1075
1076 LOG_INFO("Swapchain created. Images: " +
1077 std::to_string(mSwapChainImages.size()) +
1078 ", Resolution: " +
1079 std::to_string(mSwapChainExtent.width) + "x" +
1080 std::to_string(mSwapChainExtent.height));
1081}
1082
1083void VulkanRenderer::RecreateSwapChain()
1084{
1085 LOG_WARNING("Recreating swapchain...");
1086 int width = 0;
1087 int height = 0;
1088
1089 while (width == 0 || height == 0)
1090 {
1091 GLFWwindow* window = static_cast<GLFWwindow*>(mWindowSystem.GetNativeHandle());
1092 glfwGetFramebufferSize(window, &width, &height);
1093 glfwWaitEvents();
1094 }
1095 vkDeviceWaitIdle(mLogicalDevice);
1096
1097 CleanupSwapChain();
1098
1099 for (auto& observer : mObservers)
1100 {
1101 observer->OnRenderResourcesRelease();
1102 }
1103
1104 CreateSwapChain();
1105 CreateImageViews();
1106 CreateRenderPass();
1107
1108 CreateColorResources();
1109 CreateDepthResources();
1110 CreateFramebuffers();
1111 CreateCommandBuffers();
1112
1113 CreateSwapchainSyncObjects();
1114
1115 for (auto& observer : mObservers)
1116 {
1117 observer->OnRenderResourcesRebuild();
1118 }
1119 LOG_INFO("Swapchain recreated successfully.");
1120}
1121
1122VkSurfaceFormatKHR VulkanRenderer::ChooseSwapSurfaceFormat(std::vector<VkSurfaceFormatKHR> const& availableFormats)
1123{
1124 for (const auto& availableFormat : availableFormats)
1125 {
1126 if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
1127 {
1128 return availableFormat;
1129 }
1130 }
1131
1132 return availableFormats[0];
1133}
1134
1135VkPresentModeKHR VulkanRenderer::ChooseSwapPresentMode(std::vector<VkPresentModeKHR>const& availablePresentModes)
1136{
1137 for (const auto& availablePresentMode : availablePresentModes)
1138 {
1139 if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR)
1140 {
1141 return availablePresentMode;
1142 }
1143 }
1144 return VK_PRESENT_MODE_FIFO_KHR;
1145}
1146
1147VkExtent2D VulkanRenderer::ChooseSwapExtent(VkSurfaceCapabilitiesKHR const& capabilities)
1148{
1149 if (capabilities.currentExtent.width != UINT32_MAX)
1150 {
1151 return capabilities.currentExtent;
1152 }
1153 else
1154 {
1155 int width, height;
1156 GLFWwindow* window = static_cast<GLFWwindow*>(mWindowSystem.GetNativeHandle());
1157 glfwGetFramebufferSize(window, &width, &height);
1158
1159 VkExtent2D actualExtent = {
1160 static_cast<uint32_t>(width),
1161 static_cast<uint32_t>(height)
1162 };
1163
1164 actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
1165 actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
1166
1167 return actualExtent;
1168 }
1169}
1170
1171void VulkanRenderer::CreateImageViews()
1172{
1173 mSwapChainImageViews.resize(mSwapChainImages.size());
1174
1175 for (uint32_t i = 0; i < mSwapChainImages.size(); i++)
1176 {
1177 mSwapChainImageViews[i] = CreateImageView(mSwapChainImages[i], mSwapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
1178 }
1179}
1180
1181VkImageView VulkanRenderer::CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, std::uint32_t mipmapLevels)
1182{
1183 VkImageViewCreateInfo viewInfo{};
1184 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1185 viewInfo.image = image;
1186 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1187 viewInfo.format = format;
1188 viewInfo.subresourceRange.aspectMask = aspectFlags;
1189 viewInfo.subresourceRange.baseMipLevel = 0;
1190 viewInfo.subresourceRange.levelCount = mipmapLevels;
1191 viewInfo.subresourceRange.baseArrayLayer = 0;
1192 viewInfo.subresourceRange.layerCount = 1;
1193
1194 VkImageView imageView;
1195 if (vkCreateImageView(mLogicalDevice, &viewInfo, nullptr, &imageView) != VK_SUCCESS)
1196 {
1197 LOG_ERROR("failed to create texture image view!");
1198 throw std::runtime_error("failed to create texture image view!");
1199 }
1200
1201 return imageView;
1202}
1203
1204void VulkanRenderer::CreateRenderPass()
1205{
1206 VkAttachmentDescription colorAttachment{};
1207 colorAttachment.format = mSwapChainImageFormat;
1208 colorAttachment.samples = mMSAASamples;
1209 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1210 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1211 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1212 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1213 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1214 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1215
1216 VkAttachmentReference colorAttachmentRef{};
1217 colorAttachmentRef.attachment = 0;
1218 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1219
1220 VkAttachmentDescription depthAttachment{};
1221 depthAttachment.format = FindDepthFormat();
1222 depthAttachment.samples = mMSAASamples;
1223 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1224 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1225 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1226 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1227 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1228 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1229
1230 VkAttachmentReference depthAttachmentRef{};
1231 depthAttachmentRef.attachment = 1;
1232 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1233
1234 VkAttachmentDescription colorAttachmentResolve{};
1235 colorAttachmentResolve.format = mSwapChainImageFormat;
1236 colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT;
1237 colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1238 colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1239 colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1240 colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1241 colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1242 colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1243
1244 VkAttachmentReference colorAttachmentResolveRef{};
1245 colorAttachmentResolveRef.attachment = 2;
1246 colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1247
1248 VkSubpassDescription subpass{};
1249 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1250 subpass.colorAttachmentCount = 1;
1251 subpass.pColorAttachments = &colorAttachmentRef;
1252 subpass.pDepthStencilAttachment = &depthAttachmentRef;
1253 subpass.pResolveAttachments = &colorAttachmentResolveRef;
1254
1255 VkSubpassDependency dependency{};
1256 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
1257 dependency.dstSubpass = 0;
1258 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
1259 dependency.srcAccessMask = 0;
1260 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
1261 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
1262
1263 std::array<VkAttachmentDescription, 3> attachments = { colorAttachment, depthAttachment, colorAttachmentResolve };
1264
1265 VkRenderPassCreateInfo renderPassInfo{};
1266 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1267 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1268 renderPassInfo.pAttachments = attachments.data();
1269 renderPassInfo.subpassCount = 1;
1270 renderPassInfo.pSubpasses = &subpass;
1271 renderPassInfo.dependencyCount = 1;
1272 renderPassInfo.pDependencies = &dependency;
1273
1274 if (vkCreateRenderPass(mLogicalDevice, &renderPassInfo, nullptr, &mRenderPass) != VK_SUCCESS)
1275 {
1276 LOG_ERROR("failed to create render pass!");
1277 throw std::runtime_error("failed to create render pass!");
1278 }
1279}
1280
1281VkFormat VulkanRenderer::FindDepthFormat()
1282{
1283 return FindSupportedFormat(
1284 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
1285 VK_IMAGE_TILING_OPTIMAL,
1286 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
1287 );
1288}
1289
1290VkFormat VulkanRenderer::FindSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features)
1291{
1292 for (VkFormat format : candidates)
1293 {
1294 VkFormatProperties props;
1295 vkGetPhysicalDeviceFormatProperties(mPhysicalDevice, format, &props);
1296
1297 if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features)
1298 {
1299 return format;
1300 }
1301 else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features)
1302 {
1303 return format;
1304 }
1305 }
1306 LOG_ERROR("failed to find supported format!");
1307 throw std::runtime_error("failed to find supported format!");
1308}
1309
1310void VulkanRenderer::CreateCommandPool()
1311{
1312 QueueFamilyIndices queueFamilyIndices = FindQueueFamilies(mPhysicalDevice);
1313
1314 VkCommandPoolCreateInfo poolInfo{};
1315 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1316 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
1317 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
1318
1319 if (vkCreateCommandPool(mLogicalDevice, &poolInfo, nullptr, &mCommandPool) != VK_SUCCESS)
1320 {
1321 LOG_ERROR("failed to create command pool!");
1322 throw std::runtime_error("failed to create command pool!");
1323 }
1324}
1325
1326void VulkanRenderer::CreateColorResources()
1327{
1328 VkFormat colorFormat = mSwapChainImageFormat;
1329
1330 CreateVulkanImage(mSwapChainExtent.width, mSwapChainExtent.height, 1, mMSAASamples, colorFormat, VK_IMAGE_TILING_OPTIMAL,
1331 VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
1332 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mColorImage, mColorImageMemory);
1333 mColorImageView = CreateImageView(mColorImage, colorFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
1334}
1335
1336void VulkanRenderer::CreateVulkanImage(uint32_t width, uint32_t height, std::uint32_t mipmapLevels, VkSampleCountFlagBits numSamples, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory)
1337{
1338 VkImageCreateInfo imageInfo{};
1339 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1340 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1341 imageInfo.extent.width = static_cast<uint32_t>(width);
1342 imageInfo.extent.height = static_cast<uint32_t>(height);
1343 imageInfo.extent.depth = 1;
1344 imageInfo.mipLevels = mipmapLevels;
1345 imageInfo.arrayLayers = 1;
1346
1347 imageInfo.format = format;
1348 imageInfo.tiling = tiling;
1349
1350 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1351
1352 imageInfo.usage = usage;
1353
1354 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1355
1356 imageInfo.samples = numSamples;
1357 imageInfo.flags = 0; // Optional
1358
1359 if (vkCreateImage(mLogicalDevice, &imageInfo, nullptr, &image) != VK_SUCCESS)
1360 {
1361 LOG_ERROR("failed to create image!");
1362 throw std::runtime_error("failed to create image!");
1363 }
1364
1365 VkMemoryRequirements memRequirements;
1366 vkGetImageMemoryRequirements(mLogicalDevice, image, &memRequirements);
1367
1368 VkMemoryAllocateInfo allocInfo{};
1369 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1370 allocInfo.allocationSize = memRequirements.size;
1371 allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties);
1372
1373 if (vkAllocateMemory(mLogicalDevice, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
1374 LOG_ERROR("failed to allocate image memory!");
1375 throw std::runtime_error("failed to allocate image memory!");
1376 }
1377
1378 vkBindImageMemory(mLogicalDevice, image, imageMemory, 0);
1379}
1380
1381uint32_t VulkanRenderer::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
1382{
1383 VkPhysicalDeviceMemoryProperties memProperties;
1384 vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProperties);
1385
1386 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
1387 {
1388 if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
1389 {
1390 return i;
1391 }
1392 }
1393 LOG_ERROR("failed to find suitable memory type!");
1394 throw std::runtime_error("failed to find suitable memory type!");
1395}
1396
1398{
1399 return mPhysicalDevice;
1400}
1401
1403{
1404 return mPhysDevSupportedFeatures;
1405}
1406
1408{
1409 std::uint32_t bindingNumber = 0;
1410 std::vector<VkDescriptorSetLayoutBinding> bindings;
1411
1412 // Uniform buffer to transmit transformation matrix
1413 VkDescriptorSetLayoutBinding uboLayoutBinding{};
1414 uboLayoutBinding.binding = bindingNumber;
1415 uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1416 uboLayoutBinding.descriptorCount = 1;
1417 uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
1418 uboLayoutBinding.pImmutableSamplers = nullptr; // Optional
1419
1420 bindings.push_back(uboLayoutBinding);
1421
1422 PackedMaterialData packedMaterialData = material->PackMaterialParameters();
1423 if (!packedMaterialData.buffer.empty())
1424 {
1425 ++bindingNumber;
1426 // Uniform buffer to transmit custom material variables
1427 VkDescriptorSetLayoutBinding customMaterialVariables;
1428 customMaterialVariables.binding = bindingNumber;
1429 customMaterialVariables.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1430 customMaterialVariables.descriptorCount = 1;
1431 customMaterialVariables.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
1432 customMaterialVariables.pImmutableSamplers = nullptr; // Optional
1433
1434 bindings.push_back(customMaterialVariables);
1435 }
1436
1437 for (const auto& textureName : material->GetTextures())
1438 {
1439 (void)textureName;
1440 ++bindingNumber;
1441 VkDescriptorSetLayoutBinding samplerLayoutBinding{};
1442 samplerLayoutBinding.binding = bindingNumber;
1443 samplerLayoutBinding.descriptorCount = 1;
1444 samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1445 samplerLayoutBinding.pImmutableSamplers = nullptr;
1446 samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
1447
1448 bindings.push_back(samplerLayoutBinding);
1449 }
1450
1451 VkDescriptorSetLayoutCreateInfo layoutInfo{};
1452 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
1453 layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
1454 layoutInfo.pBindings = bindings.data();
1455
1456 VkDescriptorSetLayout result;
1457 if (vkCreateDescriptorSetLayout(mLogicalDevice, &layoutInfo, nullptr, &result) != VK_SUCCESS)
1458 {
1459 LOG_ERROR("failed to create descriptor set layout!");
1460 throw std::runtime_error("failed to create descriptor set layout!");
1461 }
1462
1463 return result;
1464}
1465
1466std::vector<VkCommandBuffer> VulkanRenderer::GetComandBuffers()
1467{
1468 return mCommandBuffers;
1469}
1470
1471std::pair<VkPipelineLayout, VkPipeline> VulkanRenderer::CreateGraphicsPipeline(Material* material, VkDescriptorSetLayout& descriptorSetLayout, std::vector<char>& spvVertShaderCode, std::vector<char>& spvFragShaderCode)
1472{
1473 VkPipelineLayout pipelineLayout;
1474 VkPipeline pipeline;
1475
1476 VkShaderModule vertShaderModule = CreateShaderModule(spvVertShaderCode);
1477 VkShaderModule fragShaderModule = CreateShaderModule(spvFragShaderCode);
1478
1479 VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
1480 vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1481 vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
1482
1483 vertShaderStageInfo.module = vertShaderModule;
1484 vertShaderStageInfo.pName = "main";
1485
1486 VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
1487 fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1488 fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
1489 fragShaderStageInfo.module = fragShaderModule;
1490 fragShaderStageInfo.pName = "main";
1491
1492 VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
1493
1494 VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
1495 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
1496
1497 VkVertexInputBindingDescription bindingDescription;
1498 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1499
1501 {
1502 bindingDescription = GetBindingDescription<Vertex2D>();
1503 attributeDescriptions = GetAttributeDescriptions<Vertex2D>();
1504 }
1506 {
1507 bindingDescription = GetBindingDescription<VertexPositionColorTextureNormalTangent>();
1508 attributeDescriptions = GetAttributeDescriptions<VertexPositionColorTextureNormalTangent>();
1509 }
1510
1511 vertexInputInfo.vertexBindingDescriptionCount = 1;
1512 vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
1513
1514 vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
1515 vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
1516
1517 VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
1518 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
1519 inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
1520 inputAssembly.primitiveRestartEnable = VK_FALSE;
1521
1522 VkViewport viewport{};
1523 viewport.x = 0.0f;
1524 viewport.y = 0.0f;
1525 viewport.width = (float)mSwapChainExtent.width;
1526 viewport.height = (float)mSwapChainExtent.height;
1527 viewport.minDepth = 0.0f;
1528 viewport.maxDepth = 1.0f;
1529
1530 VkRect2D scissor{};
1531 scissor.offset = { 0, 0 };
1532 scissor.extent = mSwapChainExtent;
1533
1534 VkPipelineViewportStateCreateInfo viewportState{};
1535 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
1536 viewportState.viewportCount = 1;
1537 viewportState.pViewports = &viewport;
1538 viewportState.scissorCount = 1;
1539 viewportState.pScissors = &scissor;
1540
1541 VkPipelineRasterizationStateCreateInfo rasterizer{};
1542 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
1543 rasterizer.depthClampEnable = VK_FALSE;
1544 rasterizer.rasterizerDiscardEnable = VK_FALSE;
1545 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
1546 rasterizer.lineWidth = 1.0f;
1548 {
1549 rasterizer.cullMode = VK_CULL_MODE_NONE;
1550 }
1551 else
1552 {
1553 rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
1554 }
1555 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
1556 rasterizer.depthBiasEnable = VK_FALSE;
1557 rasterizer.depthBiasConstantFactor = 0.0f; // Optional
1558 rasterizer.depthBiasClamp = 0.0f; // Optional
1559 rasterizer.depthBiasSlopeFactor = 0.0f; // Optional
1560
1561 VkPipelineMultisampleStateCreateInfo multisampling{};
1562 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
1563 multisampling.sampleShadingEnable = VK_FALSE;
1564 multisampling.rasterizationSamples = mMSAASamples;
1565 multisampling.minSampleShading = 1.0f; // Optional
1566 multisampling.pSampleMask = nullptr; // Optional
1567 multisampling.alphaToCoverageEnable = VK_FALSE; // Optional
1568 multisampling.alphaToOneEnable = VK_FALSE; // Optional
1569
1570 VkPipelineColorBlendAttachmentState colorBlendAttachment{};
1571 colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
1572 colorBlendAttachment.blendEnable = (material->GetMaterialSettings().blendMode == BlendMode::Translucent) ? VK_TRUE : VK_FALSE;
1573 colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
1574 colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
1575 colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional
1576 colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
1577 colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
1578 colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional
1579
1580 VkPipelineColorBlendStateCreateInfo colorBlending{};
1581 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
1582 colorBlending.logicOpEnable = VK_FALSE;
1583 colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional
1584 colorBlending.attachmentCount = 1;
1585 colorBlending.pAttachments = &colorBlendAttachment;
1586 colorBlending.blendConstants[0] = 0.0f; // Optional
1587 colorBlending.blendConstants[1] = 0.0f; // Optional
1588 colorBlending.blendConstants[2] = 0.0f; // Optional
1589 colorBlending.blendConstants[3] = 0.0f; // Optional
1590
1591 VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
1592 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
1593 pipelineLayoutInfo.setLayoutCount = 1;
1594 pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
1595
1596 if (vkCreatePipelineLayout(mLogicalDevice, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS)
1597 {
1598 LOG_ERROR("failed to create pipeline layout!");
1599 throw std::runtime_error("failed to create pipeline layout!");
1600 }
1601
1602 VkPipelineDepthStencilStateCreateInfo depthStencil{};
1603 depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
1605 {
1606 depthStencil.depthTestEnable = VK_FALSE;
1607 depthStencil.depthWriteEnable = VK_FALSE;
1608 }
1609 else
1610 {
1611 depthStencil.depthTestEnable = VK_TRUE;
1612 depthStencil.depthWriteEnable = (material->GetMaterialSettings().blendMode == BlendMode::Opaque) ? VK_TRUE : VK_FALSE;
1613 }
1614 depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
1615 depthStencil.depthBoundsTestEnable = VK_FALSE;
1616 //depthStencil.minDepthBounds = 0.0f; // Optional
1617 //depthStencil.maxDepthBounds = 1.0f; // Optional
1618 depthStencil.stencilTestEnable = VK_FALSE;
1619 //depthStencil.front = {}; // Optional
1620 depthStencil.back = {}; // Optional
1621
1622 VkGraphicsPipelineCreateInfo pipelineInfo{};
1623 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
1624 pipelineInfo.stageCount = 2;
1625 pipelineInfo.pStages = shaderStages;
1626 pipelineInfo.pVertexInputState = &vertexInputInfo;
1627 pipelineInfo.pInputAssemblyState = &inputAssembly;
1628 pipelineInfo.pViewportState = &viewportState;
1629 pipelineInfo.pRasterizationState = &rasterizer;
1630 pipelineInfo.pMultisampleState = &multisampling;
1631 pipelineInfo.pDepthStencilState = &depthStencil;
1632 pipelineInfo.pColorBlendState = &colorBlending;
1633
1634 // To enable dynamic viewport/scissor state:
1635 // const std::vector<VkDynamicState> dynamicStates = {
1636 // VK_DYNAMIC_STATE_VIEWPORT,
1637 // VK_DYNAMIC_STATE_SCISSOR
1638 // };
1639 // VkPipelineDynamicStateCreateInfo dynamicState{};
1640 // dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
1641 // dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
1642 // dynamicState.pDynamicStates = dynamicStates.data();
1643 // Then set: pipelineInfo.pDynamicState = &dynamicState;
1644 pipelineInfo.pDynamicState = nullptr;
1645
1646 pipelineInfo.layout = pipelineLayout;
1647 pipelineInfo.renderPass = mRenderPass;
1648 pipelineInfo.subpass = 0;
1649 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional
1650 pipelineInfo.basePipelineIndex = -1; // Optional
1651
1652
1653 if (vkCreateGraphicsPipelines(mLogicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline) != VK_SUCCESS)
1654 {
1655 LOG_ERROR("failed to create graphics pipeline!");
1656 throw std::runtime_error("failed to create graphics pipeline!");
1657 }
1658 vkDestroyShaderModule(mLogicalDevice, fragShaderModule, nullptr);
1659 vkDestroyShaderModule(mLogicalDevice, vertShaderModule, nullptr);
1660
1661 return std::pair<VkPipelineLayout, VkPipeline>(pipelineLayout, pipeline);
1662}
1663
1664void VulkanRenderer::CreateDepthResources()
1665{
1666 VkFormat depthFormat = FindDepthFormat();
1667
1668 CreateVulkanImage(mSwapChainExtent.width, mSwapChainExtent.height, 1, mMSAASamples, depthFormat,
1669 VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1670 mDepthImage, mDepthImageMemory);
1671 mDepthImageView = CreateImageView(mDepthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1);
1672}
1673
1674void VulkanRenderer::CreateFramebuffers()
1675{
1676 mSwapChainFramebuffers.resize(mSwapChainImageViews.size());
1677
1678 for (size_t i = 0; i < mSwapChainImageViews.size(); i++)
1679 {
1680 std::array<VkImageView, 3> attachments = { mColorImageView, mDepthImageView, mSwapChainImageViews[i] };
1681
1682 VkFramebufferCreateInfo framebufferInfo{};
1683 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1684 framebufferInfo.renderPass = mRenderPass;
1685 framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1686 framebufferInfo.pAttachments = attachments.data();
1687 framebufferInfo.width = mSwapChainExtent.width;
1688 framebufferInfo.height = mSwapChainExtent.height;
1689 framebufferInfo.layers = 1;
1690
1691 if (vkCreateFramebuffer(mLogicalDevice, &framebufferInfo, nullptr, &mSwapChainFramebuffers[i]) != VK_SUCCESS)
1692 {
1693 LOG_ERROR("failed to create framebuffer!");
1694 throw std::runtime_error("failed to create framebuffer!");
1695 }
1696 }
1697}
1698
1699void VulkanRenderer::CreateCommandBuffers()
1700{
1701 mCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
1702
1703 VkCommandBufferAllocateInfo allocInfo{};
1704 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1705 allocInfo.commandPool = mCommandPool;
1706 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1707 allocInfo.commandBufferCount = (uint32_t)mCommandBuffers.size();
1708
1709 if (vkAllocateCommandBuffers(mLogicalDevice, &allocInfo, mCommandBuffers.data()) != VK_SUCCESS)
1710 {
1711 LOG_ERROR("failed to allocate command buffers!");
1712 throw std::runtime_error("failed to allocate command buffers!");
1713 }
1714}
1715
1716void VulkanRenderer::CreateFrameSyncObjects()
1717{
1718 mImageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
1719 mInFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
1720
1721 VkSemaphoreCreateInfo semaphoreInfo{};
1722 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1723 VkFenceCreateInfo fenceInfo{};
1724 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1725 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1726
1727 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
1728 {
1729 if (vkCreateSemaphore(mLogicalDevice, &semaphoreInfo, nullptr, &mImageAvailableSemaphores[i]) != VK_SUCCESS ||
1730 vkCreateFence(mLogicalDevice, &fenceInfo, nullptr, &mInFlightFences[i]) != VK_SUCCESS)
1731 {
1732 LOG_ERROR("failed to create per-frame sync objects!");
1733 throw std::runtime_error("failed to create per-frame sync objects!");
1734 }
1735 }
1736}
1737
1738void VulkanRenderer::CreateSwapchainSyncObjects()
1739{
1740 mRenderFinishedSemaphores.resize(mSwapChainImages.size());
1741 mImagesInFlight.resize(mSwapChainImages.size(), VK_NULL_HANDLE);
1742
1743 VkSemaphoreCreateInfo semaphoreInfo{};
1744 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1745
1746 for (size_t i = 0; i < mSwapChainImages.size(); i++)
1747 {
1748 if (vkCreateSemaphore(mLogicalDevice, &semaphoreInfo, nullptr, &mRenderFinishedSemaphores[i]) != VK_SUCCESS)
1749 {
1750 LOG_ERROR("failed to create per-image renderFinished semaphores!");
1751 throw std::runtime_error("failed to create per-image renderFinished semaphores!");
1752 }
1753 }
1754}
1755
1756VkShaderModule VulkanRenderer::CreateShaderModule(const std::vector<char>& code)
1757{
1758 if (code.empty() || code.size() % sizeof(std::uint32_t) != 0U)
1759 {
1760 throw std::runtime_error("Invalid SPIR-V shader binary size.");
1761 }
1762
1763 std::vector<std::uint32_t> alignedCode(code.size() / sizeof(std::uint32_t));
1764
1765 std::memcpy(alignedCode.data(), code.data(), code.size());
1766
1767 VkShaderModuleCreateInfo createInfo{};
1768 createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
1769 createInfo.codeSize = code.size();
1770 createInfo.pCode = alignedCode.data();
1771
1772 VkShaderModule shaderModule;
1773 if (vkCreateShaderModule(
1774 mLogicalDevice,
1775 &createInfo,
1776 nullptr,
1777 &shaderModule) != VK_SUCCESS)
1778 {
1779 LOG_ERROR("failed to create shader module!");
1780 throw std::runtime_error("failed to create shader module!");
1781 }
1782
1783 return shaderModule;
1784}
1785
1786void VulkanRenderer::CleanupSwapChain()
1787{
1788 // destroy swapchain-dependent sync (render-finished per image)
1789 for (auto sem : mRenderFinishedSemaphores)
1790 {
1791 vkDestroySemaphore(mLogicalDevice, sem, nullptr);
1792 }
1793 mRenderFinishedSemaphores.clear();
1794 mImagesInFlight.clear();
1795
1796 vkDestroyImageView(mLogicalDevice, mColorImageView, nullptr);
1797 vkDestroyImage(mLogicalDevice, mColorImage, nullptr);
1798 vkFreeMemory(mLogicalDevice, mColorImageMemory, nullptr);
1799
1800 vkDestroyImageView(mLogicalDevice, mDepthImageView, nullptr);
1801 vkDestroyImage(mLogicalDevice, mDepthImage, nullptr);
1802 vkFreeMemory(mLogicalDevice, mDepthImageMemory, nullptr);
1803 for (auto framebuffer : mSwapChainFramebuffers)
1804 {
1805 vkDestroyFramebuffer(mLogicalDevice, framebuffer, nullptr);
1806 }
1807
1808 vkFreeCommandBuffers(mLogicalDevice, mCommandPool, static_cast<uint32_t>(mCommandBuffers.size()), mCommandBuffers.data());
1809
1810 vkDestroyRenderPass(mLogicalDevice, mRenderPass, nullptr);
1811 for (auto imageView : mSwapChainImageViews)
1812 {
1813 vkDestroyImageView(mLogicalDevice, imageView, nullptr);
1814 }
1815 vkDestroySwapchainKHR(mLogicalDevice, mSwapChain, nullptr);
1816}
1817
1818void VulkanRenderer::RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
1819{
1820 VkCommandBufferBeginInfo beginInfo{};
1821 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1822 //beginInfo.flags = 0; // Optional
1823 //beginInfo.pInheritanceInfo = nullptr; // Optional
1824
1825 if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS)
1826 {
1827 LOG_ERROR("failed to begin recording command buffer!");
1828 throw std::runtime_error("failed to begin recording command buffer!");
1829 }
1830
1831 VkRenderPassBeginInfo renderPassInfo{};
1832 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1833 renderPassInfo.renderPass = mRenderPass;
1834 renderPassInfo.framebuffer = mSwapChainFramebuffers[imageIndex];
1835
1836 renderPassInfo.renderArea.offset = { 0, 0 };
1837 renderPassInfo.renderArea.extent = mSwapChainExtent;
1838
1839 std::array<VkClearValue, 2> clearValues{};
1840 clearValues[0].color = { {mDefaultColor.r, mDefaultColor.g, mDefaultColor.b, 1.0f} };
1841 clearValues[1].depthStencil = { 1.0f, 0 };
1842 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
1843 renderPassInfo.pClearValues = clearValues.data();
1844
1845 vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
1846
1847 vkCmdEndRenderPass(commandBuffer);
1848
1849 if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS)
1850 {
1851 LOG_ERROR("failed to record command buffer!");
1852 throw std::runtime_error("failed to record command buffer!");
1853 }
1854}
1855
1856VkCommandBuffer VulkanRenderer::BeginSingleTimeCommand()
1857{
1858 VkCommandBufferAllocateInfo allocInfo{};
1859 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1860 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1861 allocInfo.commandPool = mCommandPool;
1862 allocInfo.commandBufferCount = 1;
1863
1864 VkCommandBuffer commandBuffer;
1865 vkAllocateCommandBuffers(mLogicalDevice, &allocInfo, &commandBuffer);
1866
1867 VkCommandBufferBeginInfo beginInfo{};
1868 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1869 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1870
1871 vkBeginCommandBuffer(commandBuffer, &beginInfo);
1872
1873 return commandBuffer;
1874}
1875
1876bool VulkanRenderer::HasStencilComponent(VkFormat format)
1877{
1878 return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
1879}
1880
1881void VulkanRenderer::EndSingleTimeCommand(VkCommandBuffer commandBuffer)
1882{
1883 vkEndCommandBuffer(commandBuffer);
1884
1885 VkSubmitInfo submitInfo{};
1886 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1887 submitInfo.commandBufferCount = 1;
1888 submitInfo.pCommandBuffers = &commandBuffer;
1889
1890 vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
1891 vkQueueWaitIdle(mGraphicsQueue);
1892
1893 vkFreeCommandBuffers(mLogicalDevice, mCommandPool, 1, &commandBuffer);
1894}
1895
1896void VulkanRenderer::ProcessDeferredDestruction()
1897{
1898 std::vector<DeferredItem> ripe;
1899
1900 while (!mDeferredQueue.empty() &&
1901 mDeferredQueue.front().retireFrame <= mFrameSerial)
1902 {
1903 ripe.push_back(mDeferredQueue.front());
1904 mDeferredQueue.pop_front();
1905 }
1906
1907 // Correct destruction order
1908 for (auto& o : ripe)
1909 if (o.type == DeferredType::DescriptorPool)
1910 vkDestroyDescriptorPool(mLogicalDevice, o.descriptorPool, nullptr);
1911
1912 for (auto& o : ripe)
1913 if (o.type == DeferredType::Buffer)
1914 vkDestroyBuffer(mLogicalDevice, o.buffer, nullptr);
1915
1916 for (auto& o : ripe)
1917 if (o.type == DeferredType::Memory)
1918 vkFreeMemory(mLogicalDevice, o.memory, nullptr);
1919}
1920
1921template<typename T>
1922VkVertexInputBindingDescription VulkanRenderer::GetBindingDescription()
1923{
1924 VkVertexInputBindingDescription binding{};
1925 binding.binding = 0;
1926 binding.stride = sizeof(T);
1927 binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
1928 return binding;
1929}
1930
1931template<>
1932std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<Vertex2D>()
1933{
1934 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1935 attributeDescriptions.resize(3);
1936
1937 attributeDescriptions[0].binding = 0;
1938 attributeDescriptions[0].location = 0;
1939 attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
1940 attributeDescriptions[0].offset = offsetof(Vertex2D, position);
1941
1942 attributeDescriptions[1].binding = 0;
1943 attributeDescriptions[1].location = 1;
1944 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1945 attributeDescriptions[1].offset = offsetof(Vertex2D, color);
1946
1947 attributeDescriptions[2].binding = 0;
1948 attributeDescriptions[2].location = 2;
1949 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1950 attributeDescriptions[2].offset = offsetof(Vertex2D, textureCoordinates);
1951
1952 return attributeDescriptions;
1953}
1954
1955template<>
1956std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<VertexPositionColorTexture>()
1957{
1958 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1959 attributeDescriptions.resize(3);
1960
1961 attributeDescriptions[0].binding = 0;
1962 attributeDescriptions[0].location = 0;
1963 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1964 attributeDescriptions[0].offset = offsetof(VertexPositionColorTexture, position);
1965
1966 attributeDescriptions[1].binding = 0;
1967 attributeDescriptions[1].location = 1;
1968 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1969 attributeDescriptions[1].offset = offsetof(VertexPositionColorTexture, color);
1970
1971 attributeDescriptions[2].binding = 0;
1972 attributeDescriptions[2].location = 2;
1973 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1974 attributeDescriptions[2].offset = offsetof(VertexPositionColorTexture, textureCoordinates);
1975
1976 return attributeDescriptions;
1977}
1978
1979template<>
1980std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<VertexPositionColorTextureNormalTangent>()
1981{
1982 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1983 attributeDescriptions.resize(5);
1984
1985 attributeDescriptions[0].binding = 0;
1986 attributeDescriptions[0].location = 0;
1987 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1988 attributeDescriptions[0].offset = offsetof(VertexPositionColorTextureNormalTangent, position);
1989
1990 attributeDescriptions[1].binding = 0;
1991 attributeDescriptions[1].location = 1;
1992 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1993 attributeDescriptions[1].offset = offsetof(VertexPositionColorTextureNormalTangent, color);
1994
1995 attributeDescriptions[2].binding = 0;
1996 attributeDescriptions[2].location = 2;
1997 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1998 attributeDescriptions[2].offset = offsetof(VertexPositionColorTextureNormalTangent, textureCoordinates);
1999
2000 attributeDescriptions[3].binding = 0;
2001 attributeDescriptions[3].location = 3;
2002 attributeDescriptions[3].format = VK_FORMAT_R32G32B32_SFLOAT;
2003 attributeDescriptions[3].offset = offsetof(VertexPositionColorTextureNormalTangent, normal);
2004
2005 attributeDescriptions[4].binding = 0;
2006 attributeDescriptions[4].location = 4;
2007 attributeDescriptions[4].format = VK_FORMAT_R32G32B32_SFLOAT;
2008 attributeDescriptions[4].offset = offsetof(VertexPositionColorTextureNormalTangent, tangent);
2009
2010 return attributeDescriptions;
2011}
2012
2013} //rendering_engine
virtual ScreenSettings GetScreenSettings() const =0
Retrieves the current screen or window settings.
Interface for backend-specific material GPU resources.
Interface for GPU mesh resource management.
Interface for rendering backend resource aggregation and submission.
Interface for observing renderer resource lifecycle events.
Interface for backend-specific GPU texture resource management.
Abstract interface for platform-specific window management.
virtual void ResetFramebufferResizedFlag()=0
Resets the framebuffer resized flag after handling a resize event.
virtual void * GetNativeHandle() const =0
Returns a pointer to the underlying native window handle.
virtual const IApplication & GetApplication()=0
Retrieves a reference to the owning application instance.
virtual bool IsFramebufferResized() const =0
Checks if the framebuffer has been resized since the last frame.
Represents a material instance with parameter values, texture bindings, and rendering configuration.
Definition material.hpp:30
std::vector< std::string > GetTextures() const
Returns the list of texture names used by this material.
Definition material.cpp:82
PackedMaterialData PackMaterialParameters()
Packs the current float/vector parameters into a binary buffer and layout metadata.
Definition material.cpp:39
const MaterialSettings GetMaterialSettings() const
Returns the material's static settings (domain, blend mode, shading model, etc.).
Definition material.cpp:22
Vulkan-specific implementation of material render resources.
Vulkan implementation of the mesh GPU resource interface.
Vulkan implementation of IRenderResources for the Rendering Engine.
Vulkan-based implementation of the IRenderer interface.
bool BeginFrame() override
Begins frame rendering operations.
VulkanRenderer(IWindowSystem &windowSystem)
Constructs a VulkanRenderer bound to a specific window system.
ITextureRenderResources * ProvideTextureRenderResources() const override
Provides access to texture-related GPU resources.
VkPhysicalDeviceFeatures & GetPhysDevSupportedFeatures()
Returns reference to the physical device�s supported features.
void GenerateMipmaps(VkImage image, VkFormat imageFormat, int32_t texWidth, int32_t texHeight, uint32_t mipLevels)
Generates mipmaps for a given image resource.
void CopyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
Copies buffer data into an image (used for uploading texture data).
VkDevice & GetLogicalDevice()
Returns reference to the logical Vulkan device.
VkDescriptorSetLayout CreateDescriptorSetLayout(Material *material)
Creates a descriptor set layout corresponding to a given material.
IRenderResources * ProvideRenderResources() const override
Provides access to the general rendering resource manager.
void InitializeRenderer() override
Initializes all rendering subsystems and GPU resources.
void BeginRenderPass() override
Begins the active render pass for the current frame.
IMeshRenderResources * ProvideMeshRenderResources() const override
Provides access to mesh-related GPU resources.
void CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
Copies data from one buffer to another using a temporary command buffer.
void EndRenderPass() override
Ends the active render pass.
void EndFrame() override
Completes the current frame rendering and presents the result.
VkPhysicalDevice & GetPhysicalDevice()
Returns reference to the physical device.
void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer &buffer, VkDeviceMemory &bufferMemory)
Creates a new Vulkan buffer with the specified usage and memory properties.
void UnregisterObserver(IRendererObserver *notifier) override
Unregisters a previously registered observer.
void WaitIdle() override
Waits until the GPU has finished all pending rendering operations.
std::pair< VkPipelineLayout, VkPipeline > CreateGraphicsPipeline(Material *material, VkDescriptorSetLayout &descriptorSetLayout, std::vector< char > &spvVertShaderCode, std::vector< char > &spvFragShaderCode)
Creates a Vulkan graphics pipeline based on material and shader inputs.
void DrawFrame() override
Executes a full frame rendering cycle.
static VkVertexInputBindingDescription GetBindingDescription()
Returns a vertex input binding description for a specific vertex type.
void AddDeferredDestroy(DeferredItem deferredItem)
uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
Finds a suitable memory type for a given allocation.
void SetDefaultColor(float r, float g, float b) override
Sets the renderer clear color.
void TransitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, std::uint32_t mipmapLevels)
Transitions the image layout for a given Vulkan image.
void RegisterObserver(IRendererObserver *notifier) override
Registers an observer for rendering events.
void ShutdownRenderer() override
Destroys and cleans up all rendering resources.
std::vector< VkCommandBuffer > GetComandBuffers()
Returns the collection of command buffers used for rendering.
IMaterialRenderResources * ProvideMaterialRenderResources() const override
Provides access to material-related GPU resources.
Vulkan-specific implementation of ITextureRenderResources.
Engine-wide logging system for runtime diagnostics and performance tracking.
#define LOG_WARNING(msg)
Definition logger.hpp:40
#define LOG_DEBUG(msg)
Definition logger.hpp:38
#define LOG_ERROR(msg)
Definition logger.hpp:41
#define LOG_INFO(msg)
Definition logger.hpp:39
const bool enableValidationLayers
const int MAX_FRAMES_IN_FLIGHT
Number of frames that can be processed simultaneously (double buffering).
Contains the raw buffer data and layout metadata of packed material parameters.
std::string name
The window or application name.