7#ifdef VISUTWIN_HAS_VULKAN
9#define VMA_IMPLEMENTATION
13#include <VkBootstrap.h>
14#include <SDL3/SDL_vulkan.h>
26#include "spdlog/spdlog.h"
29#include "engine/shaders/vulkan/forward_basic_spirv.h"
39 _window = options.window;
42 SDL_GetWindowSize(_window, &w, &h);
46 initInstance(_window);
50 VmaAllocatorCreateInfo allocatorInfo{};
51 allocatorInfo.physicalDevice = _physicalDevice;
52 allocatorInfo.device = _device;
53 allocatorInfo.instance = _instance;
54 allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_3;
55 vmaCreateAllocator(&allocatorInfo, &_vmaAllocator);
57 initSwapchain(_width, _height);
58 createDepthResources();
59 createPerFrameResources();
62 VkCommandPoolCreateInfo uploadPoolInfo{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};
63 uploadPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
64 uploadPoolInfo.queueFamilyIndex = _graphicsQueueFamily;
65 vkCreateCommandPool(_device, &uploadPoolInfo,
nullptr, &_uploadCommandPool);
67 VkFenceCreateInfo uploadFenceInfo{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
68 vkCreateFence(_device, &uploadFenceInfo,
nullptr, &_uploadFence);
71 std::array<VkDescriptorPoolSize, 2> poolSizes{};
72 poolSizes[0] = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 256};
73 poolSizes[1] = {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1024};
75 VkDescriptorPoolCreateInfo dpInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
77 dpInfo.poolSizeCount =
static_cast<uint32_t
>(poolSizes.size());
78 dpInfo.pPoolSizes = poolSizes.data();
79 dpInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
80 vkCreateDescriptorPool(_device, &dpInfo,
nullptr, &_descriptorPool);
83 _renderPipeline = std::make_unique<VulkanRenderPipeline>(
this);
86 VkSamplerCreateInfo samplerInfo{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
87 samplerInfo.magFilter = VK_FILTER_LINEAR;
88 samplerInfo.minFilter = VK_FILTER_LINEAR;
89 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
90 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
91 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
92 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
93 samplerInfo.maxLod = VK_LOD_CLAMP_NONE;
94 vkCreateSampler(_device, &samplerInfo,
nullptr, &_defaultSampler);
98 VkImageCreateInfo imgInfo{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
99 imgInfo.imageType = VK_IMAGE_TYPE_2D;
100 imgInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
101 imgInfo.extent = {1, 1, 1};
102 imgInfo.mipLevels = 1;
103 imgInfo.arrayLayers = 1;
104 imgInfo.samples = VK_SAMPLE_COUNT_1_BIT;
105 imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
106 imgInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
108 VmaAllocationCreateInfo aInfo{};
109 aInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
110 vmaCreateImage(_vmaAllocator, &imgInfo, &aInfo, &_whiteImage, &_whiteAllocation,
nullptr);
112 VkImageViewCreateInfo viewInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
113 viewInfo.image = _whiteImage;
114 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
115 viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
116 viewInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
117 vkCreateImageView(_device, &viewInfo,
nullptr, &_whiteImageView);
120 uint32_t whitePixel = 0xFFFFFFFF;
122 VmaAllocation stagingAlloc;
123 VkBufferCreateInfo sInfo{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
125 sInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
126 VmaAllocationCreateInfo saInfo{};
127 saInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
128 vmaCreateBuffer(_vmaAllocator, &sInfo, &saInfo, &stagingBuf, &stagingAlloc,
nullptr);
130 vmaMapMemory(_vmaAllocator, stagingAlloc, &mapped);
131 memcpy(mapped, &whitePixel, 4);
132 vmaUnmapMemory(_vmaAllocator, stagingAlloc);
134 vulkanImmediateSubmit(
this, [&](VkCommandBuffer cmd) {
135 vulkanTransitionImageLayout(cmd, _whiteImage,
136 VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
137 VkBufferImageCopy region{};
138 region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
139 region.imageExtent = {1, 1, 1};
140 vkCmdCopyBufferToImage(cmd, stagingBuf, _whiteImage,
141 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
142 vulkanTransitionImageLayout(cmd, _whiteImage,
143 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
144 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
146 vmaDestroyBuffer(_vmaAllocator, stagingBuf, stagingAlloc);
149 spdlog::info(
"VulkanGraphicsDevice initialized ({}x{})", _width, _height);
152 VulkanGraphicsDevice::~VulkanGraphicsDevice()
154 if (_device != VK_NULL_HANDLE)
155 vkDeviceWaitIdle(_device);
157 _renderPipeline.reset();
159 if (_defaultSampler != VK_NULL_HANDLE)
160 vkDestroySampler(_device, _defaultSampler,
nullptr);
161 if (_whiteImageView != VK_NULL_HANDLE)
162 vkDestroyImageView(_device, _whiteImageView,
nullptr);
163 if (_whiteImage != VK_NULL_HANDLE)
164 vmaDestroyImage(_vmaAllocator, _whiteImage, _whiteAllocation);
166 if (_descriptorPool != VK_NULL_HANDLE)
167 vkDestroyDescriptorPool(_device, _descriptorPool,
nullptr);
169 destroyPerFrameResources();
171 if (_uploadFence != VK_NULL_HANDLE)
172 vkDestroyFence(_device, _uploadFence,
nullptr);
173 if (_uploadCommandPool != VK_NULL_HANDLE)
174 vkDestroyCommandPool(_device, _uploadCommandPool,
nullptr);
176 destroyDepthResources();
179 if (_vmaAllocator != VK_NULL_HANDLE)
180 vmaDestroyAllocator(_vmaAllocator);
181 if (_device != VK_NULL_HANDLE)
182 vkDestroyDevice(_device,
nullptr);
183 if (_surface != VK_NULL_HANDLE)
184 vkDestroySurfaceKHR(_instance, _surface,
nullptr);
185 if (_debugMessenger != VK_NULL_HANDLE) {
186 auto fn =
reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT
>(
187 vkGetInstanceProcAddr(_instance,
"vkDestroyDebugUtilsMessengerEXT"));
188 if (fn) fn(_instance, _debugMessenger,
nullptr);
190 if (_instance != VK_NULL_HANDLE)
191 vkDestroyInstance(_instance,
nullptr);
193 spdlog::info(
"VulkanGraphicsDevice destroyed");
200 void VulkanGraphicsDevice::initInstance(SDL_Window* window)
202 vkb::InstanceBuilder builder;
203 builder.set_app_name(
"VisuTwin Canvas")
204 .set_engine_name(
"VisuTwin")
205 .require_api_version(1, 3, 0)
206 .request_validation_layers(
true)
207 .use_default_debug_messenger();
209 auto result = builder.build();
211 spdlog::error(
"Failed to create Vulkan instance: {}", result.error().message());
214 auto vkbInstance = result.value();
215 _instance = vkbInstance.instance;
216 _debugMessenger = vkbInstance.debug_messenger;
218 if (!SDL_Vulkan_CreateSurface(window, _instance,
nullptr, &_surface)) {
219 spdlog::error(
"Failed to create Vulkan surface");
223 void VulkanGraphicsDevice::initDevice()
225 VkPhysicalDeviceVulkan13Features features13{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES};
226 features13.dynamicRendering = VK_TRUE;
227 features13.synchronization2 = VK_TRUE;
229 vkb::PhysicalDeviceSelector selector{vkb::Instance{_instance, _debugMessenger}};
230 selector.set_surface(_surface)
231 .set_minimum_version(1, 3)
232 .set_required_features_13(features13);
234 auto physResult = selector.select();
236 spdlog::error(
"Failed to select Vulkan physical device: {}", physResult.error().message());
239 auto vkbPhysical = physResult.value();
240 _physicalDevice = vkbPhysical.physical_device;
242 VkPhysicalDeviceProperties props;
243 vkGetPhysicalDeviceProperties(_physicalDevice, &props);
244 spdlog::info(
"Vulkan device: {}", props.deviceName);
246 vkb::DeviceBuilder deviceBuilder{vkbPhysical};
247 auto devResult = deviceBuilder.build();
249 spdlog::error(
"Failed to create Vulkan device: {}", devResult.error().message());
252 auto vkbDevice = devResult.value();
253 _device = vkbDevice.device;
255 auto qr = vkbDevice.get_queue(vkb::QueueType::graphics);
256 if (qr) _graphicsQueue = qr.value();
257 auto qi = vkbDevice.get_queue_index(vkb::QueueType::graphics);
258 if (qi) _graphicsQueueFamily = qi.value();
261 void VulkanGraphicsDevice::initSwapchain(
int width,
int height)
263 vkb::SwapchainBuilder swapBuilder{_physicalDevice, _device, _surface};
264 swapBuilder.set_desired_extent(
static_cast<uint32_t
>(width),
static_cast<uint32_t
>(height))
265 .set_desired_format({VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR})
266 .set_desired_present_mode(VK_PRESENT_MODE_FIFO_KHR)
267 .add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT);
269 auto result = swapBuilder.build();
271 spdlog::error(
"Failed to create Vulkan swapchain: {}", result.error().message());
274 auto vkbSwap = result.value();
275 _swapchain = vkbSwap.swapchain;
276 _swapchainFormat = vkbSwap.image_format;
277 _swapchainExtent = vkbSwap.extent;
278 _swapchainImages = vkbSwap.get_images().value();
279 _swapchainImageViews = vkbSwap.get_image_views().value();
282 void VulkanGraphicsDevice::cleanupSwapchain()
284 for (
auto view : _swapchainImageViews)
285 vkDestroyImageView(_device, view,
nullptr);
286 _swapchainImageViews.clear();
287 _swapchainImages.clear();
288 if (_swapchain != VK_NULL_HANDLE) {
289 vkDestroySwapchainKHR(_device, _swapchain,
nullptr);
290 _swapchain = VK_NULL_HANDLE;
294 void VulkanGraphicsDevice::createDepthResources()
296 VkImageCreateInfo imageInfo{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
297 imageInfo.imageType = VK_IMAGE_TYPE_2D;
298 imageInfo.format = _depthFormat;
299 imageInfo.extent = {_swapchainExtent.width, _swapchainExtent.height, 1};
300 imageInfo.mipLevels = 1;
301 imageInfo.arrayLayers = 1;
302 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
303 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
304 imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
306 VmaAllocationCreateInfo allocInfo{};
307 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
308 vmaCreateImage(_vmaAllocator, &imageInfo, &allocInfo,
309 &_depthImage, &_depthAllocation,
nullptr);
311 VkImageViewCreateInfo viewInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
312 viewInfo.image = _depthImage;
313 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
314 viewInfo.format = _depthFormat;
315 viewInfo.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1};
316 vkCreateImageView(_device, &viewInfo,
nullptr, &_depthImageView);
319 void VulkanGraphicsDevice::destroyDepthResources()
321 if (_depthImageView != VK_NULL_HANDLE)
322 vkDestroyImageView(_device, _depthImageView,
nullptr);
323 if (_depthImage != VK_NULL_HANDLE)
324 vmaDestroyImage(_vmaAllocator, _depthImage, _depthAllocation);
325 _depthImageView = VK_NULL_HANDLE;
326 _depthImage = VK_NULL_HANDLE;
327 _depthAllocation = VK_NULL_HANDLE;
330 void VulkanGraphicsDevice::createPerFrameResources()
332 for (
auto& frame : _frames) {
333 VkCommandPoolCreateInfo poolInfo{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};
334 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
335 poolInfo.queueFamilyIndex = _graphicsQueueFamily;
336 vkCreateCommandPool(_device, &poolInfo,
nullptr, &frame.commandPool);
338 VkCommandBufferAllocateInfo allocInfo{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
339 allocInfo.commandPool = frame.commandPool;
340 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
341 allocInfo.commandBufferCount = 1;
342 vkAllocateCommandBuffers(_device, &allocInfo, &frame.commandBuffer);
344 VkSemaphoreCreateInfo semInfo{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
345 vkCreateSemaphore(_device, &semInfo,
nullptr, &frame.imageAvailable);
346 vkCreateSemaphore(_device, &semInfo,
nullptr, &frame.renderFinished);
348 VkFenceCreateInfo fenceInfo{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
349 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
350 vkCreateFence(_device, &fenceInfo,
nullptr, &frame.inFlightFence);
354 void VulkanGraphicsDevice::destroyPerFrameResources()
356 for (
auto& frame : _frames) {
357 if (frame.inFlightFence != VK_NULL_HANDLE)
358 vkDestroyFence(_device, frame.inFlightFence,
nullptr);
359 if (frame.renderFinished != VK_NULL_HANDLE)
360 vkDestroySemaphore(_device, frame.renderFinished,
nullptr);
361 if (frame.imageAvailable != VK_NULL_HANDLE)
362 vkDestroySemaphore(_device, frame.imageAvailable,
nullptr);
363 if (frame.commandPool != VK_NULL_HANDLE)
364 vkDestroyCommandPool(_device, frame.commandPool,
nullptr);
373 void VulkanGraphicsDevice::onFrameStart()
375 auto& frame = _frames[_frameIndex];
377 vkWaitForFences(_device, 1, &frame.inFlightFence, VK_TRUE, UINT64_MAX);
378 vkResetFences(_device, 1, &frame.inFlightFence);
380 VkResult result = vkAcquireNextImageKHR(_device, _swapchain, UINT64_MAX,
381 frame.imageAvailable, VK_NULL_HANDLE, &_swapchainImageIndex);
382 if (result == VK_ERROR_OUT_OF_DATE_KHR) {
383 setResolution(_width, _height);
387 vkResetCommandBuffer(frame.commandBuffer, 0);
389 VkCommandBufferBeginInfo beginInfo{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
390 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
391 vkBeginCommandBuffer(frame.commandBuffer, &beginInfo);
395 vkResetDescriptorPool(_device, _descriptorPool, 0);
398 void VulkanGraphicsDevice::onFrameEnd()
400 auto& frame = _frames[_frameIndex];
401 VkCommandBuffer cmd = frame.commandBuffer;
404 vulkanTransitionImageLayout(cmd, _swapchainImages[_swapchainImageIndex],
405 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
406 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
408 vkEndCommandBuffer(cmd);
411 VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
412 VkSubmitInfo submitInfo{VK_STRUCTURE_TYPE_SUBMIT_INFO};
413 submitInfo.waitSemaphoreCount = 1;
414 submitInfo.pWaitSemaphores = &frame.imageAvailable;
415 submitInfo.pWaitDstStageMask = &waitStage;
416 submitInfo.commandBufferCount = 1;
417 submitInfo.pCommandBuffers = &cmd;
418 submitInfo.signalSemaphoreCount = 1;
419 submitInfo.pSignalSemaphores = &frame.renderFinished;
420 vkQueueSubmit(_graphicsQueue, 1, &submitInfo, frame.inFlightFence);
423 VkPresentInfoKHR presentInfo{VK_STRUCTURE_TYPE_PRESENT_INFO_KHR};
424 presentInfo.waitSemaphoreCount = 1;
425 presentInfo.pWaitSemaphores = &frame.renderFinished;
426 presentInfo.swapchainCount = 1;
427 presentInfo.pSwapchains = &_swapchain;
428 presentInfo.pImageIndices = &_swapchainImageIndex;
429 vkQueuePresentKHR(_graphicsQueue, &presentInfo);
431 _frameIndex = (_frameIndex + 1) % kMaxFramesInFlight;
438 void VulkanGraphicsDevice::startRenderPass(
RenderPass* renderPass)
440 auto& frame = _frames[_frameIndex];
441 VkCommandBuffer cmd = frame.commandBuffer;
444 vulkanTransitionImageLayout(cmd, _swapchainImages[_swapchainImageIndex],
445 VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
448 VkImageMemoryBarrier depthBarrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
449 depthBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
450 depthBarrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
451 depthBarrier.srcAccessMask = 0;
452 depthBarrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
453 depthBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
454 depthBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
455 depthBarrier.image = _depthImage;
456 depthBarrier.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1};
457 vkCmdPipelineBarrier(cmd,
458 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
459 VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
460 0, 0,
nullptr, 0,
nullptr, 1, &depthBarrier);
463 auto colorOps = renderPass ? renderPass->colorOps() :
nullptr;
465 VkRenderingAttachmentInfo colorAttachment{VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO};
466 colorAttachment.imageView = _swapchainImageViews[_swapchainImageIndex];
467 colorAttachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
468 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
470 if (colorOps && colorOps->clear) {
471 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
472 colorAttachment.clearValue.color = {{
473 colorOps->clearValue.r, colorOps->clearValue.g,
474 colorOps->clearValue.b, colorOps->clearValue.a}};
476 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
479 VkRenderingAttachmentInfo depthAttachment{VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO};
480 depthAttachment.imageView = _depthImageView;
481 depthAttachment.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
482 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
483 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
484 depthAttachment.clearValue.depthStencil = {1.0f, 0};
486 auto dsOps = renderPass ? renderPass->depthStencilOps() :
nullptr;
488 depthAttachment.loadOp = dsOps->clearDepth ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
489 depthAttachment.storeOp = dsOps->storeDepth ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE;
490 depthAttachment.clearValue.depthStencil = {dsOps->clearDepthValue, 0};
493 VkRenderingInfo renderingInfo{VK_STRUCTURE_TYPE_RENDERING_INFO};
494 renderingInfo.renderArea = {{0, 0}, _swapchainExtent};
495 renderingInfo.layerCount = 1;
496 renderingInfo.colorAttachmentCount = 1;
497 renderingInfo.pColorAttachments = &colorAttachment;
498 renderingInfo.pDepthAttachment = &depthAttachment;
500 vkCmdBeginRendering(cmd, &renderingInfo);
503 VkViewport viewport{};
505 viewport.y =
static_cast<float>(_swapchainExtent.height) - _vy;
506 viewport.width = _vw > 0 ? _vw :
static_cast<float>(_swapchainExtent.width);
507 viewport.height = -(_vh > 0 ? _vh :
static_cast<float>(_swapchainExtent.height));
508 viewport.minDepth = 0.0f;
509 viewport.maxDepth = 1.0f;
510 vkCmdSetViewport(cmd, 0, 1, &viewport);
513 scissor.offset = {_sx, _sy};
515 _sw > 0 ?
static_cast<uint32_t
>(_sw) : _swapchainExtent.width,
516 _sh > 0 ? static_cast<uint32_t>(_sh) : _swapchainExtent.height
518 vkCmdSetScissor(cmd, 0, 1, &scissor);
520 _dynamicRenderingActive =
true;
521 _insideRenderPass =
true;
522 _currentPipeline = VK_NULL_HANDLE;
523 _pushConstantsDirty =
true;
526 void VulkanGraphicsDevice::endRenderPass(
RenderPass* renderPass)
529 if (_dynamicRenderingActive) {
530 auto& frame = _frames[_frameIndex];
531 vkCmdEndRendering(frame.commandBuffer);
532 _dynamicRenderingActive =
false;
534 _insideRenderPass =
false;
541 void VulkanGraphicsDevice::draw(
const Primitive& primitive,
542 const std::shared_ptr<IndexBuffer>& indexBuffer,
543 int numInstances,
int indirectSlot,
bool first,
bool last)
546 if (!_shader || !_dynamicRenderingActive)
return;
548 auto& frame = _frames[_frameIndex];
549 VkCommandBuffer cmd = frame.commandBuffer;
551 auto vulkanShader = std::dynamic_pointer_cast<VulkanShader>(_shader);
552 if (!vulkanShader || vulkanShader->vertexModule() == VK_NULL_HANDLE)
return;
555 auto vf = !_vertexBuffers.empty() ? _vertexBuffers[0] :
nullptr;
557 VkPipeline pipeline = _renderPipeline->get(primitive,
558 vf ? vf->format() :
nullptr,
559 vulkanShader, _blendState, _depthState, _cullMode,
560 _swapchainFormat, _depthFormat);
562 if (pipeline != _currentPipeline) {
563 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
564 _currentPipeline = pipeline;
565 _pushConstantsDirty =
true;
570 auto* vb =
static_cast<VulkanVertexBuffer*
>(vf.get());
571 if (vb->buffer() != VK_NULL_HANDLE) {
572 VkBuffer buf = vb->buffer();
573 VkDeviceSize offset = 0;
574 vkCmdBindVertexBuffers(cmd, 0, 1, &buf, &offset);
580 if (_pushConstantsDirty) {
581 vkCmdPushConstants(cmd, _renderPipeline->pipelineLayout(),
582 VK_SHADER_STAGE_VERTEX_BIT, 0,
sizeof(PushConstants), &_pushConstants);
583 _pushConstantsDirty =
false;
589 VkDescriptorSet texSet;
590 VkDescriptorSetAllocateInfo dsAlloc{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
591 dsAlloc.descriptorPool = _descriptorPool;
592 dsAlloc.descriptorSetCount = 1;
593 auto layout = _renderPipeline->textureSetLayout();
594 dsAlloc.pSetLayouts = &layout;
596 if (vkAllocateDescriptorSets(_device, &dsAlloc, &texSet) == VK_SUCCESS) {
598 VkImageView texView = _whiteImageView;
599 VkSampler texSampler = _defaultSampler;
603 std::vector<TextureSlot> texSlots;
604 _material->getTextureSlots(texSlots);
605 for (
auto& ts : texSlots) {
606 if (ts.slot == 0 && ts.texture !=
nullptr) {
607 auto* impl = ts.texture->impl();
609 auto* vkTex =
static_cast<gpu::VulkanTexture*
>(impl);
610 if (vkTex->imageView() != VK_NULL_HANDLE) {
611 texView = vkTex->imageView();
612 if (vkTex->sampler() != VK_NULL_HANDLE)
613 texSampler = vkTex->sampler();
622 std::array<VkDescriptorImageInfo, 6> imageInfos{};
623 std::array<VkWriteDescriptorSet, 6> writes{};
624 for (uint32_t i = 0; i < 6; i++) {
625 imageInfos[i].sampler = _defaultSampler;
626 imageInfos[i].imageView = _whiteImageView;
627 imageInfos[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
629 writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
630 writes[i].dstSet = texSet;
631 writes[i].dstBinding = i;
632 writes[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
633 writes[i].descriptorCount = 1;
634 writes[i].pImageInfo = &imageInfos[i];
637 imageInfos[0].sampler = texSampler;
638 imageInfos[0].imageView = texView;
640 vkUpdateDescriptorSets(_device, 6, writes.data(), 0,
nullptr);
641 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
642 _renderPipeline->pipelineLayout(), 1, 1, &texSet, 0,
nullptr);
648 auto* ib =
static_cast<VulkanIndexBuffer*
>(indexBuffer.get());
649 if (ib->buffer() != VK_NULL_HANDLE) {
651 ? VK_INDEX_TYPE_UINT32 : VK_INDEX_TYPE_UINT16;
652 vkCmdBindIndexBuffer(cmd, ib->buffer(), 0, idxType);
653 vkCmdDrawIndexed(cmd, primitive.count, numInstances,
654 primitive.base, primitive.baseVertex, 0);
657 vkCmdDraw(cmd, primitive.count, numInstances, primitive.base, 0);
664 _currentPipeline = VK_NULL_HANDLE;
672 void VulkanGraphicsDevice::setTransformUniforms(
675 memcpy(_pushConstants.viewProjection, viewProjection.c, 64);
676 memcpy(_pushConstants.model, model.c, 64);
677 _pushConstantsDirty =
true;
680 void VulkanGraphicsDevice::setLightingUniforms(
const Color& ambientColor,
681 const std::vector<GpuLightData>& lights,
const Vector3& cameraPosition,
682 bool enableNormalMaps,
float exposure,
const FogParams& fogParams,
685 (void)ambientColor; (void)lights; (void)cameraPosition;
686 (void)enableNormalMaps; (void)exposure; (void)fogParams;
687 (void)shadowParams; (void)toneMapping;
691 void VulkanGraphicsDevice::setEnvironmentUniforms(
692 Texture* envAtlas,
float skyboxIntensity,
float skyboxMip,
693 const Vector3& skyDomeCenter,
bool isDome,
Texture* skyboxCubeMap)
695 (void)envAtlas; (void)skyboxIntensity; (void)skyboxMip;
696 (void)skyDomeCenter; (void)isDome; (void)skyboxCubeMap;
703 std::shared_ptr<Shader> VulkanGraphicsDevice::createShader(
708 return std::make_shared<VulkanShader>(
this, definition,
709 vulkan_spirv::kForwardBasicVert, vulkan_spirv::kForwardBasicVertSize,
710 vulkan_spirv::kForwardBasicFrag, vulkan_spirv::kForwardBasicFragSize);
713 std::unique_ptr<gpu::HardwareTexture> VulkanGraphicsDevice::createGPUTexture(
Texture* texture)
715 return std::make_unique<gpu::VulkanTexture>(texture);
718 std::shared_ptr<VertexBuffer> VulkanGraphicsDevice::createVertexBuffer(
719 const std::shared_ptr<VertexFormat>& format,
int numVertices,
722 return std::make_shared<VulkanVertexBuffer>(
this, format, numVertices, options);
725 std::shared_ptr<IndexBuffer> VulkanGraphicsDevice::createIndexBuffer(
726 IndexFormat format,
int numIndices,
const std::vector<uint8_t>& data)
728 auto ib = std::make_shared<VulkanIndexBuffer>(
this, format, numIndices);
729 if (!data.empty()) ib->setData(data);
733 std::shared_ptr<RenderTarget> VulkanGraphicsDevice::createRenderTarget(
745 void VulkanGraphicsDevice::setResolution(
int width,
int height)
747 if (width == _width && height == _height)
return;
751 if (_device != VK_NULL_HANDLE) {
752 vkDeviceWaitIdle(_device);
753 destroyDepthResources();
755 initSwapchain(_width, _height);
756 createDepthResources();
760 std::pair<int, int> VulkanGraphicsDevice::size()
const
762 return {_width, _height};
GPU texture resource supporting 2D, cubemap, volume, and array formats with mipmap management.
RGBA color with floating-point components in [0, 1].
4x4 column-major transformation matrix with SIMD acceleration.
Describes how vertex and index data should be interpreted for a draw call.
3D vector for positions, directions, and normals with multi-backend SIMD acceleration.