Rendering Engine 0.2.9
Modular Graphics Rendering Engine | v0.2.9
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
327{
328 return new VulkanTextureResources(const_cast<VulkanRenderer*>(this));
329}
330
332{
333 return new VulkanMaterialResources(const_cast<VulkanRenderer*>(this));
334}
335
337{
338 return new VulkanMeshResources(const_cast<VulkanRenderer*>(this));
339}
340
341void VulkanRenderer::SetDefaultColor(float r, float g, float b)
342{
343 mDefaultColor = glm::vec3(r, g, b);
344}
345
347{
348 return new VulkanRenderResources(const_cast<VulkanRenderer*>(this));
349}
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 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
722 {
723 LOG_ERROR(std::string("Vulkan validation: ") + pCallbackData->pMessage);
724 }
725 else
726 {
727 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
728 {
729 LOG_WARNING(std::string("Vulkan validation: ") + pCallbackData->pMessage);
730 }
731 else
732 {
733 LOG_DEBUG(std::string("Vulkan validation: ") + pCallbackData->pMessage);
734 }
735 }
736
737 return VK_FALSE;
738}
739
740void VulkanRenderer::SetupDebugMessenger()
741{
743 {
744 return;
745 }
746
747 VkDebugUtilsMessengerCreateInfoEXT createInfo{};
748 PopulateDebugMessengerCreateInfo(createInfo);
749
750 if (CreateDebugUtilsMessengerEXT(mInstance, &createInfo, nullptr, &mDebugMessenger) != VK_SUCCESS)
751 {
752 LOG_ERROR("failed to set up debug messenger!");
753 throw std::runtime_error("failed to set up debug messenger!");
754 }
755}
756
757VkResult VulkanRenderer::CreateDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerCreateInfoEXT const* pCreateInfo, VkAllocationCallbacks const* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger)
758{
759 auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
760 if (func != nullptr)
761 {
762 return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
763 }
764 else
765 {
766 return VK_ERROR_EXTENSION_NOT_PRESENT;
767 }
768}
769
770void VulkanRenderer::DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator)
771{
772 auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
773 if (func != nullptr)
774 {
775 func(instance, debugMessenger, pAllocator);
776 }
777}
778
779void VulkanRenderer::CreateSurface()
780{
781 GLFWwindow* window = static_cast<GLFWwindow*>(mWindowSystem.GetNativeHandle());
782 if (glfwCreateWindowSurface(mInstance, window, nullptr, &mSurface) != VK_SUCCESS)
783 {
784 LOG_ERROR("failed to create window surface!");
785 throw std::runtime_error("failed to create window surface!");
786 }
787}
788
789void VulkanRenderer::PickPhysicalDevice()
790{
791 uint32_t deviceCount = 0;
792 vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr);
793
794 if (deviceCount == 0)
795 {
796 LOG_ERROR("failed to find GPUs with Vulkan support!");
797 throw std::runtime_error("failed to find GPUs with Vulkan support!");
798 }
799
800 std::vector<VkPhysicalDevice> devices(deviceCount);
801 vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data());
802
803 for (const auto& device : devices)
804 {
805 if (IsDeviceSuitable(device)) {
806 mPhysicalDevice = device;
807 vkGetPhysicalDeviceFeatures(mPhysicalDevice, &mPhysDevSupportedFeatures);
808 vkGetPhysicalDeviceProperties(mPhysicalDevice, &mPhysicalDeviceProperties);
809 mMSAASamples = CheckMaxUsableSampleCount();
810 break;
811 }
812 }
813
814 if (mPhysicalDevice == VK_NULL_HANDLE)
815 {
816 LOG_ERROR("Failed to find a suitable GPU!");
817 throw std::runtime_error("failed to find a suitable GPU!");
818 }
819 LOG_INFO(std::string("Selected GPU: ") +
820 mPhysicalDeviceProperties.deviceName);
821}
822
823bool VulkanRenderer::IsDeviceSuitable(VkPhysicalDevice device)
824{
825 // Next features could be used for rating and checkink proper device
826
827 //VkPhysicalDeviceProperties deviceProperties;
828 //vkGetPhysicalDeviceProperties(device, &deviceProperties);
829
830 // VkPhysicalDeviceFeatures supportedFeatures;
831 // vkGetPhysicalDeviceFeatures(device, &supportedFeatures);
832
833 //return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
834 // deviceFeatures.geometryShader;
835
836 QueueFamilyIndices indices = FindQueueFamilies(device);
837
838 bool extensionsSupported = CheckDeviceExtensionSupport(device);
839
840 bool swapChainAdequate = false;
841 if (extensionsSupported)
842 {
843 auto const swapChainSupport = QuerySwapChainSupport(device);
844 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
845 }
846 return indices.IsComplete() && extensionsSupported && swapChainAdequate;
847}
848
849QueueFamilyIndices VulkanRenderer::FindQueueFamilies(VkPhysicalDevice device)
850{
851 QueueFamilyIndices indices;
852 // Logic to find queue family indices to populate struct with
853 uint32_t queueFamilyCount = 0;
854 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
855
856 std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
857 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
858
859 int i = 0;
860 for (const auto& queueFamily : queueFamilies)
861 {
862 if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
863 {
864 indices.graphicsFamily = i;
865 }
866
867 VkBool32 presentSupport = false;
868 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, mSurface, &presentSupport);
869 if (presentSupport)
870 {
871 indices.presentFamily = i;
872 }
873 if (indices.IsComplete())
874 {
875 break;
876 }
877
878 i++;
879 }
880 return indices;
881}
882
883bool VulkanRenderer::CheckDeviceExtensionSupport(VkPhysicalDevice physicalDevice)
884{
885 uint32_t extensionCount;
886 vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr);
887
888 std::vector<VkExtensionProperties> availableExtensions(extensionCount);
889 vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, availableExtensions.data());
890
891 std::set<std::string> requiredExtensions(mDeviceExtensions.begin(), mDeviceExtensions.end());
892
893 for (const auto& extension : availableExtensions)
894 {
895 requiredExtensions.erase(extension.extensionName);
896 }
897
898 return requiredExtensions.empty();
899}
900
901SwapChainSupportDetails VulkanRenderer::QuerySwapChainSupport(VkPhysicalDevice device)
902{
903 SwapChainSupportDetails scsDetails;
904
905 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, mSurface, &scsDetails.capabilities);
906
907 uint32_t formatCount;
908 vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, nullptr);
909
910 if (formatCount != 0)
911 {
912 scsDetails.formats.resize(formatCount);
913 vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, scsDetails.formats.data());
914 }
915
916 uint32_t presentModeCount;
917 vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, nullptr);
918
919 if (presentModeCount != 0)
920 {
921 scsDetails.presentModes.resize(presentModeCount);
922 vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, scsDetails.presentModes.data());
923 }
924
925 return scsDetails;
926}
927
928VkSampleCountFlagBits VulkanRenderer::CheckMaxUsableSampleCount()
929{
930 VkPhysicalDeviceProperties physicalDeviceProperties;
931 vkGetPhysicalDeviceProperties(mPhysicalDevice, &physicalDeviceProperties);
932
933 VkSampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts & physicalDeviceProperties.limits.framebufferDepthSampleCounts;
934 if (counts & VK_SAMPLE_COUNT_64_BIT) { return VK_SAMPLE_COUNT_64_BIT; }
935 if (counts & VK_SAMPLE_COUNT_32_BIT) { return VK_SAMPLE_COUNT_32_BIT; }
936 if (counts & VK_SAMPLE_COUNT_16_BIT) { return VK_SAMPLE_COUNT_16_BIT; }
937 if (counts & VK_SAMPLE_COUNT_8_BIT) { return VK_SAMPLE_COUNT_8_BIT; }
938 if (counts & VK_SAMPLE_COUNT_4_BIT) { return VK_SAMPLE_COUNT_4_BIT; }
939 if (counts & VK_SAMPLE_COUNT_2_BIT) { return VK_SAMPLE_COUNT_2_BIT; }
940
941 return VK_SAMPLE_COUNT_1_BIT;
942}
943
944void VulkanRenderer::CreateLogicalDevice()
945{
946 QueueFamilyIndices indices = FindQueueFamilies(mPhysicalDevice);
947
948 std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
949 std::set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
950
951 float queuePriority = 1.0f;
952 for (uint32_t queueFamily : uniqueQueueFamilies)
953 {
954 VkDeviceQueueCreateInfo queueCreateInfo{};
955 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
956 queueCreateInfo.queueFamilyIndex = queueFamily;
957 queueCreateInfo.queueCount = 1;
958 queueCreateInfo.pQueuePriorities = &queuePriority;
959 queueCreateInfos.push_back(queueCreateInfo);
960 }
961
962 VkDeviceQueueCreateInfo queueCreateInfo{};
963 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
964 queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value();
965 queueCreateInfo.queueCount = 1;
966
967 queueCreateInfo.pQueuePriorities = &queuePriority;
968
969 VkPhysicalDeviceFeatures deviceFeatures{};
970 if (mPhysDevSupportedFeatures.samplerAnisotropy)
971 {
972 deviceFeatures.samplerAnisotropy = VK_TRUE;
973 }
974 else
975 {
976 deviceFeatures.samplerAnisotropy = VK_FALSE;
977 }
978
979 if (mPhysDevSupportedFeatures.sampleRateShading)
980 {
981 deviceFeatures.sampleRateShading = VK_TRUE;
982 }
983 else
984 {
985 deviceFeatures.sampleRateShading = VK_FALSE;
986 }
987
988 VkDeviceCreateInfo createInfo{};
989 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
990
991 createInfo.pQueueCreateInfos = queueCreateInfos.data();
992 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
993
994 createInfo.pEnabledFeatures = &deviceFeatures;
995
996 createInfo.enabledExtensionCount = static_cast<uint32_t>(mDeviceExtensions.size());
997 createInfo.ppEnabledExtensionNames = mDeviceExtensions.data();
998
1000 {
1001 createInfo.enabledLayerCount = static_cast<uint32_t>(mValidationLayers.size());
1002 createInfo.ppEnabledLayerNames = mValidationLayers.data();
1003 }
1004 else
1005 {
1006 createInfo.enabledLayerCount = 0;
1007 }
1008
1009 if (vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mLogicalDevice) != VK_SUCCESS)
1010 {
1011 LOG_ERROR("failed to create logical device!");
1012 throw std::runtime_error("failed to create logical device!");
1013 }
1014
1015 vkGetDeviceQueue(mLogicalDevice, indices.graphicsFamily.value(), 0, &mGraphicsQueue);
1016 vkGetDeviceQueue(mLogicalDevice, indices.presentFamily.value(), 0, &mPresentQueue);
1017 LOG_INFO("Logical device created.");
1018}
1019
1020void VulkanRenderer::CreateSwapChain()
1021{
1022 SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(mPhysicalDevice);
1023
1024 VkSurfaceFormatKHR surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.formats);
1025 // TO Disable VSync VkPresentModeKHR presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
1026 VkPresentModeKHR presentMode = ChooseSwapPresentMode(swapChainSupport.presentModes);
1027 VkExtent2D extent = ChooseSwapExtent(swapChainSupport.capabilities);
1028
1029 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
1030 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount)
1031 {
1032 imageCount = swapChainSupport.capabilities.maxImageCount;
1033 }
1034
1035 VkSwapchainCreateInfoKHR createInfo{};
1036 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1037 createInfo.surface = mSurface;
1038
1039 createInfo.minImageCount = imageCount;
1040 createInfo.imageFormat = surfaceFormat.format;
1041 createInfo.imageColorSpace = surfaceFormat.colorSpace;
1042 createInfo.imageExtent = extent;
1043 createInfo.imageArrayLayers = 1;
1044 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1045
1046 QueueFamilyIndices indices = FindQueueFamilies(mPhysicalDevice);
1047 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1048
1049 if (indices.graphicsFamily != indices.presentFamily) {
1050 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
1051 createInfo.queueFamilyIndexCount = 2;
1052 createInfo.pQueueFamilyIndices = queueFamilyIndices;
1053 }
1054 else
1055 {
1056 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1057 createInfo.queueFamilyIndexCount = 0; // Optional
1058 createInfo.pQueueFamilyIndices = nullptr; // Optional
1059 }
1060
1061 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
1062 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1063 createInfo.presentMode = presentMode;
1064 createInfo.clipped = VK_TRUE;
1065 createInfo.oldSwapchain = VK_NULL_HANDLE;
1066
1067 if (vkCreateSwapchainKHR(mLogicalDevice, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS)
1068 {
1069 LOG_ERROR("failed to create swap chain!");
1070 throw std::runtime_error("failed to create swap chain!");
1071 }
1072
1073 vkGetSwapchainImagesKHR(mLogicalDevice, mSwapChain, &imageCount, nullptr);
1074 mSwapChainImages.resize(imageCount);
1075 vkGetSwapchainImagesKHR(mLogicalDevice, mSwapChain, &imageCount, mSwapChainImages.data());
1076
1077 mSwapChainImageFormat = surfaceFormat.format;
1078 mSwapChainExtent = extent;
1079
1080 LOG_INFO("Swapchain created. Images: " +
1081 std::to_string(mSwapChainImages.size()) +
1082 ", Resolution: " +
1083 std::to_string(mSwapChainExtent.width) + "x" +
1084 std::to_string(mSwapChainExtent.height));
1085}
1086
1087void VulkanRenderer::RecreateSwapChain()
1088{
1089 LOG_WARNING("Recreating swapchain...");
1090 int width = 0;
1091 int height = 0;
1092
1093 while (width == 0 || height == 0)
1094 {
1095 GLFWwindow* window = static_cast<GLFWwindow*>(mWindowSystem.GetNativeHandle());
1096 glfwGetFramebufferSize(window, &width, &height);
1097 glfwWaitEvents();
1098 }
1099 vkDeviceWaitIdle(mLogicalDevice);
1100
1101 CleanupSwapChain();
1102
1103 for (auto& observer : mObservers)
1104 {
1105 observer->OnRenderResourcesRelease();
1106 }
1107
1108 CreateSwapChain();
1109 CreateImageViews();
1110 CreateRenderPass();
1111
1112 CreateColorResources();
1113 CreateDepthResources();
1114 CreateFramebuffers();
1115 CreateCommandBuffers();
1116
1117 CreateSwapchainSyncObjects();
1118
1119 for (auto& observer : mObservers)
1120 {
1121 observer->OnRenderResourcesRebuild();
1122 }
1123 LOG_INFO("Swapchain recreated successfully.");
1124}
1125
1126VkSurfaceFormatKHR VulkanRenderer::ChooseSwapSurfaceFormat(std::vector<VkSurfaceFormatKHR> const& availableFormats)
1127{
1128 for (const auto& availableFormat : availableFormats)
1129 {
1130 if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
1131 {
1132 return availableFormat;
1133 }
1134 }
1135
1136 return availableFormats[0];
1137}
1138
1139VkPresentModeKHR VulkanRenderer::ChooseSwapPresentMode(std::vector<VkPresentModeKHR>const& availablePresentModes)
1140{
1141 for (const auto& availablePresentMode : availablePresentModes)
1142 {
1143 if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR)
1144 {
1145 return availablePresentMode;
1146 }
1147 }
1148 return VK_PRESENT_MODE_FIFO_KHR;
1149}
1150
1151VkExtent2D VulkanRenderer::ChooseSwapExtent(VkSurfaceCapabilitiesKHR const& capabilities)
1152{
1153 if (capabilities.currentExtent.width != UINT32_MAX)
1154 {
1155 return capabilities.currentExtent;
1156 }
1157 else
1158 {
1159 int width, height;
1160 GLFWwindow* window = static_cast<GLFWwindow*>(mWindowSystem.GetNativeHandle());
1161 glfwGetFramebufferSize(window, &width, &height);
1162
1163 VkExtent2D actualExtent = {
1164 static_cast<uint32_t>(width),
1165 static_cast<uint32_t>(height)
1166 };
1167
1168 actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
1169 actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
1170
1171 return actualExtent;
1172 }
1173}
1174
1175void VulkanRenderer::CreateImageViews()
1176{
1177 mSwapChainImageViews.resize(mSwapChainImages.size());
1178
1179 for (uint32_t i = 0; i < mSwapChainImages.size(); i++)
1180 {
1181 mSwapChainImageViews[i] = CreateImageView(mSwapChainImages[i], mSwapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
1182 }
1183}
1184
1185VkImageView VulkanRenderer::CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, std::uint32_t mipmapLevels)
1186{
1187 VkImageViewCreateInfo viewInfo{};
1188 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1189 viewInfo.image = image;
1190 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1191 viewInfo.format = format;
1192 viewInfo.subresourceRange.aspectMask = aspectFlags;
1193 viewInfo.subresourceRange.baseMipLevel = 0;
1194 viewInfo.subresourceRange.levelCount = mipmapLevels;
1195 viewInfo.subresourceRange.baseArrayLayer = 0;
1196 viewInfo.subresourceRange.layerCount = 1;
1197
1198 VkImageView imageView;
1199 if (vkCreateImageView(mLogicalDevice, &viewInfo, nullptr, &imageView) != VK_SUCCESS)
1200 {
1201 LOG_ERROR("failed to create texture image view!");
1202 throw std::runtime_error("failed to create texture image view!");
1203 }
1204
1205 return imageView;
1206}
1207
1208void VulkanRenderer::CreateRenderPass()
1209{
1210 VkAttachmentDescription colorAttachment{};
1211 colorAttachment.format = mSwapChainImageFormat;
1212 colorAttachment.samples = mMSAASamples;
1213 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1214 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1215 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1216 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1217 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1218 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1219
1220 VkAttachmentReference colorAttachmentRef{};
1221 colorAttachmentRef.attachment = 0;
1222 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1223
1224 VkAttachmentDescription depthAttachment{};
1225 depthAttachment.format = FindDepthFormat();
1226 depthAttachment.samples = mMSAASamples;
1227 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1228 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1229 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1230 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1231 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1232 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1233
1234 VkAttachmentReference depthAttachmentRef{};
1235 depthAttachmentRef.attachment = 1;
1236 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1237
1238 VkAttachmentDescription colorAttachmentResolve{};
1239 colorAttachmentResolve.format = mSwapChainImageFormat;
1240 colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT;
1241 colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1242 colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1243 colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1244 colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1245 colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1246 colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1247
1248 VkAttachmentReference colorAttachmentResolveRef{};
1249 colorAttachmentResolveRef.attachment = 2;
1250 colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1251
1252 VkSubpassDescription subpass{};
1253 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1254 subpass.colorAttachmentCount = 1;
1255 subpass.pColorAttachments = &colorAttachmentRef;
1256 subpass.pDepthStencilAttachment = &depthAttachmentRef;
1257 subpass.pResolveAttachments = &colorAttachmentResolveRef;
1258
1259 VkSubpassDependency dependency{};
1260 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
1261 dependency.dstSubpass = 0;
1262 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
1263 dependency.srcAccessMask = 0;
1264 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
1265 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
1266
1267 std::array<VkAttachmentDescription, 3> attachments = { colorAttachment, depthAttachment, colorAttachmentResolve };
1268
1269 VkRenderPassCreateInfo renderPassInfo{};
1270 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1271 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1272 renderPassInfo.pAttachments = attachments.data();
1273 renderPassInfo.subpassCount = 1;
1274 renderPassInfo.pSubpasses = &subpass;
1275 renderPassInfo.dependencyCount = 1;
1276 renderPassInfo.pDependencies = &dependency;
1277
1278 if (vkCreateRenderPass(mLogicalDevice, &renderPassInfo, nullptr, &mRenderPass) != VK_SUCCESS)
1279 {
1280 LOG_ERROR("failed to create render pass!");
1281 throw std::runtime_error("failed to create render pass!");
1282 }
1283}
1284
1285VkFormat VulkanRenderer::FindDepthFormat()
1286{
1287 return FindSupportedFormat(
1288 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
1289 VK_IMAGE_TILING_OPTIMAL,
1290 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
1291 );
1292}
1293
1294VkFormat VulkanRenderer::FindSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features)
1295{
1296 for (VkFormat format : candidates)
1297 {
1298 VkFormatProperties props;
1299 vkGetPhysicalDeviceFormatProperties(mPhysicalDevice, format, &props);
1300
1301 if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features)
1302 {
1303 return format;
1304 }
1305 else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features)
1306 {
1307 return format;
1308 }
1309 }
1310 LOG_ERROR("failed to find supported format!");
1311 throw std::runtime_error("failed to find supported format!");
1312}
1313
1314void VulkanRenderer::CreateCommandPool()
1315{
1316 QueueFamilyIndices queueFamilyIndices = FindQueueFamilies(mPhysicalDevice);
1317
1318 VkCommandPoolCreateInfo poolInfo{};
1319 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1320 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
1321 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
1322
1323 if (vkCreateCommandPool(mLogicalDevice, &poolInfo, nullptr, &mCommandPool) != VK_SUCCESS)
1324 {
1325 LOG_ERROR("failed to create command pool!");
1326 throw std::runtime_error("failed to create command pool!");
1327 }
1328}
1329
1330void VulkanRenderer::CreateColorResources()
1331{
1332 VkFormat colorFormat = mSwapChainImageFormat;
1333
1334 CreateVulkanImage(mSwapChainExtent.width, mSwapChainExtent.height, 1, mMSAASamples, colorFormat, VK_IMAGE_TILING_OPTIMAL,
1335 VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
1336 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mColorImage, mColorImageMemory);
1337 mColorImageView = CreateImageView(mColorImage, colorFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
1338}
1339
1340void 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)
1341{
1342 VkImageCreateInfo imageInfo{};
1343 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1344 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1345 imageInfo.extent.width = static_cast<uint32_t>(width);
1346 imageInfo.extent.height = static_cast<uint32_t>(height);
1347 imageInfo.extent.depth = 1;
1348 imageInfo.mipLevels = mipmapLevels;
1349 imageInfo.arrayLayers = 1;
1350
1351 imageInfo.format = format;
1352 imageInfo.tiling = tiling;
1353
1354 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1355
1356 imageInfo.usage = usage;
1357
1358 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1359
1360 imageInfo.samples = numSamples;
1361 imageInfo.flags = 0; // Optional
1362
1363 if (vkCreateImage(mLogicalDevice, &imageInfo, nullptr, &image) != VK_SUCCESS)
1364 {
1365 LOG_ERROR("failed to create image!");
1366 throw std::runtime_error("failed to create image!");
1367 }
1368
1369 VkMemoryRequirements memRequirements;
1370 vkGetImageMemoryRequirements(mLogicalDevice, image, &memRequirements);
1371
1372 VkMemoryAllocateInfo allocInfo{};
1373 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1374 allocInfo.allocationSize = memRequirements.size;
1375 allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties);
1376
1377 if (vkAllocateMemory(mLogicalDevice, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
1378 LOG_ERROR("failed to allocate image memory!");
1379 throw std::runtime_error("failed to allocate image memory!");
1380 }
1381
1382 vkBindImageMemory(mLogicalDevice, image, imageMemory, 0);
1383}
1384
1385uint32_t VulkanRenderer::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
1386{
1387 VkPhysicalDeviceMemoryProperties memProperties;
1388 vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProperties);
1389
1390 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
1391 {
1392 if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
1393 {
1394 return i;
1395 }
1396 }
1397 LOG_ERROR("failed to find suitable memory type!");
1398 throw std::runtime_error("failed to find suitable memory type!");
1399}
1400
1402{
1403 return mPhysicalDevice;
1404}
1405
1407{
1408 return mPhysDevSupportedFeatures;
1409}
1410
1412{
1413 std::uint32_t bindingNumber = 0;
1414 std::vector<VkDescriptorSetLayoutBinding> bindings;
1415
1416 // Uniform buffer to transmit transformation matrix
1417 VkDescriptorSetLayoutBinding uboLayoutBinding{};
1418 uboLayoutBinding.binding = bindingNumber;
1419 uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1420 uboLayoutBinding.descriptorCount = 1;
1421 uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
1422 uboLayoutBinding.pImmutableSamplers = nullptr; // Optional
1423
1424 bindings.push_back(uboLayoutBinding);
1425
1426 PackedMaterialData packedMaterialData = material->PackMaterialParameters();
1427 if (!packedMaterialData.buffer.empty())
1428 {
1429 ++bindingNumber;
1430 // Uniform buffer to transmit custom material variables
1431 VkDescriptorSetLayoutBinding customMaterialVariables;
1432 customMaterialVariables.binding = bindingNumber;
1433 customMaterialVariables.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1434 customMaterialVariables.descriptorCount = 1;
1435 customMaterialVariables.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
1436 customMaterialVariables.pImmutableSamplers = nullptr; // Optional
1437
1438 bindings.push_back(customMaterialVariables);
1439 }
1440
1441 for (const auto& textureName : material->GetTextures())
1442 {
1443 (void)textureName;
1444 ++bindingNumber;
1445 VkDescriptorSetLayoutBinding samplerLayoutBinding{};
1446 samplerLayoutBinding.binding = bindingNumber;
1447 samplerLayoutBinding.descriptorCount = 1;
1448 samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1449 samplerLayoutBinding.pImmutableSamplers = nullptr;
1450 samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
1451
1452 bindings.push_back(samplerLayoutBinding);
1453 }
1454
1455 VkDescriptorSetLayoutCreateInfo layoutInfo{};
1456 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
1457 layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
1458 layoutInfo.pBindings = bindings.data();
1459
1460 VkDescriptorSetLayout result;
1461 if (vkCreateDescriptorSetLayout(mLogicalDevice, &layoutInfo, nullptr, &result) != VK_SUCCESS)
1462 {
1463 LOG_ERROR("failed to create descriptor set layout!");
1464 throw std::runtime_error("failed to create descriptor set layout!");
1465 }
1466
1467 return result;
1468}
1469
1470std::vector<VkCommandBuffer> VulkanRenderer::GetComandBuffers()
1471{
1472 return mCommandBuffers;
1473}
1474
1475std::pair<VkPipelineLayout, VkPipeline> VulkanRenderer::CreateGraphicsPipeline(Material* material, VkDescriptorSetLayout& descriptorSetLayout, std::vector<char>& spvVertShaderCode, std::vector<char>& spvFragShaderCode)
1476{
1477 VkPipelineLayout pipelineLayout;
1478 VkPipeline pipeline;
1479
1480 VkShaderModule vertShaderModule = CreateShaderModule(spvVertShaderCode);
1481 VkShaderModule fragShaderModule = CreateShaderModule(spvFragShaderCode);
1482
1483 VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
1484 vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1485 vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
1486
1487 vertShaderStageInfo.module = vertShaderModule;
1488 vertShaderStageInfo.pName = "main";
1489
1490 VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
1491 fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1492 fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
1493 fragShaderStageInfo.module = fragShaderModule;
1494 fragShaderStageInfo.pName = "main";
1495
1496 VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
1497
1498 VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
1499 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
1500
1501 VkVertexInputBindingDescription bindingDescription;
1502 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1503
1505 {
1506 bindingDescription = GetBindingDescription<Vertex2D>();
1507 attributeDescriptions = GetAttributeDescriptions<Vertex2D>();
1508 }
1510 {
1511 bindingDescription = GetBindingDescription<VertexPositionColorTextureNormalTangent>();
1512 attributeDescriptions = GetAttributeDescriptions<VertexPositionColorTextureNormalTangent>();
1513 }
1514
1515 vertexInputInfo.vertexBindingDescriptionCount = 1;
1516 vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
1517
1518 vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
1519 vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
1520
1521 VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
1522 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
1523 inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
1524 inputAssembly.primitiveRestartEnable = VK_FALSE;
1525
1526 VkViewport viewport{};
1527 viewport.x = 0.0f;
1528 viewport.y = 0.0f;
1529 viewport.width = (float)mSwapChainExtent.width;
1530 viewport.height = (float)mSwapChainExtent.height;
1531 viewport.minDepth = 0.0f;
1532 viewport.maxDepth = 1.0f;
1533
1534 VkRect2D scissor{};
1535 scissor.offset = { 0, 0 };
1536 scissor.extent = mSwapChainExtent;
1537
1538 VkPipelineViewportStateCreateInfo viewportState{};
1539 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
1540 viewportState.viewportCount = 1;
1541 viewportState.pViewports = &viewport;
1542 viewportState.scissorCount = 1;
1543 viewportState.pScissors = &scissor;
1544
1545 VkPipelineRasterizationStateCreateInfo rasterizer{};
1546 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
1547 rasterizer.depthClampEnable = VK_FALSE;
1548 rasterizer.rasterizerDiscardEnable = VK_FALSE;
1549 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
1550 rasterizer.lineWidth = 1.0f;
1552 {
1553 rasterizer.cullMode = VK_CULL_MODE_NONE;
1554 }
1555 else
1556 {
1557 rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
1558 }
1559 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
1560 rasterizer.depthBiasEnable = VK_FALSE;
1561 rasterizer.depthBiasConstantFactor = 0.0f; // Optional
1562 rasterizer.depthBiasClamp = 0.0f; // Optional
1563 rasterizer.depthBiasSlopeFactor = 0.0f; // Optional
1564
1565 VkPipelineMultisampleStateCreateInfo multisampling{};
1566 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
1567 multisampling.sampleShadingEnable = VK_FALSE;
1568 multisampling.rasterizationSamples = mMSAASamples;
1569 multisampling.minSampleShading = 1.0f; // Optional
1570 multisampling.pSampleMask = nullptr; // Optional
1571 multisampling.alphaToCoverageEnable = VK_FALSE; // Optional
1572 multisampling.alphaToOneEnable = VK_FALSE; // Optional
1573
1574 VkPipelineColorBlendAttachmentState colorBlendAttachment{};
1575 colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
1576 colorBlendAttachment.blendEnable = (material->GetMaterialSettings().blendMode == BlendMode::Translucent) ? VK_TRUE : VK_FALSE;
1577 colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
1578 colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
1579 colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional
1580 colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
1581 colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
1582 colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional
1583
1584 VkPipelineColorBlendStateCreateInfo colorBlending{};
1585 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
1586 colorBlending.logicOpEnable = VK_FALSE;
1587 colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional
1588 colorBlending.attachmentCount = 1;
1589 colorBlending.pAttachments = &colorBlendAttachment;
1590 colorBlending.blendConstants[0] = 0.0f; // Optional
1591 colorBlending.blendConstants[1] = 0.0f; // Optional
1592 colorBlending.blendConstants[2] = 0.0f; // Optional
1593 colorBlending.blendConstants[3] = 0.0f; // Optional
1594
1595 // To use dynamicState, assign reference to it in pipelineInfo.pDynamicState.
1596 std::vector<VkDynamicState> dynamicStates = {
1597 VK_DYNAMIC_STATE_VIEWPORT,
1598 VK_DYNAMIC_STATE_SCISSOR
1599 };
1600 VkPipelineDynamicStateCreateInfo dynamicState{};
1601 dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
1602 dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
1603 dynamicState.pDynamicStates = dynamicStates.data();
1604
1605 VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
1606 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
1607 pipelineLayoutInfo.setLayoutCount = 1;
1608 pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
1609
1610 if (vkCreatePipelineLayout(mLogicalDevice, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS)
1611 {
1612 LOG_ERROR("failed to create pipeline layout!");
1613 throw std::runtime_error("failed to create pipeline layout!");
1614 }
1615
1616 VkPipelineDepthStencilStateCreateInfo depthStencil{};
1617 depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
1619 {
1620 depthStencil.depthTestEnable = VK_FALSE;
1621 depthStencil.depthWriteEnable = VK_FALSE;
1622 }
1623 else
1624 {
1625 depthStencil.depthTestEnable = VK_TRUE;
1626 depthStencil.depthWriteEnable = (material->GetMaterialSettings().blendMode == BlendMode::Opaque) ? VK_TRUE : VK_FALSE;
1627 }
1628 depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
1629 depthStencil.depthBoundsTestEnable = VK_FALSE;
1630 //depthStencil.minDepthBounds = 0.0f; // Optional
1631 //depthStencil.maxDepthBounds = 1.0f; // Optional
1632 depthStencil.stencilTestEnable = VK_FALSE;
1633 //depthStencil.front = {}; // Optional
1634 depthStencil.back = {}; // Optional
1635
1636 VkGraphicsPipelineCreateInfo pipelineInfo{};
1637 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
1638 pipelineInfo.stageCount = 2;
1639 pipelineInfo.pStages = shaderStages;
1640 pipelineInfo.pVertexInputState = &vertexInputInfo;
1641 pipelineInfo.pInputAssemblyState = &inputAssembly;
1642 pipelineInfo.pViewportState = &viewportState;
1643 pipelineInfo.pRasterizationState = &rasterizer;
1644 pipelineInfo.pMultisampleState = &multisampling;
1645 pipelineInfo.pDepthStencilState = &depthStencil;
1646 pipelineInfo.pColorBlendState = &colorBlending;
1647 pipelineInfo.pDynamicState = nullptr;
1648 pipelineInfo.layout = pipelineLayout;
1649 pipelineInfo.renderPass = mRenderPass;
1650 pipelineInfo.subpass = 0;
1651 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional
1652 pipelineInfo.basePipelineIndex = -1; // Optional
1653
1654
1655 if (vkCreateGraphicsPipelines(mLogicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline) != VK_SUCCESS)
1656 {
1657 LOG_ERROR("failed to create graphics pipeline!");
1658 throw std::runtime_error("failed to create graphics pipeline!");
1659 }
1660 vkDestroyShaderModule(mLogicalDevice, fragShaderModule, nullptr);
1661 vkDestroyShaderModule(mLogicalDevice, vertShaderModule, nullptr);
1662
1663 return std::pair<VkPipelineLayout, VkPipeline>(pipelineLayout, pipeline);
1664}
1665
1666void VulkanRenderer::CreateDepthResources()
1667{
1668 VkFormat depthFormat = FindDepthFormat();
1669
1670 CreateVulkanImage(mSwapChainExtent.width, mSwapChainExtent.height, 1, mMSAASamples, depthFormat,
1671 VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1672 mDepthImage, mDepthImageMemory);
1673 mDepthImageView = CreateImageView(mDepthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1);
1674}
1675
1676void VulkanRenderer::CreateFramebuffers()
1677{
1678 mSwapChainFramebuffers.resize(mSwapChainImageViews.size());
1679
1680 for (size_t i = 0; i < mSwapChainImageViews.size(); i++)
1681 {
1682 std::array<VkImageView, 3> attachments = { mColorImageView, mDepthImageView, mSwapChainImageViews[i] };
1683
1684 VkFramebufferCreateInfo framebufferInfo{};
1685 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1686 framebufferInfo.renderPass = mRenderPass;
1687 framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1688 framebufferInfo.pAttachments = attachments.data();
1689 framebufferInfo.width = mSwapChainExtent.width;
1690 framebufferInfo.height = mSwapChainExtent.height;
1691 framebufferInfo.layers = 1;
1692
1693 if (vkCreateFramebuffer(mLogicalDevice, &framebufferInfo, nullptr, &mSwapChainFramebuffers[i]) != VK_SUCCESS)
1694 {
1695 LOG_ERROR("failed to create framebuffer!");
1696 throw std::runtime_error("failed to create framebuffer!");
1697 }
1698 }
1699}
1700
1701void VulkanRenderer::CreateCommandBuffers()
1702{
1703 mCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
1704
1705 VkCommandBufferAllocateInfo allocInfo{};
1706 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1707 allocInfo.commandPool = mCommandPool;
1708 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1709 allocInfo.commandBufferCount = (uint32_t)mCommandBuffers.size();
1710
1711 if (vkAllocateCommandBuffers(mLogicalDevice, &allocInfo, mCommandBuffers.data()) != VK_SUCCESS)
1712 {
1713 LOG_ERROR("failed to allocate command buffers!");
1714 throw std::runtime_error("failed to allocate command buffers!");
1715 }
1716}
1717
1718void VulkanRenderer::CreateFrameSyncObjects()
1719{
1720 mImageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
1721 mInFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
1722
1723 VkSemaphoreCreateInfo semaphoreInfo{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
1724 VkFenceCreateInfo fenceInfo{ 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{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
1744
1745 for (size_t i = 0; i < mSwapChainImages.size(); i++)
1746 {
1747 if (vkCreateSemaphore(mLogicalDevice, &semaphoreInfo, nullptr, &mRenderFinishedSemaphores[i]) != VK_SUCCESS)
1748 {
1749 LOG_ERROR("failed to create per-image renderFinished semaphores!");
1750 throw std::runtime_error("failed to create per-image renderFinished semaphores!");
1751 }
1752 }
1753}
1754
1755VkShaderModule VulkanRenderer::CreateShaderModule(std::vector<char>& code)
1756{
1757 VkShaderModuleCreateInfo createInfo{};
1758 createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
1759 createInfo.codeSize = code.size();
1760 createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
1761
1762 VkShaderModule shaderModule;
1763 if (vkCreateShaderModule(mLogicalDevice, &createInfo, nullptr, &shaderModule) != VK_SUCCESS)
1764 {
1765 LOG_ERROR("failed to create shader module!");
1766 throw std::runtime_error("failed to create shader module!");
1767 }
1768
1769 return shaderModule;
1770}
1771
1772void VulkanRenderer::CleanupSwapChain()
1773{
1774 // destroy swapchain-dependent sync (render-finished per image)
1775 for (auto sem : mRenderFinishedSemaphores)
1776 {
1777 vkDestroySemaphore(mLogicalDevice, sem, nullptr);
1778 }
1779 mRenderFinishedSemaphores.clear();
1780 mImagesInFlight.clear();
1781
1782 vkDestroyImageView(mLogicalDevice, mColorImageView, nullptr);
1783 vkDestroyImage(mLogicalDevice, mColorImage, nullptr);
1784 vkFreeMemory(mLogicalDevice, mColorImageMemory, nullptr);
1785
1786 vkDestroyImageView(mLogicalDevice, mDepthImageView, nullptr);
1787 vkDestroyImage(mLogicalDevice, mDepthImage, nullptr);
1788 vkFreeMemory(mLogicalDevice, mDepthImageMemory, nullptr);
1789 for (auto framebuffer : mSwapChainFramebuffers)
1790 {
1791 vkDestroyFramebuffer(mLogicalDevice, framebuffer, nullptr);
1792 }
1793
1794 vkFreeCommandBuffers(mLogicalDevice, mCommandPool, static_cast<uint32_t>(mCommandBuffers.size()), mCommandBuffers.data());
1795
1796 vkDestroyRenderPass(mLogicalDevice, mRenderPass, nullptr);
1797 for (auto imageView : mSwapChainImageViews)
1798 {
1799 vkDestroyImageView(mLogicalDevice, imageView, nullptr);
1800 }
1801 vkDestroySwapchainKHR(mLogicalDevice, mSwapChain, nullptr);
1802}
1803
1804void VulkanRenderer::RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
1805{
1806 VkCommandBufferBeginInfo beginInfo{};
1807 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1808 //beginInfo.flags = 0; // Optional
1809 //beginInfo.pInheritanceInfo = nullptr; // Optional
1810
1811 if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS)
1812 {
1813 LOG_ERROR("failed to begin recording command buffer!");
1814 throw std::runtime_error("failed to begin recording command buffer!");
1815 }
1816
1817 VkRenderPassBeginInfo renderPassInfo{};
1818 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1819 renderPassInfo.renderPass = mRenderPass;
1820 renderPassInfo.framebuffer = mSwapChainFramebuffers[imageIndex];
1821
1822 renderPassInfo.renderArea.offset = { 0, 0 };
1823 renderPassInfo.renderArea.extent = mSwapChainExtent;
1824
1825 std::array<VkClearValue, 2> clearValues{};
1826 clearValues[0].color = { {mDefaultColor.r, mDefaultColor.g, mDefaultColor.b, 1.0f} };
1827 clearValues[1].depthStencil = { 1.0f, 0 };
1828 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
1829 renderPassInfo.pClearValues = clearValues.data();
1830
1831 vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
1832
1833 vkCmdEndRenderPass(commandBuffer);
1834
1835 if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS)
1836 {
1837 LOG_ERROR("failed to record command buffer!");
1838 throw std::runtime_error("failed to record command buffer!");
1839 }
1840}
1841
1842VkCommandBuffer VulkanRenderer::BeginSingleTimeCommand()
1843{
1844 VkCommandBufferAllocateInfo allocInfo{};
1845 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1846 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1847 allocInfo.commandPool = mCommandPool;
1848 allocInfo.commandBufferCount = 1;
1849
1850 VkCommandBuffer commandBuffer;
1851 vkAllocateCommandBuffers(mLogicalDevice, &allocInfo, &commandBuffer);
1852
1853 VkCommandBufferBeginInfo beginInfo{};
1854 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1855 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1856
1857 vkBeginCommandBuffer(commandBuffer, &beginInfo);
1858
1859 return commandBuffer;
1860}
1861
1862bool VulkanRenderer::HasStencilComponent(VkFormat format)
1863{
1864 return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
1865}
1866
1867void VulkanRenderer::EndSingleTimeCommand(VkCommandBuffer commandBuffer)
1868{
1869 vkEndCommandBuffer(commandBuffer);
1870
1871 VkSubmitInfo submitInfo{};
1872 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1873 submitInfo.commandBufferCount = 1;
1874 submitInfo.pCommandBuffers = &commandBuffer;
1875
1876 vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
1877 vkQueueWaitIdle(mGraphicsQueue);
1878
1879 vkFreeCommandBuffers(mLogicalDevice, mCommandPool, 1, &commandBuffer);
1880}
1881
1882void VulkanRenderer::ProcessDeferredDestruction()
1883{
1884 std::vector<DeferredItem> ripe;
1885
1886 while (!mDeferredQueue.empty() &&
1887 mDeferredQueue.front().retireFrame <= mFrameSerial)
1888 {
1889 ripe.push_back(mDeferredQueue.front());
1890 mDeferredQueue.pop_front();
1891 }
1892
1893 // Correct destruction order
1894 for (auto& o : ripe)
1895 if (o.type == DeferredType::DescriptorPool)
1896 vkDestroyDescriptorPool(mLogicalDevice, o.descriptorPool, nullptr);
1897
1898 for (auto& o : ripe)
1899 if (o.type == DeferredType::Buffer)
1900 vkDestroyBuffer(mLogicalDevice, o.buffer, nullptr);
1901
1902 for (auto& o : ripe)
1903 if (o.type == DeferredType::Memory)
1904 vkFreeMemory(mLogicalDevice, o.memory, nullptr);
1905}
1906
1907template<typename T>
1908VkVertexInputBindingDescription VulkanRenderer::GetBindingDescription()
1909{
1910 VkVertexInputBindingDescription binding{};
1911 binding.binding = 0;
1912 binding.stride = sizeof(T);
1913 binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
1914 return binding;
1915}
1916
1917template<>
1918std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<Vertex2D>()
1919{
1920 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1921 attributeDescriptions.resize(3);
1922
1923 attributeDescriptions[0].binding = 0;
1924 attributeDescriptions[0].location = 0;
1925 attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
1926 attributeDescriptions[0].offset = offsetof(Vertex2D, position);
1927
1928 attributeDescriptions[1].binding = 0;
1929 attributeDescriptions[1].location = 1;
1930 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1931 attributeDescriptions[1].offset = offsetof(Vertex2D, color);
1932
1933 attributeDescriptions[2].binding = 0;
1934 attributeDescriptions[2].location = 2;
1935 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1936 attributeDescriptions[2].offset = offsetof(Vertex2D, textureCoordinates);
1937
1938 return attributeDescriptions;
1939}
1940
1941template<>
1942std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<VertexPositionColorTexture>()
1943{
1944 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1945 attributeDescriptions.resize(3);
1946
1947 attributeDescriptions[0].binding = 0;
1948 attributeDescriptions[0].location = 0;
1949 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1950 attributeDescriptions[0].offset = offsetof(VertexPositionColorTexture, position);
1951
1952 attributeDescriptions[1].binding = 0;
1953 attributeDescriptions[1].location = 1;
1954 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1955 attributeDescriptions[1].offset = offsetof(VertexPositionColorTexture, color);
1956
1957 attributeDescriptions[2].binding = 0;
1958 attributeDescriptions[2].location = 2;
1959 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1960 attributeDescriptions[2].offset = offsetof(VertexPositionColorTexture, textureCoordinates);
1961
1962 return attributeDescriptions;
1963}
1964
1965template<>
1966std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<VertexPositionColorTextureNormalTangent>()
1967{
1968 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1969 attributeDescriptions.resize(5);
1970
1971 attributeDescriptions[0].binding = 0;
1972 attributeDescriptions[0].location = 0;
1973 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1974 attributeDescriptions[0].offset = offsetof(VertexPositionColorTextureNormalTangent, position);
1975
1976 attributeDescriptions[1].binding = 0;
1977 attributeDescriptions[1].location = 1;
1978 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1979 attributeDescriptions[1].offset = offsetof(VertexPositionColorTextureNormalTangent, color);
1980
1981 attributeDescriptions[2].binding = 0;
1982 attributeDescriptions[2].location = 2;
1983 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1984 attributeDescriptions[2].offset = offsetof(VertexPositionColorTextureNormalTangent, textureCoordinates);
1985
1986 attributeDescriptions[3].binding = 0;
1987 attributeDescriptions[3].location = 3;
1988 attributeDescriptions[3].format = VK_FORMAT_R32G32B32_SFLOAT;
1989 attributeDescriptions[3].offset = offsetof(VertexPositionColorTextureNormalTangent, normal);
1990
1991 attributeDescriptions[4].binding = 0;
1992 attributeDescriptions[4].location = 4;
1993 attributeDescriptions[4].format = VK_FORMAT_R32G32B32_SFLOAT;
1994 attributeDescriptions[4].offset = offsetof(VertexPositionColorTextureNormalTangent, tangent);
1995
1996 return attributeDescriptions;
1997}
1998
1999} //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.