| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286 |
- #include "vk_engine.h"
- // VMA
- #define VMA_IMPLEMENTATION
- #include "vk_mem_alloc.h"
- // GLFW Window
- #define GLFW_INCLUDE_VULKAN
- #include <GLFW/glfw3.h>
- // GLM
- #include <gtx/transform.hpp>
- // Bootstrap library
- #include <VkBootstrap.h>
- // STL includes
- #include <iostream>
- #include <iomanip>
- #include <fstream>
- #pragma region macros
- // Vulkan error macro
- using namespace std;
- #define VK_CHECK(x) \
- do \
- { \
- VkResult err = x; \
- if (err) \
- { \
- std::cout << "Detected Vulkan error: " << err << "\n"; \
- abort(); \
- } \
- } while (0)
- #pragma endregion
- // Vulkan includes
- #include "vk_initializers.h"
- #include "vk_pipeline.h"
- #include "vk_texture.h"
- using namespace Coral3D;
- void VulkanEngine::init()
- {
- // WINDOW
- init_window();
- // VULKAN
- init_vulkan();
- init_swapchain();
- init_commands();
- init_def_renderpass();
- init_framebuffers();
- init_sync_structures();
- init_descriptors();
- init_pipelines();
- // SCENES
- load_images();
- load_meshes();
- init_scene();
- m_IsInitialized = true;
- std::cout << "VulkanEngine initialized\n";
- }
- void VulkanEngine::run()
- {
- while (!glfwWindowShouldClose(m_pWindow))
- {
- glfwPollEvents();
- draw();
- }
- glfwTerminate();
- }
- void VulkanEngine::init_window()
- {
- std::cout << "VulkanEngine created\n";
- // Init glfw library
- if (!glfwInit())
- return;
- // Create window without OpenGL context
- glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
- m_pWindow = glfwCreateWindow(1280, 720, "Coral3D", nullptr, nullptr);
- if (!m_pWindow)
- {
- glfwTerminate();
- throw std::runtime_error("Failed to create window");
- }
- if (!glfwInit())
- throw std::runtime_error("Failed to initialize glfw");
- }
- void VulkanEngine::immediate_submit(std::function<void(VkCommandBuffer cmd)>&& function)
- {
- VkCommandBuffer cmd{ m_UploadContext.command_buffer };
-
- // Begin command buffer recording. We use it exact once before resetting
- VkCommandBufferBeginInfo cmdBeginInfo{ vkinit::command_buffer_bi(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) };
- VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
-
- // Execute function
- function(cmd);
- VK_CHECK(vkEndCommandBuffer(cmd));
- VkSubmitInfo submit{ vkinit::submit_info(&cmd) };
- // Submit command buffer to queue and execute it
- // uploadFence will now block until the graphic commands finish execution
- VK_CHECK(vkQueueSubmit(m_GraphicsQueue, 1, &submit, m_UploadContext.upload_fence));
- vkWaitForFences(m_Device, 1, &m_UploadContext.upload_fence, true, UINT64_MAX);
- vkResetFences(m_Device, 1, &m_UploadContext.upload_fence);
- // Reset command buffers inside the pool
- vkResetCommandPool(m_Device, m_UploadContext.command_pool, 0);
- }
- void VulkanEngine::draw()
- {
- // Wait for the GPU to be done with the last frame
- VK_CHECK(vkWaitForFences(m_Device, 1, &get_current_frame().render_fence, true, UINT64_MAX));
- VK_CHECK(vkResetFences(m_Device, 1, &get_current_frame().render_fence));
- // Request image from the swap chain
- uint32_t swapchainImageIndex;
- VK_CHECK(vkAcquireNextImageKHR(m_Device, m_SwapChain, UINT64_MAX, get_current_frame().present_semaphore, nullptr, &swapchainImageIndex));
- // Commands have finished executing, so reset the buffer
- VK_CHECK(vkResetCommandBuffer(get_current_frame().main_command_buffer, 0));
- // Record commands
- VkCommandBufferBeginInfo cmdBeginInfo{};
- cmdBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
- cmdBeginInfo.pNext = nullptr;
- cmdBeginInfo.pInheritanceInfo = nullptr;
- // We record each frame to the command buffer, so we need to let Vulkan know that we're only executing it once
- cmdBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
- // Begin recording
- VK_CHECK(vkBeginCommandBuffer(get_current_frame().main_command_buffer, &cmdBeginInfo));
- // Clear color
- VkClearValue clearValue;
- clearValue.color = { {0.1f, 0.1f, 0.85f, 1.0f} };
- // Depth clear value
- VkClearValue depthClear;
- depthClear.depthStencil.depth = 1.f;
- VkClearValue clearValues[] = { clearValue, depthClear };
- // Begin the render pass
- VkRenderPassBeginInfo rpInfo{};
- rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
- rpInfo.pNext = nullptr;
- // Use the default render pass on the image with the index that we acquired
- rpInfo.renderPass = m_RenderPass;
- rpInfo.renderArea.offset.x = 0;
- rpInfo.renderArea.offset.y = 0;
- rpInfo.renderArea.extent = m_WindowExtent;
- rpInfo.framebuffer = m_Framebuffers[swapchainImageIndex];
- // Connect clear values
- rpInfo.clearValueCount = 2;
- rpInfo.pClearValues = &clearValues[0];
- // Send the begin command to Vulkan
- vkCmdBeginRenderPass(get_current_frame().main_command_buffer, &rpInfo, VK_SUBPASS_CONTENTS_INLINE);
- // RENDER STUFF HERE
- // ...
- draw_objects(get_current_frame().main_command_buffer, m_Renderables.data(), m_Renderables.size());
- // End the render pass
- vkCmdEndRenderPass(get_current_frame().main_command_buffer);
- // End recording
- VK_CHECK(vkEndCommandBuffer(get_current_frame().main_command_buffer));
- // Prepare to submit to the queue
- VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
- VkSubmitInfo submit{};
- submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
- submit.pNext = nullptr;
- submit.pWaitDstStageMask = &waitStage;
- // Wait for image to be ready for rendering before rendering to it
- submit.waitSemaphoreCount = 1;
- submit.pWaitSemaphores = &get_current_frame().present_semaphore;
- // Signal ready when finished rendering
- submit.signalSemaphoreCount = 1;
- submit.pSignalSemaphores = &get_current_frame().render_semaphore;
- submit.commandBufferCount = 1;
- submit.pCommandBuffers = &get_current_frame().main_command_buffer;
- // Submit to the graphics queue
- VK_CHECK(vkQueueSubmit(m_GraphicsQueue, 1, &submit, get_current_frame().render_fence));
- // Present the image to the window when the rendering semaphore has signaled that rendering is done
- VkPresentInfoKHR presentInfo{};
- presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
- presentInfo.pNext = nullptr;
- presentInfo.pSwapchains = &m_SwapChain;
- presentInfo.swapchainCount = 1;
- presentInfo.waitSemaphoreCount = 1;
- presentInfo.pWaitSemaphores = &get_current_frame().render_semaphore;
- // Which image to present
- presentInfo.pImageIndices = &swapchainImageIndex;
- VK_CHECK(vkQueuePresentKHR(m_GraphicsQueue, &presentInfo));
- m_FrameNumber++;
- }
- std::vector<IndirectBatch> Coral3D::VulkanEngine::batch_draws(const std::vector<RenderObject>& renderObjects)
- {
- std::vector<IndirectBatch> draws;
- int id{};
- std::for_each(begin(renderObjects), end(renderObjects), [&](const RenderObject& ro) {
- bool isMeshBatched { !draws.empty() && ro.mesh == draws.back().mesh };
- bool isMaterialBatched { !draws.empty() && ro.material == draws.back().material };
- // Mesh and material combo is already batched, just increase count
- if (isMeshBatched && isMaterialBatched)
- draws.back().count++;
- // Mesh and material combo is not already batched, add new draw
- else
- {
- IndirectBatch newDraw;
- newDraw.mesh = ro.mesh;
- newDraw.material = ro.material;
- newDraw.first = id;
- newDraw.count = 1;
- draws.emplace_back(newDraw);
- }
- id++;
- });
- return draws;
- }
- void VulkanEngine::cleanup()
- {
- if (m_IsInitialized)
- {
- // Wait for the GPU to be done with the last frame
- vkDeviceWaitIdle(m_Device);
- m_MainDeletionQueue.Flush();
- vkDestroyDevice(m_Device, nullptr);
- vkDestroySurfaceKHR(m_Instance, m_Surface, nullptr);
- vkb::destroy_debug_utils_messenger(m_Instance, m_DebugMessenger);
- vkDestroyInstance(m_Instance, nullptr);
- }
- }
- void VulkanEngine::init_vulkan()
- {
- std::cout << "Initializing Vulkan\n";
- vkb::InstanceBuilder builder;
- // Make the Vulkan instance, with basic debug features
- auto instanceDesc = builder.set_app_name("Coral3D")
- #ifdef NDEBUG
- .request_validation_layers(false)
- #else
- .request_validation_layers(true)
- #endif
- .require_api_version(1, 1, 0)
- .use_default_debug_messenger()
- .build();
- vkb::Instance vkbInstance = instanceDesc.value();
- // Store the instance
- m_Instance = vkbInstance.instance;
- // Store the debug messenger
- m_DebugMessenger = vkbInstance.debug_messenger;
- if (glfwCreateWindowSurface(m_Instance, m_pWindow, nullptr, &m_Surface) != VK_SUCCESS) {
- throw std::runtime_error("failed to create window surface!");
- }
- // Get the window size to use for the swap chain
- int width, height;
- glfwGetWindowSize(m_pWindow, &width, &height);
- m_WindowExtent.width = static_cast<uint32_t>(width);
- m_WindowExtent.height = static_cast<uint32_t>(height);
- // use vkbootstrap to select a GPU.
- // We want a GPU that can write to the GLFW surface and supports Vulkan 1.2
- vkb::PhysicalDeviceSelector selector{ vkbInstance };
- VkPhysicalDeviceFeatures features{};
- features.multiDrawIndirect = true;
- features.drawIndirectFirstInstance = true;
- selector.set_required_features(features);
- vkb::PhysicalDevice physicalDevice = selector
- .set_minimum_version(1, 1)
- .set_surface(m_Surface)
- .select()
- .value();
- // create the final Vulkan device
- vkb::DeviceBuilder deviceBuilder{ physicalDevice };
- VkPhysicalDeviceShaderDrawParameterFeatures shaderDrawParams{};
- shaderDrawParams.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES;
- shaderDrawParams.pNext = nullptr;
-
- shaderDrawParams.shaderDrawParameters = VK_TRUE;
- vkb::Device vkbDevice = deviceBuilder.add_pNext(&shaderDrawParams).build().value();
- // Get the VkDevice handle used in the rest of a Vulkan application
- m_Device = vkbDevice.device;
- m_PhysicalDevice = physicalDevice.physical_device;
- // Get the graphics queue for the rest of the Vulkan application
- m_GraphicsQueue = vkbDevice.get_queue(vkb::QueueType::graphics).value();
- m_GraphicsQueueFamily = vkbDevice.get_queue_index(vkb::QueueType::graphics).value();
- VmaAllocatorCreateInfo allocatorInfo{};
- allocatorInfo.physicalDevice = m_PhysicalDevice;
- allocatorInfo.device = m_Device;
- allocatorInfo.instance = m_Instance;
- vmaCreateAllocator(&allocatorInfo, &m_Allocator);
- m_MainDeletionQueue.deletors.emplace_back([=]() {
- vmaDestroyAllocator(m_Allocator);
- });
- m_DeviceProperties = vkbDevice.physical_device.properties;
- std::cout << vkbDevice.physical_device.features.multiDrawIndirect << std::endl;
- std::cout << "The GPU has a minimum buffer alignment of " << m_DeviceProperties.limits.minUniformBufferOffsetAlignment << "\n";
- }
- void VulkanEngine::init_swapchain()
- {
- std::cout << "Initializing Swap Chain\n";
- vkb::SwapchainBuilder swapChainBuilder{ m_PhysicalDevice, m_Device, m_Surface };
- vkb::Swapchain vkbSwapChain = swapChainBuilder
- .use_default_format_selection()
- // use VSync present mode
- .set_desired_present_mode(VK_PRESENT_MODE_MAILBOX_KHR)
- .set_desired_extent(m_WindowExtent.width, m_WindowExtent.height)
- .build()
- .value();
- // store swap chain and its related images
- m_SwapChain = vkbSwapChain.swapchain;
- m_SwapChainImages = vkbSwapChain.get_images().value();
- m_SwapChainImageViews = vkbSwapChain.get_image_views().value();
- m_SwapChainImageFormat = vkbSwapChain.image_format;
- m_MainDeletionQueue.deletors.emplace_back([=]() {
- vkDestroySwapchainKHR(m_Device, m_SwapChain, nullptr);
- });
- // Depth buffer image matches window extents
- VkExtent3D depthImageExtent
- {
- m_WindowExtent.width,
- m_WindowExtent.height,
- 1
- };
- // Depth format (must be changed to support stencil)
- m_DepthFormat = VK_FORMAT_D32_SFLOAT;
- VkImageCreateInfo depthImageCreateInfo = vkinit::image_ci(m_DepthFormat, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depthImageExtent);
-
- // Allocate image on GPU
- VmaAllocationCreateInfo depthImageAllocInfo{};
- depthImageAllocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
- depthImageAllocInfo.requiredFlags = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- // Allocate mem and create image
- vmaCreateImage(m_Allocator, &depthImageCreateInfo, &depthImageAllocInfo, &m_DepthImage.image, &m_DepthImage.allocation, nullptr);
- // Build depth buffer image view
- VkImageViewCreateInfo depthImageViewCreateInfo = vkinit::image_view_ci(m_DepthFormat, m_DepthImage.image, VK_IMAGE_ASPECT_DEPTH_BIT);
- VK_CHECK(vkCreateImageView(m_Device, &depthImageViewCreateInfo, nullptr, &m_DepthImageView));
- m_MainDeletionQueue.deletors.emplace_back([=]() {
- vkDestroyImageView(m_Device, m_DepthImageView, nullptr);
- vmaDestroyImage(m_Allocator, m_DepthImage.image, m_DepthImage.allocation);
- });
- }
- void VulkanEngine::init_commands()
- {
- std::cout << "Initializing Commands\n";
- // Create pool for upload context
- VkCommandPoolCreateInfo uploadCommandPoolInfo{ vkinit::command_pool_ci(m_GraphicsQueueFamily) };
- VK_CHECK(vkCreateCommandPool(m_Device, &uploadCommandPoolInfo, nullptr, &m_UploadContext.command_pool));
- m_MainDeletionQueue.deletors.emplace_back([=]() {
- vkDestroyCommandPool(m_Device, m_UploadContext.command_pool, nullptr);
- });
- // Allocate default command buffer that we will use for immediate commands
- VkCommandBufferAllocateInfo cmdAllocInfo{ vkinit::command_buffer_ai(m_UploadContext.command_pool, 1) };
-
- VkCommandBuffer cmd;
- VK_CHECK(vkAllocateCommandBuffers(m_Device, &cmdAllocInfo, &m_UploadContext.command_buffer));
- // Create command pool for commands submitted to the graphics queue
- // We also want the pool to allow for resetting of individual command buffers
- auto commandPoolInfo = vkinit::command_pool_ci(m_GraphicsQueueFamily, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
-
- for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
- {
- VK_CHECK(vkCreateCommandPool(m_Device, &commandPoolInfo, nullptr, &m_Frames[i].command_pool));
- // Allocate the default command buffer that we will use for rendering
- auto commandBufferInfo = vkinit::command_buffer_ai(m_Frames[i].command_pool, 1);
- VK_CHECK(vkAllocateCommandBuffers(m_Device, &commandBufferInfo, &m_Frames[i].main_command_buffer));
- m_MainDeletionQueue.deletors.emplace_back([=]() {
- vkDestroyCommandPool(m_Device, m_Frames[i].command_pool, nullptr);
- });
- }
- }
- AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage)
- {
- VkBufferCreateInfo info{};
- info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
- info.pNext = nullptr;
-
- info.size = allocSize;
- info.usage = usage;
- VmaAllocationCreateInfo vmaallocInfo{};
- vmaallocInfo.usage = memoryUsage;
- AllocatedBuffer newBuffer{};
- VK_CHECK(vmaCreateBuffer(m_Allocator, &info, &vmaallocInfo,
- &newBuffer.buffer,
- &newBuffer.allocation,
- nullptr));
- return newBuffer;
- }
- void VulkanEngine::init_descriptors()
- {
- // Create descriptor pool, descriptor sets will be allocated from this pool
- std::vector<VkDescriptorPoolSize> sizes
- {
- { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 10 },
- { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 10 },
- { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 10 },
- { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 10}
- };
- VkDescriptorPoolCreateInfo poolInfo{};
- poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
- poolInfo.pNext = nullptr;
- poolInfo.flags = 0;
- poolInfo.maxSets = 10;
- poolInfo.poolSizeCount = static_cast<uint32_t>(sizes.size());
- poolInfo.pPoolSizes = sizes.data();
- vkCreateDescriptorPool(m_Device, &poolInfo, nullptr, &m_DescriptorPool);
- #pragma region DescriptorSet 1 (Global)
- // DescriptorSetLayout defines how the descriptor data is layed out in the descriptor set
- // Bindig 0: Uniform buffer (Camera data)
- VkDescriptorSetLayoutBinding camBufferBinding{ vkinit::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
- VK_SHADER_STAGE_VERTEX_BIT, 0) };
- // Binding 1: Uniform buffer (Scene data)
- VkDescriptorSetLayoutBinding sceneBufferBinding{ vkinit::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
- VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 1) };
- VkDescriptorSetLayoutBinding bindings[] = { camBufferBinding, sceneBufferBinding };
- VkDescriptorSetLayoutCreateInfo camSetInfo{};
- camSetInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
- camSetInfo.pNext = nullptr;
- // We currently only have one binding, the camera buffer
- camSetInfo.bindingCount = 2;
- camSetInfo.pBindings = bindings;
- camSetInfo.flags = 0;
- vkCreateDescriptorSetLayout(m_Device, &camSetInfo, nullptr, &m_GlobalSetLayout);
- #pragma endregion
- #pragma region DescriptorSet 2 (Object)
- VkDescriptorSetLayoutBinding objectBind{vkinit::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
- VK_SHADER_STAGE_VERTEX_BIT, 0) };
- VkDescriptorSetLayoutCreateInfo objectSetInfo{};
- objectSetInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
- objectSetInfo.pNext = nullptr;
- objectSetInfo.bindingCount = 1;
- objectSetInfo.pBindings = &objectBind;
- objectSetInfo.flags = 0;
- vkCreateDescriptorSetLayout(m_Device, &objectSetInfo, nullptr, &m_ObjectSetLayout);
- #pragma endregion
- #pragma region DescriptorSet 3 (Texture)
- VkDescriptorSetLayoutBinding textureBind{vkinit::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0)};
- VkDescriptorSetLayoutCreateInfo texSetInfo{};
- texSetInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
- texSetInfo.pNext = nullptr;
- texSetInfo.bindingCount = 1;
- texSetInfo.flags = 0;
- texSetInfo.pBindings = &textureBind;
- vkCreateDescriptorSetLayout(m_Device, &texSetInfo, nullptr, &m_SingleTextureSetLayout);
- #pragma endregion
- const size_t sceneDataBufferSize = MAX_FRAMES_IN_FLIGHT * pad_uniform_buffer_size(sizeof(GPUSceneData));
- m_SceneDataBuffer = create_buffer(sceneDataBufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU);
-
- const int MAX_OBJECTS = 10'000;
- for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
- {
- // Object data buffer
- m_Frames[i].object_buffer = create_buffer(sizeof(GPUObjectData) * MAX_OBJECTS,
- VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU);
- // Camera data buffer
- m_Frames[i].camera_buffer = create_buffer(sizeof(GPUCameraData), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU);
- // Indirect buffer
- m_Frames[i].indirect_buffer = create_buffer(sizeof(VkDrawIndirectCommand) * 10'000,
- VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU);
- // Allocate one descriptor set for each frame
- VkDescriptorSetAllocateInfo allocInfo{};
- allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
- allocInfo.pNext = nullptr;
- allocInfo.descriptorPool = m_DescriptorPool;
- allocInfo.descriptorSetCount = 1;
- allocInfo.pSetLayouts = &m_GlobalSetLayout;
- vkAllocateDescriptorSets(m_Device, &allocInfo, &m_Frames[i].global_descriptor);
- // Allocate one descriptor set that will point to the object buffer for each frame
- VkDescriptorSetAllocateInfo objectAlloc{};
- objectAlloc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
- objectAlloc.pNext = nullptr;
- objectAlloc.descriptorPool = m_DescriptorPool;
- objectAlloc.descriptorSetCount = 1;
- objectAlloc.pSetLayouts = &m_ObjectSetLayout;
- vkAllocateDescriptorSets(m_Device, &objectAlloc, &m_Frames[i].object_descriptor);
- // Info about the camera buffer we want to point at in the descriptor
- VkDescriptorBufferInfo cameraInfo{};
- cameraInfo.buffer = m_Frames[i].camera_buffer.buffer;
- // Offset into the buffer
- cameraInfo.offset = 0;
- // Size of data to pass to the shader
- cameraInfo.range = sizeof(GPUCameraData);
- VkDescriptorBufferInfo sceneInfo{};
- sceneInfo.buffer = m_SceneDataBuffer.buffer;
- sceneInfo.offset = 0;
- sceneInfo.range = sizeof(GPUSceneData);
- VkDescriptorBufferInfo objectBufferInfo{};
- objectBufferInfo.buffer = m_Frames[i].object_buffer.buffer;
- objectBufferInfo.offset = 0;
- objectBufferInfo.range = sizeof(GPUObjectData) * MAX_OBJECTS;
- VkWriteDescriptorSet cameraWrite{vkinit::write_descriptor_buffer(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
- m_Frames[i].global_descriptor, &cameraInfo, 0)};
- VkWriteDescriptorSet sceneWrite{ vkinit::write_descriptor_buffer(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
- m_Frames[i].global_descriptor, &sceneInfo, 1) };
- VkWriteDescriptorSet objectWrite{ vkinit::write_descriptor_buffer(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
- m_Frames[i].object_descriptor, &objectBufferInfo, 0) };
- VkWriteDescriptorSet setWrites[] = { cameraWrite, sceneWrite, objectWrite };
- vkUpdateDescriptorSets(m_Device, 3, setWrites, 0, nullptr);
- }
- m_MainDeletionQueue.deletors.emplace_back([&]() {
- vkDestroyDescriptorPool(m_Device, m_DescriptorPool, nullptr);
- vkDestroyDescriptorSetLayout(m_Device, m_GlobalSetLayout, nullptr);
- vkDestroyDescriptorSetLayout(m_Device, m_ObjectSetLayout, nullptr);
- // Add buffers to deletion queue
- for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
- {
- vmaDestroyBuffer(m_Allocator, m_Frames[i].camera_buffer.buffer, m_Frames[i].camera_buffer.allocation);
- vmaDestroyBuffer(m_Allocator, m_Frames[i].object_buffer.buffer, m_Frames[i].object_buffer.allocation);
- }
- vmaDestroyBuffer(m_Allocator, m_SceneDataBuffer.buffer, m_SceneDataBuffer.allocation);
- });
- }
- void VulkanEngine::init_def_renderpass()
- {
- std::cout << "Initializing Default Render Pass\n";
- // Description of the image that we will be writing rendering commands to
- VkAttachmentDescription colorAttachment{};
- // format should be the same as the swap chain images
- colorAttachment.format = m_SwapChainImageFormat;
- // MSAA samples, set to 1 (no MSAA) by default
- colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
- // Clear when render pass begins
- colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
- // Keep the attachment stored when render pass ends
- colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
- // Don't care about stencil data
- colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
- colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
- // Image data layout before render pass starts (undefined = don't care)
- colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- // Image data layout after render pass (to change to), set to present by default
- colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
- VkAttachmentReference colorAttachmentRef{};
- // Attachment number will index into the pAttachments array in the parent render pass itself
- colorAttachmentRef.attachment = 0;
- // Optimal layout for writing to the image
- colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
- // Description of the depth image
- VkAttachmentDescription depthAttachment{};
- depthAttachment.flags = 0;
- depthAttachment.format = m_DepthFormat;
- depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
- depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
- depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
- depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
- depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
- depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
- VkAttachmentReference depthAttachmentRef{};
- depthAttachmentRef.attachment = 1;
- depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
- // Create one sub pass (minimum one sub pass required)
- VkSubpassDescription subpass{};
- subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpass.colorAttachmentCount = 1;
- subpass.pColorAttachments = &colorAttachmentRef;
- // Connect depth attachment to subpass
- subpass.pDepthStencilAttachment = &depthAttachmentRef;
- VkRenderPassCreateInfo renderPassInfo{};
- renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
- VkAttachmentDescription attachments[2] = { colorAttachment, depthAttachment };
- // Connect the color attachment description to the info
- renderPassInfo.attachmentCount = 2;
- renderPassInfo.pAttachments = &attachments[0];
- // Connect the subpass(es) to the info
- renderPassInfo.subpassCount = 1;
- renderPassInfo.pSubpasses = &subpass;
- // These dependencies tell Vulkan that the attachment cannot be used before the previous renderpasses have finished using it
- VkSubpassDependency colorDependency{};
- colorDependency.srcSubpass = VK_SUBPASS_EXTERNAL;
- colorDependency.dstSubpass = 0;
-
- colorDependency.srcAccessMask = 0;
- colorDependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
- colorDependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
- colorDependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
- VkSubpassDependency depthDependency{};
- depthDependency.srcSubpass = VK_SUBPASS_EXTERNAL;
- depthDependency.dstSubpass = 0;
- depthDependency.srcAccessMask = 0;
- depthDependency.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
- depthDependency.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
- depthDependency.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
- VkSubpassDependency dependencies[2] = { colorDependency, depthDependency };
- renderPassInfo.dependencyCount = 2;
- renderPassInfo.pDependencies = &dependencies[0];
- VK_CHECK(vkCreateRenderPass(m_Device, &renderPassInfo, nullptr, &m_RenderPass));
- m_MainDeletionQueue.deletors.emplace_back([=]() {
- vkDestroyRenderPass(m_Device, m_RenderPass, nullptr);
- });
- }
- void VulkanEngine::init_framebuffers()
- {
- std::cout << "Initializing Framebuffers\n";
- // Create the framebuffers for the swap chain images.
- // This will connect the render pass to the images for rendering
- VkFramebufferCreateInfo fbInfo{};
- fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
- // Connect the render pass to the framebuffer
- fbInfo.renderPass = m_RenderPass;
- fbInfo.attachmentCount = 1;
- fbInfo.width = m_WindowExtent.width;
- fbInfo.height = m_WindowExtent.height;
- fbInfo.layers = 1;
- // Get the number of swap chain image views
- const uint32_t swapChainImageViewCount = static_cast<uint32_t>(m_SwapChainImageViews.size());
- m_Framebuffers = std::vector<VkFramebuffer>(swapChainImageViewCount);
- // Create the framebuffers for each image view
- for (uint32_t i = 0; i < swapChainImageViewCount; i++)
- {
- VkImageView attachments[2] = { m_SwapChainImageViews[i], m_DepthImageView };
- fbInfo.attachmentCount = 2;
- fbInfo.pAttachments = attachments;
- VK_CHECK(vkCreateFramebuffer(m_Device, &fbInfo, nullptr, &m_Framebuffers[i]));
- m_MainDeletionQueue.deletors.emplace_back([=]() {
- vkDestroyFramebuffer(m_Device, m_Framebuffers[i], nullptr);
- vkDestroyImageView(m_Device, m_SwapChainImageViews[i], nullptr);
- });
- }
- }
- FrameData& VulkanEngine::get_current_frame()
- {
- return m_Frames[m_FrameNumber % MAX_FRAMES_IN_FLIGHT];
- }
- void VulkanEngine::init_sync_structures()
- {
- std::cout << "Initializing Sync Structures\n";
- // Upload fence
- VkFenceCreateInfo uploadFenceCreateInfo{ vkinit::fence_ci() };
- VK_CHECK(vkCreateFence(m_Device, &uploadFenceCreateInfo, nullptr, &m_UploadContext.upload_fence));
- m_MainDeletionQueue.deletors.emplace_back([=]() {
- vkDestroyFence(m_Device, m_UploadContext.upload_fence, nullptr);
- });
- // Render fence
- VkFenceCreateInfo fenceCreateInfo{ vkinit::fence_ci(VK_FENCE_CREATE_SIGNALED_BIT) };
- // Sempahore needs no flags
- VkSemaphoreCreateInfo semaphoreCreateInfo{ vkinit::semaphore_ci() };
-
- for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
- {
- VK_CHECK(vkCreateFence(m_Device, &fenceCreateInfo, nullptr, &m_Frames[i].render_fence));
- m_MainDeletionQueue.deletors.emplace_back([=]() {
- vkDestroyFence(m_Device, m_Frames[i].render_fence, nullptr);
- });
- VK_CHECK(vkCreateSemaphore(m_Device, &semaphoreCreateInfo, nullptr, &m_Frames[i].present_semaphore));
- VK_CHECK(vkCreateSemaphore(m_Device, &semaphoreCreateInfo, nullptr, &m_Frames[i].render_semaphore));
- m_MainDeletionQueue.deletors.emplace_back([=]()
- {
- vkDestroySemaphore(m_Device, m_Frames[i].present_semaphore, nullptr);
- vkDestroySemaphore(m_Device, m_Frames[i].render_semaphore, nullptr);
- });
- }
- }
- bool VulkanEngine::load_shader_module(const char* filePath, VkShaderModule* outShaderModule)
- {
- // Open the file stream, seek to the end of the file
- std::ifstream file(filePath, std::ios::ate | std::ios::binary);
- if (!file.is_open())
- return false;
- // Get the file size
- size_t fileSize = static_cast<size_t>(file.tellg());
- // SPRIV expects the buffer to be on uint32_t, so make sure to reserve a buffer big enough for that
- std::vector<uint32_t> buffer(fileSize / sizeof(uint32_t));
- // Put cursor at the beginning of the file
- file.seekg(0);
- // Load the entire file into the buffer
- file.read(reinterpret_cast<char*>(buffer.data()), fileSize);
- // Close the file stream
- file.close();
- // Create a new shader module, using the buffer we loaded
- VkShaderModuleCreateInfo createInfo{};
- createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
- createInfo.pNext = nullptr;
- // Code size has to be in bytes, so multiply the int in the buffer by the size of an int
- createInfo.codeSize = buffer.size() * sizeof(uint32_t);
- createInfo.pCode = buffer.data();
- // Validate creation
- VkShaderModule shaderModule;
- if (vkCreateShaderModule(m_Device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS)
- return false;
- // Set the outShaderModule to the newly created shader module
- *outShaderModule = shaderModule;
- return true;
- }
- void VulkanEngine::init_pipelines()
- {
- std::cout << "Initializing Pipelines\n";
- VkShaderModule fragShader;
- if (!load_shader_module("shaders/PosNormCol.frag.spv", &fragShader))
- std::cout << "Error building the triangle fragment shader module\n";
- else
- std::cout << "Successfully built the fragment shader module\n";
- VkShaderModule vertShader;
- if (!load_shader_module("shaders/PosNormCol.vert.spv", &vertShader))
- std::cout << "Error building the triangle vertex shader module\n";
- else
- std::cout << "Successfully built the vertex shader module\n";
- // Build pipeline layout that controls inputs/outputs of the shader
- VkPipelineLayoutCreateInfo pipelineLayoutInfo{ vkinit::pipeline_layout_ci() };
- VkDescriptorSetLayout setLayouts[] = { m_GlobalSetLayout, m_ObjectSetLayout, m_SingleTextureSetLayout };
- pipelineLayoutInfo.setLayoutCount = 3;
- pipelineLayoutInfo.pSetLayouts = setLayouts;
- VK_CHECK(vkCreatePipelineLayout(m_Device, &pipelineLayoutInfo, nullptr, &m_MeshPipelineLayout));
- // Build the stage-create-info for both vertex and fragment stages.
- // This lets the pipeline know the shader modules per stage
- PipelineBuilder pipelineBuilder;
- pipelineBuilder.m_ShaderStages.push_back(
- vkinit::pipeline_shader_stage_ci(VK_SHADER_STAGE_VERTEX_BIT, vertShader)
- );
- pipelineBuilder.m_ShaderStages.push_back(
- vkinit::pipeline_shader_stage_ci(VK_SHADER_STAGE_FRAGMENT_BIT, fragShader)
- );
- // Vertex input controls how to read vertices from vertex buffers
- pipelineBuilder.m_VertexInputInfo = vkinit::vertex_input_state_ci();
- VertexInputDescription vertexDesc = Vertex::get_vert_desc();
- pipelineBuilder.m_VertexInputInfo.pVertexAttributeDescriptions = vertexDesc.attributes.data();
- pipelineBuilder.m_VertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexDesc.attributes.size());
- pipelineBuilder.m_VertexInputInfo.pVertexBindingDescriptions = vertexDesc.bindings.data();
- pipelineBuilder.m_VertexInputInfo.vertexBindingDescriptionCount = static_cast<uint32_t>(vertexDesc.bindings.size());
- // Set input assembly state, which controls primitive topology
- pipelineBuilder.m_InputAssembly = vkinit::input_assembly_ci(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
- // Build the viewport and scissor from the swapchain extents
- pipelineBuilder.m_Viewport.x = 0.0f;
- pipelineBuilder.m_Viewport.y = 0.0f;
- pipelineBuilder.m_Viewport.width = static_cast<float>(m_WindowExtent.width);
- pipelineBuilder.m_Viewport.height = static_cast<float>(m_WindowExtent.height);
- pipelineBuilder.m_Viewport.minDepth = 0.0f;
- pipelineBuilder.m_Viewport.maxDepth = 1.0f;
- pipelineBuilder.m_Scissor.offset = { 0, 0 };
- pipelineBuilder.m_Scissor.extent = m_WindowExtent;
- // Build rasterizer
- pipelineBuilder.m_Rasterizer = vkinit::rasterization_state_ci(VK_POLYGON_MODE_FILL);
- // Build multisampling
- pipelineBuilder.m_Multisampling = vkinit::multisample_state_ci();
- // Build color blend attachment with no blending and writing to RGBA
- pipelineBuilder.m_ColorBlendAttachment = vkinit::color_blend_attachment_state();
- // Use the triangle layout
- pipelineBuilder.m_PipelineLayout = m_MeshPipelineLayout;
- // Depth testing
- pipelineBuilder.m_DepthStencil = vkinit::depth_stencil_ci(true, true, VK_COMPARE_OP_LESS_OR_EQUAL);
- // Build the pipeline
- m_MeshPipeline = pipelineBuilder.build_pipeline(m_Device, m_RenderPass);
- create_material("defaultmesh", m_MeshPipeline, m_MeshPipelineLayout);
- vkDestroyShaderModule(m_Device, vertShader, nullptr);
- vkDestroyShaderModule(m_Device, fragShader, nullptr);
- m_MainDeletionQueue.deletors.emplace_back([=]() {
- vkDestroyPipeline(m_Device, m_MeshPipeline, nullptr);
- vkDestroyPipelineLayout(m_Device, m_MeshPipelineLayout, nullptr);
- });
- }
- void VulkanEngine::load_meshes()
- {
- m_TeapotMesh.load_from_obj("../../../../assets/teapot.obj");
- upload_mesh(m_TeapotMesh);
- m_Meshes["Teapot"] = m_TeapotMesh;
- //Mesh lost_empire{};
- //lost_empire.load_from_obj("../../../../assets/lost_empire.obj");
- //upload_mesh(lost_empire);
- //m_Meshes["empire"] = lost_empire;
- }
- void VulkanEngine::upload_mesh(Mesh& mesh)
- {
- const size_t bufferSize{ mesh.vertices.size() * sizeof(Vertex) };
- // Allocate CPU side vertex buffer
- VkBufferCreateInfo stagingBufferInfo{};
- stagingBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
- stagingBufferInfo.pNext = nullptr;
- // Total size, in bytes, of the buffer
- // Should hold all vertices
- stagingBufferInfo.size = bufferSize;
- stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
- // This data should be on CPU RAM
- VmaAllocationCreateInfo vmaallocInfo{};
- vmaallocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
- AllocatedBuffer stagingBuffer;
- VK_CHECK(vmaCreateBuffer(m_Allocator, &stagingBufferInfo, &vmaallocInfo,
- &stagingBuffer.buffer,
- &stagingBuffer.allocation,
- nullptr));
- // Copy vertex data into buffer
- void* data;
- vmaMapMemory(m_Allocator, stagingBuffer.allocation, &data);
- memcpy(data, mesh.vertices.data(), bufferSize);
- vmaUnmapMemory(m_Allocator, stagingBuffer.allocation);
- // Allocate GPU side vertex buffer
- VkBufferCreateInfo vertexBufferInfo{};
- vertexBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
- vertexBufferInfo.pNext = nullptr;
- vertexBufferInfo.size = bufferSize;
- vertexBufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
- vmaallocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
- VK_CHECK(vmaCreateBuffer(m_Allocator, &vertexBufferInfo, &vmaallocInfo,
- &mesh.vertexBuffer.buffer,
- &mesh.vertexBuffer.allocation,
- nullptr));
- // Copy data from CPU to GPU
- immediate_submit([=](VkCommandBuffer cmd) {
- VkBufferCopy copy;
- copy.dstOffset = 0;
- copy.srcOffset = 0;
- copy.size = bufferSize;
- vkCmdCopyBuffer(cmd, stagingBuffer.buffer, mesh.vertexBuffer.buffer, 1, ©);
- });
- m_MainDeletionQueue.deletors.emplace_back([=]() {
- vmaDestroyBuffer(m_Allocator, mesh.vertexBuffer.buffer, mesh.vertexBuffer.allocation);
- });
- vmaDestroyBuffer(m_Allocator, stagingBuffer.buffer, stagingBuffer.allocation);
- }
- size_t VulkanEngine::pad_uniform_buffer_size(size_t originalSize)
- {
- size_t minUboAlignment = m_DeviceProperties.limits.minUniformBufferOffsetAlignment;
- size_t alignedSize = originalSize;
- if (minUboAlignment > 0)
- {
- alignedSize = (alignedSize + minUboAlignment - 1) & ~(minUboAlignment - 1);
- }
- return alignedSize;
- }
- Material* VulkanEngine::create_material(const std::string& name, VkPipeline pipeline, VkPipelineLayout layout)
- {
- Material mat;
- mat.pipeline = pipeline;
- mat.pipeline_layout = layout;
- m_Materials[name] = mat;
- return &m_Materials[name];
- }
- Material* VulkanEngine::get_material(const std::string& name)
- {
- auto it = m_Materials.find(name);
- if (it == end(m_Materials))
- return nullptr;
- else
- return &(*it).second;
- }
- Mesh* VulkanEngine::get_mesh(const std::string& name)
- {
- auto it = m_Meshes.find(name);
- if (it == end(m_Meshes))
- return nullptr;
- else
- return &(*it).second;
- }
- void Coral3D::VulkanEngine::load_images()
- {
- //Texture lost_empire;
- //vkutil::load_image_from_file(*this, "../../../../assets/lost_empire-RGBA.png", lost_empire.image);
- //VkImageViewCreateInfo img_info = vkinit::image_view_ci(VK_FORMAT_R8G8B8A8_SRGB, lost_empire.image.image, VK_IMAGE_ASPECT_COLOR_BIT);
- //vkCreateImageView(m_Device, &img_info, nullptr, &lost_empire.image_view);
- //m_LoadedTextures["empire_diffuse"] = lost_empire;
- Texture uv_checker;
- vkutil::load_image_from_file(*this, "../../../../assets/uv_checker.jpg", uv_checker.image);
- VkImageViewCreateInfo img_info = vkinit::image_view_ci(VK_FORMAT_R8G8B8A8_SRGB, uv_checker.image.image, VK_IMAGE_ASPECT_COLOR_BIT);
- vkCreateImageView(m_Device, &img_info, nullptr, &uv_checker.image_view);
- m_LoadedTextures["checker_diffuse"] = uv_checker;
- }
- void VulkanEngine::draw_objects(VkCommandBuffer cmdBuffer, RenderObject* first, int count)
- {
- glm::vec3 camPos{0.f, -30.f, -1310.f};
- // glm::vec3 camPos{0.f, -5.f, 0.f};
- // Camera matrices
- glm::mat4 trans{glm::translate(glm::mat4{1.f}, camPos)};
- glm::mat4 rot{glm::rotate(glm::radians(25.f), glm::vec3{1, 0, 0})};
- glm::mat4 view = rot * trans;
- glm::mat4 proj{glm::perspective(glm::radians(70.f), (float)m_WindowExtent.width / (float)m_WindowExtent.height, 0.1f, 10000.f)};
- proj[1][1] *= -1;
- GPUCameraData camData;
- camData.proj = proj;
- camData.view = view;
- camData.view_proj = proj * view;
- camData.view_inv = glm::inverse(view);
- // Camera data
- void* data;
- vmaMapMemory(m_Allocator, get_current_frame().camera_buffer.allocation, &data);
- memcpy(data, &camData, sizeof(GPUCameraData));
- vmaUnmapMemory(m_Allocator, get_current_frame().camera_buffer.allocation);
- // Scene data
- char* sceneData;
- vmaMapMemory(m_Allocator, m_SceneDataBuffer.allocation, (void**)&sceneData);
-
- int frameIndex = m_FrameNumber % MAX_FRAMES_IN_FLIGHT;
- sceneData += pad_uniform_buffer_size(sizeof(GPUSceneData)) * frameIndex;
-
- memcpy(sceneData, &m_GPUSceneData, sizeof(GPUSceneData));
- vmaUnmapMemory(m_Allocator, m_SceneDataBuffer.allocation);
- // Object data
- void* objectData;
- vmaMapMemory(m_Allocator, get_current_frame().object_buffer.allocation, &objectData);
- GPUObjectData* objectSSBO = (GPUObjectData*)objectData;
- for (int i = 0; i < count; i++)
- {
- RenderObject& object = first[i];
- objectSSBO[i].world = object.transform *
- glm::rotate(glm::mat4{1.f}, glm::radians(m_FrameNumber * 0.4f), glm::vec3(0, 0, 1));
- }
- vmaUnmapMemory(m_Allocator, get_current_frame().object_buffer.allocation);
- auto draws = batch_draws(m_Renderables);
- void* drawData;
- vmaMapMemory(m_Allocator, get_current_frame().indirect_buffer.allocation, &drawData);
- VkDrawIndirectCommand* drawCommands = reinterpret_cast<VkDrawIndirectCommand*>(drawData);
- for (int i = 0; i < count; i++)
- {
- RenderObject& object{ first[i] };
- drawCommands[i].vertexCount = object.mesh->vertices.size();
- drawCommands[i].instanceCount = 1;
- drawCommands[i].firstVertex = 0;
- drawCommands[i].firstInstance = i;
- }
- vmaUnmapMemory(m_Allocator, get_current_frame().indirect_buffer.allocation);
- for (IndirectBatch& draw : draws)
- {
- // Bind pipeline
- vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, draw.material->pipeline);
- #pragma region Bind Descriptors
- uint32_t uniformOffset = pad_uniform_buffer_size(sizeof(GPUSceneData)) * frameIndex;
- // Bind global descriptor
- vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
- draw.material->pipeline_layout, 0, 1, &get_current_frame().global_descriptor, 1, &uniformOffset);
- // Bind object descriptor
- vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
- draw.material->pipeline_layout, 1, 1, &get_current_frame().object_descriptor, 0, nullptr);
- // Bind texture descriptor (if applicable)
- if (draw.material->texture_set != VK_NULL_HANDLE)
- vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, draw.material->pipeline_layout, 2, 1, &draw.material->texture_set, 0, nullptr);
- #pragma endregion
- // Bind mesh
- VkDeviceSize offset{ 0 };
- vkCmdBindVertexBuffers(cmdBuffer, 0, 1, &draw.mesh->vertexBuffer.buffer, &offset);
- uint32_t draw_stride = sizeof(VkDrawIndirectCommand);
- VkDeviceSize indirectOffset = draw.first * draw_stride;
-
- vkCmdDrawIndirect(cmdBuffer, get_current_frame().indirect_buffer.buffer, indirectOffset, draw.count, draw_stride);
- }
- //Mesh* lastMesh = nullptr;
- //Material* lastMaterial = nullptr;
- //for (int i = 0; i < count; i++)
- //{
- // RenderObject& object = first[i];
- // // Only bind pipeline if it doesn't match with already bound one
- // if (object.material != lastMaterial)
- // {
- // vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, object.material->pipeline);
- // lastMaterial = object.material;
- // // Offset in scene buffer to access current frame data
- // uint32_t uniformOffset = pad_uniform_buffer_size(sizeof(GPUSceneData)) * frameIndex;
- // vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, object.material->pipeline_layout, 0, 1, &get_current_frame().global_descriptor, 1, &uniformOffset);
- //
- // // Object data descriptor
- // vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, object.material->pipeline_layout, 1, 1, &get_current_frame().object_descriptor, 0, nullptr);
- //
- // if (object.material->texture_set != VK_NULL_HANDLE)
- // vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, object.material->pipeline_layout, 2, 1, &object.material->texture_set, 0, nullptr);
- // }
- // // Only bind mesh if it's different from last bind
- // if (object.mesh != lastMesh)
- // {
- // // Bind the mesh vertex buffer with offset 0
- // VkDeviceSize offset{ 0 };
- // vkCmdBindVertexBuffers(cmdBuffer, 0, 1, &object.mesh->vertexBuffer.buffer, &offset);
- // lastMesh = object.mesh;
- // }
- // vkCmdDraw(cmdBuffer, object.mesh->vertices.size(), 1, 0, i);
- //}
- }
- void VulkanEngine::init_scene()
- {
- Material* defaultMaterial = get_material("defaultmesh");
- VkSamplerCreateInfo samplerInfo{ vkinit::sampler_ci(VK_FILTER_NEAREST) };
- VkSampler nearesetSampler;
- vkCreateSampler(m_Device, &samplerInfo, nullptr, &nearesetSampler);
- // Allocate descriptor set for texture to use on the material
- VkDescriptorSetAllocateInfo allocInfo{};
- allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
- allocInfo.pNext = nullptr;
- allocInfo.descriptorPool = m_DescriptorPool;
- allocInfo.descriptorSetCount = 1;
- allocInfo.pSetLayouts = &m_SingleTextureSetLayout;
- vkAllocateDescriptorSets(m_Device, &allocInfo, &defaultMaterial->texture_set);
- // Make descriptor set point to texture
- VkDescriptorImageInfo imageBufferInfo{};
- imageBufferInfo.sampler = nearesetSampler;
- imageBufferInfo.imageView = m_LoadedTextures["checker_diffuse"].image_view;
- imageBufferInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
- VkWriteDescriptorSet texture{ vkinit::write_descriptor_image(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, defaultMaterial->texture_set, &imageBufferInfo, 0) };
- vkUpdateDescriptorSets(m_Device, 1, &texture, 0, nullptr);
- //RenderObject map;
- //map.mesh = get_mesh("empire");
- //map.material = get_material("defaultmesh");
- //map.transform = glm::translate(glm::vec3{5, -10, 0});
- //std::cout << std::setprecision(10) << "Number of triangles: " << map.mesh->vertices.size() << "\n";
- //m_Renderables.emplace_back(map);
- Mesh* teapotMesh = get_mesh("Teapot");
- const int vertCount = teapotMesh->vertices.size();
-
- constexpr int numObjects = 2;
- for (int x = -numObjects * 0.5f; x < numObjects * 0.5f; x++)
- {
- for (int y = -numObjects * 0.5f; y < numObjects * 0.5f; y++)
- {
- RenderObject teapot;
- teapot.mesh = teapotMesh;
- teapot.material = defaultMaterial;
- teapot.transform = glm::translate(glm::mat4{1.0f}, glm::vec3(x * 40.f, 0, y * 40.f)) * glm::rotate(glm::radians(-90.f), glm::vec3{1, 0, 0});
- m_Renderables.push_back(teapot);
- }
- }
- // Scene data
- m_GPUSceneData.ambient_color = { 0.625f, 0.0f, 0.24f, 0.1f };
- m_GPUSceneData.light_direction = { -0.577f, -0.577f, 0.577f, 1.f };
- std::cout << "Number of renderables: " << m_Renderables.size() << "\n";
- std::cout << std::setprecision(0) << "Number of triangles: " << (m_Renderables.size() * vertCount) / 3 << "\n";
- }
|