// ============================================= // Aster: box.cpp // Copyright (c) 2020-2024 Anish Bhobe // ============================================= #include "aster/aster.h" #include "aster/core/buffer.h" #include "aster/core/constants.h" #include "aster/core/context.h" #include "aster/core/device.h" #include "aster/core/image.h" #include "aster/core/physical_device.h" #include "aster/core/pipeline.h" #include "aster/core/swapchain.h" #include "aster/core/window.h" #include "helpers.h" #define STB_IMAGE_IMPLEMENTATION #include "aster/systems/buffer_manager.h" #include "aster/systems/image_manager.h" #include "aster/systems/commit_manager.h" #include "frame.h" #include "stb_image.h" #include "aster/systems/image_view_manager.h" #include "aster/systems/resource_manager.h" #include constexpr u32 MAX_FRAMES_IN_FLIGHT = 3; constexpr auto VERTEX_SHADER_FILE = "shader/box.vs.hlsl.spv"; constexpr auto FRAGMENT_SHADER_FILE = "shader/box.ps.hlsl.spv"; struct ImageFile { void *m_Data = nullptr; u32 m_Width = 0; u32 m_Height = 0; u32 m_NumChannels = 0; bool Load(cstr fileName); [[nodiscard]] usize GetSize() const; ~ImageFile(); }; bool ImageFile::Load(cstr fileName) { int width, height, nrChannels; m_Data = stbi_load(fileName, &width, &height, &nrChannels, 4); ERROR_IF(!m_Data, "Could not load {}", fileName); if (!m_Data) { return false; } m_Width = width; m_Height = height; m_NumChannels = 4; return true; } usize ImageFile::GetSize() const { return Cast(m_Width) * m_Height * m_NumChannels; } ImageFile::~ImageFile() { stbi_image_free(m_Data); m_Data = nullptr; } vk::ShaderModule CreateShader(const Device *device, cstr shaderFile); Pipeline CreatePipeline(const systems::CommitManager *resourceManager, const Swapchain *swapchain); struct Vertex { vec3 m_Position; f32 m_PositionW = 1.0; vec2 m_TexCoord0; vec2 m_Padding0_ = {0.0f, 0.0f}; }; struct Camera { mat4 m_Model; mat4 m_View; mat4 m_Perspective; }; int main(int, char **) { MIN_LOG_LEVEL(Logger::LogType::eInfo); Window window = {"Box (Aster)", {640, 480}}; Context context = {"Box", VERSION}; Surface surface = {&context, &window, "Primary"}; PhysicalDevices physicalDevices = {&surface, &context}; PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices); INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data()); Features enabledDeviceFeatures = { .m_Vulkan10Features = {.samplerAnisotropy = true}, .m_Vulkan12Features = { .descriptorIndexing = true, .shaderSampledImageArrayNonUniformIndexing = true, .shaderStorageBufferArrayNonUniformIndexing = true, .shaderStorageImageArrayNonUniformIndexing = true, .descriptorBindingUniformBufferUpdateAfterBind = true, // Not related to Bindless .descriptorBindingSampledImageUpdateAfterBind = true, .descriptorBindingStorageImageUpdateAfterBind = true, .descriptorBindingStorageBufferUpdateAfterBind = true, .descriptorBindingPartiallyBound = true, .runtimeDescriptorArray = true, .bufferDeviceAddress = true, .bufferDeviceAddressCaptureReplay = true, }, .m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true}, }; QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse); Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"}; vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0); Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"}; systems::ResourceManager resourceManager{&device, 12, 12, 1, 12}; systems::CommitManager commitManager{&device, 12, 12, 12, resourceManager.Samplers().CreateSampler({})}; Pipeline pipeline = CreatePipeline(&commitManager, &swapchain); Camera camera = { .m_Model = {1.0f}, .m_View = glm::lookAt(vec3(0.0f, 2.0f, 2.0f), vec3(0.0f), vec3(0.0f, 1.0f, 0.0f)), .m_Perspective = glm::perspective( 70_deg, Cast(swapchain.m_Extent.width) / Cast(swapchain.m_Extent.height), 0.1f, 100.0f), }; vk::CommandPool copyPool; vk::CommandBuffer copyBuffer; { vk::CommandPoolCreateInfo poolCreateInfo = { .flags = vk::CommandPoolCreateFlagBits::eTransient, .queueFamilyIndex = queueAllocation.m_Family, }; AbortIfFailedM(device.m_Device.createCommandPool(&poolCreateInfo, nullptr, ©Pool), "Copy command pool creation failed."); vk::CommandBufferAllocateInfo bufferAllocateInfo = { .commandPool = copyPool, .level = vk::CommandBufferLevel::ePrimary, .commandBufferCount = 1, }; AbortIfFailedM(device.m_Device.allocateCommandBuffers(&bufferAllocateInfo, ©Buffer), "Copy command buffer allocation failed."); } eastl::array vertices = { Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)}, Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)}, Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)}, Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)}, Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)}, Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)}, Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)}, Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)}, Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)}, Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)}, Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)}, Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)}, Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)}, Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)}, Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)}, Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)}, Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)}, Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)}, Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)}, Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)}, Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)}, Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)}, Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)}, Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)}, Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)}, Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)}, Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)}, Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)}, Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)}, Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)}, Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)}, Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)}, Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)}, Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)}, Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)}, Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)}, }; ImageFile imageFile; bool loaded = imageFile.Load("image/container.jpg"); assert(loaded); INFO("Image {}x{} : {} channels", imageFile.m_Width, imageFile.m_Height, imageFile.m_NumChannels); auto vbo = resourceManager.Buffers().CreateStorageBuffer(vertices.size() * sizeof vertices[0], "Vertex Buffer"); vbo->Write(0, vertices.size() * sizeof vertices[0], vertices.data()); auto crate = resourceManager.CombinedImageViews() .CreateTexture2D({ .m_Format = vk::Format::eR8G8B8A8Srgb, .m_Extent = {imageFile.m_Width, imageFile.m_Height}, .m_Name = "Crate Texture", }); { StagingBuffer imageStaging; imageStaging.Init(&device, imageFile.GetSize(), "Image Staging"); imageStaging.Write(0, imageFile.GetSize(), imageFile.m_Data); vk::ImageMemoryBarrier2 imageReadyToWrite = { .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, .srcAccessMask = vk::AccessFlagBits2::eNone, .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, .oldLayout = vk::ImageLayout::eUndefined, .newLayout = vk::ImageLayout::eTransferDstOptimal, .srcQueueFamilyIndex = queueAllocation.m_Family, .dstQueueFamilyIndex = queueAllocation.m_Family, .image = crate->GetImage(), .subresourceRange = { .aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }, }; vk::DependencyInfo imageReadyToWriteDependency = { .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &imageReadyToWrite, }; vk::ImageMemoryBarrier2 imageReadyToRead = { .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, .dstStageMask = vk::PipelineStageFlagBits2::eFragmentShader, .dstAccessMask = vk::AccessFlagBits2::eShaderRead, .oldLayout = vk::ImageLayout::eTransferDstOptimal, .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, .srcQueueFamilyIndex = queueAllocation.m_Family, .dstQueueFamilyIndex = queueAllocation.m_Family, .image = crate->GetImage(), .subresourceRange = { .aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }, }; vk::DependencyInfo imageReadyToReadDependency = { .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &imageReadyToRead, }; vk::Fence fence; vk::FenceCreateInfo fenceCreateInfo = {}; AbortIfFailed(device.m_Device.createFence(&fenceCreateInfo, nullptr, &fence)); vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; AbortIfFailed(copyBuffer.begin(&beginInfo)); copyBuffer.pipelineBarrier2(&imageReadyToWriteDependency); vk::BufferImageCopy imageCopy = { .bufferOffset = 0, .bufferRowLength = imageFile.m_Width, .bufferImageHeight = imageFile.m_Height, .imageSubresource = { .aspectMask = vk::ImageAspectFlagBits::eColor, .mipLevel = 0, .baseArrayLayer = 0, .layerCount = 1, }, .imageOffset = {}, .imageExtent = {imageFile.m_Width, imageFile.m_Height, 1}, }; copyBuffer.copyBufferToImage(imageStaging.m_Buffer, crate->GetImage(), vk::ImageLayout::eTransferDstOptimal, 1, &imageCopy); copyBuffer.pipelineBarrier2(&imageReadyToReadDependency); AbortIfFailed(copyBuffer.end()); vk::SubmitInfo submitInfo = { .commandBufferCount = 1, .pCommandBuffers = ©Buffer, }; AbortIfFailed(commandQueue.submit(1, &submitInfo, fence)); INFO("Submit copy"); AbortIfFailed(device.m_Device.waitForFences(1, &fence, true, MaxValue)); INFO("Fence wait"); AbortIfFailedM(device.m_Device.resetCommandPool(copyPool, {}), "Couldn't reset command pool."); device.m_Device.destroy(fence, nullptr); imageStaging.Destroy(); } auto ubo = resourceManager.Buffers().CreateStorageBuffer(sizeof camera, "Camera UBO"); ubo->Write(0, sizeof camera, &camera); // Persistent variables vk::Viewport viewport = { .x = 0, .y = Cast(swapchain.m_Extent.height), .width = Cast(swapchain.m_Extent.width), .height = -Cast(swapchain.m_Extent.height), .minDepth = 0.0, .maxDepth = 1.0, }; vk::Rect2D scissor = { .offset = {0, 0}, .extent = swapchain.m_Extent, }; auto resizeViewportScissor = [&viewport, &scissor](vk::Extent2D extent) { viewport.y = Cast(extent.height); viewport.width = Cast(extent.width); viewport.height = -Cast(extent.height); scissor.extent = extent; }; swapchain.RegisterResizeCallback(resizeViewportScissor); vk::ImageSubresourceRange subresourceRange = { .aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }; vk::ImageMemoryBarrier2 topOfThePipeBarrier = { // For Color Attachment output ref: // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/7193#issuecomment-1875960974 .srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, .srcAccessMask = vk::AccessFlagBits2::eNone, .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, .oldLayout = vk::ImageLayout::eUndefined, .newLayout = vk::ImageLayout::eColorAttachmentOptimal, .srcQueueFamilyIndex = queueAllocation.m_Family, .dstQueueFamilyIndex = queueAllocation.m_Family, .subresourceRange = subresourceRange, }; vk::DependencyInfo topOfThePipeDependency = { .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &topOfThePipeBarrier, }; vk::ImageMemoryBarrier2 renderToPresentBarrier = { .srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, .srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, .dstStageMask = vk::PipelineStageFlagBits2::eBottomOfPipe, .dstAccessMask = vk::AccessFlagBits2::eNone, .oldLayout = vk::ImageLayout::eColorAttachmentOptimal, .newLayout = vk::ImageLayout::ePresentSrcKHR, .srcQueueFamilyIndex = queueAllocation.m_Family, .dstQueueFamilyIndex = queueAllocation.m_Family, .subresourceRange = subresourceRange, }; vk::DependencyInfo renderToPresentDependency = { .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &renderToPresentBarrier, }; FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT}; eastl::fixed_vector, MAX_FRAMES_IN_FLIGHT> depthImages; auto initDepthImages = [&depthImages, &frameManager, &resourceManager](const vk::Extent2D extent) { for (u32 i = 0; i < frameManager.m_FramesInFlight; ++i) { depthImages.push_back( resourceManager.CombinedImageViews().CreateDepthStencilImage({.m_Extent = extent, .m_Name = "Depth"})); } }; initDepthImages(swapchain.m_Extent); auto recreateDepthBuffers = [&depthImages, &initDepthImages](const vk::Extent2D extent) { depthImages.clear(); initDepthImages(extent); }; swapchain.RegisterResizeCallback(recreateDepthBuffers); struct PCB { systems::ResId m_Camera; systems::ResId m_VertexBuffer; systems::ResId m_Texture; }; PCB pcb = { .m_Camera = commitManager.CommitBuffer(ubo), .m_VertexBuffer = commitManager.CommitBuffer(vbo), .m_Texture = commitManager.CommitTexture(crate), }; Time::Init(); INFO("Starting loop"); while (window.Poll()) { Time::Update(); commitManager.Update(); camera.m_Model *= rotate(mat4{1.0f}, Cast(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f)); ubo->Write(0, sizeof camera, &camera); Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &surface, window.GetSize()); u32 imageIndex = currentFrame->m_ImageIdx; vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex]; vk::Image currentImage = swapchain.m_Images[imageIndex]; vk::CommandBuffer cmd = currentFrame->m_CommandBuffer; vk::ImageView currentDepthImageView = depthImages[currentFrame->m_FrameIdx]->m_View; topOfThePipeBarrier.image = currentImage; renderToPresentBarrier.image = currentImage; vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; AbortIfFailed(cmd.begin(&beginInfo)); cmd.pipelineBarrier2(&topOfThePipeDependency); // Render eastl::array attachmentInfos = { vk::RenderingAttachmentInfo{ .imageView = currentImageView, .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, .resolveMode = vk::ResolveModeFlagBits::eNone, .loadOp = vk::AttachmentLoadOp::eClear, .storeOp = vk::AttachmentStoreOp::eStore, .clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 0.0f}, }, }; vk::RenderingAttachmentInfo depthAttachment = { .imageView = currentDepthImageView, .imageLayout = vk::ImageLayout::eDepthAttachmentOptimal, .resolveMode = vk::ResolveModeFlagBits::eNone, .loadOp = vk::AttachmentLoadOp::eClear, .storeOp = vk::AttachmentStoreOp::eDontCare, .clearValue = vk::ClearDepthStencilValue{.depth = 1.0f, .stencil = 0}, }; vk::RenderingInfo renderingInfo = { .renderArea = {.extent = swapchain.m_Extent}, .layerCount = 1, .colorAttachmentCount = Cast(attachmentInfos.size()), .pColorAttachments = attachmentInfos.data(), .pDepthAttachment = &depthAttachment, }; cmd.beginRendering(&renderingInfo); cmd.setViewport(0, 1, &viewport); cmd.setScissor(0, 1, &scissor); cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, &commitManager.GetDescriptorSet(), 0, nullptr); cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAllGraphics, 0, 12, &pcb); cmd.draw(Cast(vertices.size()), 1, 0, 0); cmd.endRendering(); cmd.pipelineBarrier2(&renderToPresentDependency); AbortIfFailed(cmd.end()); vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput; vk::SubmitInfo submitInfo = { .waitSemaphoreCount = 1, .pWaitSemaphores = ¤tFrame->m_ImageAcquireSem, .pWaitDstStageMask = &waitDstStage, .commandBufferCount = 1, .pCommandBuffers = &cmd, .signalSemaphoreCount = 1, .pSignalSemaphores = ¤tFrame->m_RenderFinishSem, }; AbortIfFailed(commandQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence)); currentFrame->Present(commandQueue, &swapchain, &surface, window.GetSize()); } device.WaitIdle(); device.m_Device.destroy(copyPool, nullptr); return 0; } Pipeline CreatePipeline(const systems::CommitManager *resourceManager, const Swapchain *swapchain) { // Pipeline Setup auto *device = resourceManager->m_Device; auto vertexShaderModule = CreateShader(device, VERTEX_SHADER_FILE); auto fragmentShaderModule = CreateShader(device, FRAGMENT_SHADER_FILE); eastl::array shaderStages = {{ { .stage = vk::ShaderStageFlagBits::eVertex, .module = vertexShaderModule, .pName = "main", }, { .stage = vk::ShaderStageFlagBits::eFragment, .module = fragmentShaderModule, .pName = "main", }, }}; auto descriptorSetLayout = resourceManager->GetDescriptorSetLayout(); vk::PushConstantRange pcr = { .stageFlags = vk::ShaderStageFlagBits::eAllGraphics, .offset = 0, .size = 12, }; vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { .setLayoutCount = 1, .pSetLayouts = &descriptorSetLayout, .pushConstantRangeCount = 1, .pPushConstantRanges = &pcr, }; vk::PipelineLayout pipelineLayout; AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); device->SetName(pipelineLayout, "Box Layout"); vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = {}; vk::PipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = { .topology = vk::PrimitiveTopology::eTriangleList, .primitiveRestartEnable = false, }; vk::PipelineViewportStateCreateInfo viewportStateCreateInfo = { .viewportCount = 1, .scissorCount = 1, }; vk::PipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = { .depthClampEnable = false, .rasterizerDiscardEnable = false, .polygonMode = vk::PolygonMode::eFill, .cullMode = vk::CullModeFlagBits::eNone, .frontFace = vk::FrontFace::eCounterClockwise, .depthBiasEnable = false, .lineWidth = 1.0, }; vk::PipelineMultisampleStateCreateInfo multisampleStateCreateInfo = { .rasterizationSamples = vk::SampleCountFlagBits::e1, .sampleShadingEnable = false, }; vk::PipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = { .depthTestEnable = true, .depthWriteEnable = true, .depthCompareOp = vk::CompareOp::eLess, }; vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = { .blendEnable = false, .srcColorBlendFactor = vk::BlendFactor::eSrcColor, .dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcColor, .colorBlendOp = vk::BlendOp::eAdd, .srcAlphaBlendFactor = vk::BlendFactor::eSrcAlpha, .dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha, .alphaBlendOp = vk::BlendOp::eAdd, .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA, }; vk::PipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = { .logicOpEnable = false, .attachmentCount = 1, .pAttachments = &colorBlendAttachmentState, }; eastl::array dynamicStates = { vk::DynamicState::eScissor, vk::DynamicState::eViewport, }; vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo = { .dynamicStateCount = Cast(dynamicStates.size()), .pDynamicStates = dynamicStates.data(), }; vk::PipelineRenderingCreateInfo renderingCreateInfo = { .viewMask = 0, .colorAttachmentCount = 1, .pColorAttachmentFormats = &swapchain->m_Format, .depthAttachmentFormat = vk::Format::eD24UnormS8Uint, }; vk::GraphicsPipelineCreateInfo pipelineCreateInfo = { .pNext = &renderingCreateInfo, .stageCount = Cast(shaderStages.size()), .pStages = shaderStages.data(), .pVertexInputState = &vertexInputStateCreateInfo, .pInputAssemblyState = &inputAssemblyStateCreateInfo, .pViewportState = &viewportStateCreateInfo, .pRasterizationState = &rasterizationStateCreateInfo, .pMultisampleState = &multisampleStateCreateInfo, .pDepthStencilState = &depthStencilStateCreateInfo, .pColorBlendState = &colorBlendStateCreateInfo, .pDynamicState = &dynamicStateCreateInfo, .layout = pipelineLayout, }; vk::Pipeline pipeline; AbortIfFailed(device->m_Device.createGraphicsPipelines(nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline)); device->SetName(pipeline, "Box Pipeline"); device->m_Device.destroy(vertexShaderModule, nullptr); device->m_Device.destroy(fragmentShaderModule, nullptr); return {device, pipelineLayout, pipeline, {}}; } vk::ShaderModule CreateShader(const Device *device, cstr shaderFile) { eastl::vector shaderCode = ReadFile(shaderFile); const vk::ShaderModuleCreateInfo shaderModuleCreateInfo = { .codeSize = shaderCode.size() * sizeof(u32), .pCode = shaderCode.data(), }; vk::ShaderModule shaderModule; AbortIfFailedMV(device->m_Device.createShaderModule(&shaderModuleCreateInfo, nullptr, &shaderModule), "Shader {} could not be created.", shaderFile); return shaderModule; }