diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index b9c5848..a6982a9 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -1,6 +1,35 @@ project(genesis-editor) +### Currently we require that the editor has all spv binary files in the same directory as the executable +# This may change at a later date. For now, we copy the files to the build directory. + +# Set the source and destination directories +set(SOURCE_DIR "${CMAKE_SOURCE_DIR}/editor/bin") + +# Get a list of all .spv files in the source directory +file(GLOB SPV_FILES "${SOURCE_DIR}/*.spv") + +# This target does the following: +# - Create the destination directory if it does not exist +# - Remove all .spv files from the destination directory if they exist. +# - Copy all .spv files from the source directory to the destination directory + +add_custom_target(copy_spv_files ALL + COMMENT "Copying .spv files" + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/editor/Debug/bin" + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/editor/Release/bin" + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/editor/RelWithDebInfo/bin" + COMMAND ${CMAKE_COMMAND} -E remove "${CMAKE_BINARY_DIR}/editor/Debug/bin/*.spv" + COMMAND ${CMAKE_COMMAND} -E remove "${CMAKE_BINARY_DIR}/editor/Release/bin/*.spv" + COMMAND ${CMAKE_COMMAND} -E remove "${CMAKE_BINARY_DIR}/editor/RelWithDebInfo/bin/*.spv" + COMMAND ${CMAKE_COMMAND} -E copy ${SPV_FILES} "${CMAKE_BINARY_DIR}/editor/Debug/bin" + COMMAND ${CMAKE_COMMAND} -E copy ${SPV_FILES} "${CMAKE_BINARY_DIR}/editor/Release/bin" + COMMAND ${CMAKE_COMMAND} -E copy ${SPV_FILES} "${CMAKE_BINARY_DIR}/editor/RelWithDebInfo/bin" + DEPENDS ${SPV_FILES} +) + add_executable(genesis-editor) +add_dependencies(genesis-editor copy_spv_files) # The engine requires the .spv files to be copied before it can be built target_link_libraries(genesis-editor PRIVATE genesis::lib genesis::ext diff --git a/engine/include/application.hpp b/engine/include/application.hpp index 3083104..6bccb7a 100644 --- a/engine/include/application.hpp +++ b/engine/include/application.hpp @@ -16,12 +16,7 @@ namespace gen class Application { public: - explicit Application(const char * appName, mim::vec2i const & initialSize) - : m_window{initialSize, appName}, - m_graphicsDevice{m_window, appName}, - - {} - + explicit Application(const char * appName, mim::vec2i const & initialSize); void run(); @@ -33,7 +28,6 @@ namespace gen GraphicsDevice m_graphicsDevice; GraphicsPipeline m_graphicsPipeline; - Logger m_logger{"application"}; }; diff --git a/engine/include/graphics/device.hpp b/engine/include/graphics/device.hpp index 356797c..3fe1e8f 100644 --- a/engine/include/graphics/device.hpp +++ b/engine/include/graphics/device.hpp @@ -47,6 +47,12 @@ namespace gen GEN_NODISCARD vk::SurfaceKHR const & getSurface() const { return m_surface.get(); } GEN_NODISCARD vk::PhysicalDevice const & getPhysicalDevice() const { return m_gpu.physicalDevice; } GEN_NODISCARD vk::Device const & getDevice() const { return m_device.get(); } + GEN_NODISCARD vk::UniqueDevice const & getUniqueDevice() const { return m_device; } + GEN_NODISCARD vk::Queue const & getGraphicsQueue() const { return m_graphicsQueue; } + GEN_NODISCARD vk::SwapchainKHR const & getSwapChain() const { return m_swapChain.get(); } + GEN_NODISCARD vk::SwapchainCreateInfoKHR const & getSwapChainInfo() const { return m_swapChainInfo; } + GEN_NODISCARD std::vector const & getSwapChainImages() const { return m_swapChainImages; } + GEN_NODISCARD std::vector const & getSwapChainImageViews() const { return m_swapChainImageViews; } /// Setters diff --git a/engine/include/graphics/pipeline.hpp b/engine/include/graphics/pipeline.hpp index 88a192d..179b922 100644 --- a/engine/include/graphics/pipeline.hpp +++ b/engine/include/graphics/pipeline.hpp @@ -7,6 +7,7 @@ #include #include "core.hpp" +#include "logger/log.hpp" #include "device.hpp" @@ -14,6 +15,17 @@ namespace gen { struct PipelineConfigInfo { + vk::Viewport viewport{}; + vk::Rect2D scissor{}; + vk::PipelineInputAssemblyStateCreateInfo inputAssemblyInfo{}; + vk::PipelineTessellationStateCreateInfo tessellationInfo{}; + vk::PipelineViewportStateCreateInfo viewportInfo{}; + vk::PipelineRasterizationStateCreateInfo rasterizationInfo{}; + vk::PipelineMultisampleStateCreateInfo multisampleInfo{}; + vk::PipelineDepthStencilStateCreateInfo depthStencilInfo{}; + vk::PipelineColorBlendStateCreateInfo colorBlendInfo{}; + vk::PipelineColorBlendAttachmentState colorBlendAttachment{}; + uint32_t subpass{}; }; class GraphicsPipeline @@ -28,23 +40,29 @@ namespace gen static PipelineConfigInfo defaultPipelineConfigInfo(mim::vec2i extent); private: + void createRenderPass(); + static std::vector readFile(const std::string & filePath); void createGraphicsPipeline(); - void createShaderModule(const std::vector & code, vk::ShaderModule * shaderModule); - + void createShaderModule(const std::vector & code); // NOLINTNEXTLINE The assumption is that the device will outlive the pipeline at all times since a device fundamentally needs a pipeline to exist GraphicsDevice & m_device; - vk::UniquePipeline m_graphicsPipeline; vk::UniqueShaderModule m_vertShaderModule; vk::UniqueShaderModule m_fragShaderModule; + vk::UniqueRenderPass m_renderPass; + vk::UniquePipelineLayout m_pipelineLayout; + vk::UniquePipeline m_graphicsPipeline; + const PipelineConfigInfo m_configInfo; vk::UniqueInstance m_instance; vk::DebugUtilsMessengerEXT m_debugMessenger; + + Logger m_logger{"graphics"}; }; } // namespace gen \ No newline at end of file diff --git a/engine/include/graphics/vkHelpers.hpp b/engine/include/graphics/vkHelpers.hpp index 614922b..ba26810 100644 --- a/engine/include/graphics/vkHelpers.hpp +++ b/engine/include/graphics/vkHelpers.hpp @@ -33,7 +33,9 @@ namespace vk::util bool checkDeviceExtensionSupport(vk::PhysicalDevice device, const std::vector & deviceExtensions); - vk::UniqueShaderModule createShaderModule(vk::Device device, vk::ShaderStageFlagBits shaderStage, std::string const & shaderText); + vk::UniqueShaderModule createShaderModule(vk::Device device, std::vector const & code); + + vk::UniqueShaderModule createShaderModuleFromHLSL(vk::Device device, vk::ShaderStageFlagBits shaderStage, std::string const & shaderText); vk::UniqueSurfaceKHR createWindowSurface(vk::Instance instance, gen::Window const & window); diff --git a/engine/src/application.cpp b/engine/src/application.cpp index 97b71d7..5b5b075 100644 --- a/engine/src/application.cpp +++ b/engine/src/application.cpp @@ -9,6 +9,12 @@ namespace gen { // possibly change this to instead use Update() and Draw() functions + Application::Application(const char * appName, const mim::vec2i & initialSize) + : m_window(initialSize, appName), m_graphicsDevice(m_window, appName), + m_graphicsPipeline(m_graphicsDevice, GraphicsPipeline::defaultPipelineConfigInfo(initialSize)) + { + } + void Application::run() { gameLoop(); diff --git a/engine/src/graphics/device.cpp b/engine/src/graphics/device.cpp index f9083a9..0c9b009 100644 --- a/engine/src/graphics/device.cpp +++ b/engine/src/graphics/device.cpp @@ -18,6 +18,7 @@ namespace gen { static const std::vector deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, + VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, #ifdef GEN_PLATFORM_APPLE VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, #endif diff --git a/engine/src/graphics/pipeline.cpp b/engine/src/graphics/pipeline.cpp index 8e8ec75..67be036 100644 --- a/engine/src/graphics/pipeline.cpp +++ b/engine/src/graphics/pipeline.cpp @@ -1,38 +1,121 @@ // Copyright (c) 2023-present Genesis Engine contributors (see LICENSE.txt) -#include "core.hpp" #include "graphics/pipeline.hpp" #include "graphics/vkHelpers.hpp" -#include "logger/log.hpp" +#include +#include #include #include -#include #include +#include namespace gen { - GraphicsPipeline::GraphicsPipeline(GraphicsDevice & device, const PipelineConfigInfo & configInfo) - : m_device{device}, m_configInfo{configInfo} + GraphicsPipeline::GraphicsPipeline(GraphicsDevice & device, const PipelineConfigInfo & configInfo) : m_device{device}, m_configInfo{configInfo} { + createRenderPass(); + createGraphicsPipeline(); + m_logger.info("Graphics pipeline constructed"); } GraphicsPipeline::~GraphicsPipeline() { + m_logger.info("Graphics pipeline destructed"); } PipelineConfigInfo GraphicsPipeline::defaultPipelineConfigInfo(const mim::vec2i extent) { PipelineConfigInfo configInfo{}; + + configInfo.viewport.x = 0.0F; + configInfo.viewport.y = 0.0F; + configInfo.viewport.width = static_cast(extent.x); + configInfo.viewport.height = static_cast(extent.y); + configInfo.viewport.minDepth = 0.0F; + configInfo.viewport.maxDepth = 1.0F; + configInfo.scissor.offset = vk::Offset2D{0, 0}; + configInfo.scissor.extent = vk::Extent2D{static_cast(extent.x), static_cast(extent.y)}; + + configInfo.inputAssemblyInfo.flags = vk::PipelineInputAssemblyStateCreateFlags{}; + configInfo.inputAssemblyInfo.topology = vk::PrimitiveTopology::eTriangleList; + configInfo.inputAssemblyInfo.primitiveRestartEnable = false; + + configInfo.viewportInfo.flags = vk::PipelineViewportStateCreateFlags{}; + configInfo.viewportInfo.sType = vk::StructureType::ePipelineViewportStateCreateInfo; + configInfo.viewportInfo.viewportCount = 1; + configInfo.viewportInfo.pViewports = &configInfo.viewport; + configInfo.viewportInfo.scissorCount = 1; + configInfo.viewportInfo.pScissors = &configInfo.scissor; + + configInfo.rasterizationInfo.flags = vk::PipelineRasterizationStateCreateFlags{}; + configInfo.rasterizationInfo.sType = vk::StructureType::ePipelineRasterizationStateCreateInfo; + configInfo.rasterizationInfo.depthClampEnable = false; + configInfo.rasterizationInfo.rasterizerDiscardEnable = false; + configInfo.rasterizationInfo.polygonMode = vk::PolygonMode::eFill; + configInfo.rasterizationInfo.lineWidth = 1.0F; + configInfo.rasterizationInfo.cullMode = vk::CullModeFlagBits::eNone; // TODO: Switch this to eBack once done testing. + configInfo.rasterizationInfo.frontFace = vk::FrontFace::eClockwise; + configInfo.rasterizationInfo.depthBiasEnable = false; + configInfo.rasterizationInfo.depthBiasConstantFactor = 0.0F; // Optional + configInfo.rasterizationInfo.depthBiasClamp = 0.0F; // Optional + configInfo.rasterizationInfo.depthBiasSlopeFactor = 0.0F; // Optional + + // Multi-sampling is not currently enabled. This will be enabled later. + configInfo.multisampleInfo.flags = vk::PipelineMultisampleStateCreateFlags{}; + configInfo.multisampleInfo.sType = vk::StructureType::ePipelineMultisampleStateCreateInfo; + configInfo.multisampleInfo.rasterizationSamples = vk::SampleCountFlagBits::e1; + configInfo.multisampleInfo.sampleShadingEnable = false; + configInfo.multisampleInfo.minSampleShading = 1.0F; // Optional + configInfo.multisampleInfo.pSampleMask = nullptr; // Optional + configInfo.multisampleInfo.alphaToCoverageEnable = false; // Optional + configInfo.multisampleInfo.alphaToOneEnable = false; // Optional + + // implement depth and stencil later + + configInfo.colorBlendAttachment.colorWriteMask = + vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA; + // this implements alpha blending + configInfo.colorBlendAttachment.blendEnable = true; + configInfo.colorBlendAttachment.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha; // Optional + configInfo.colorBlendAttachment.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha; // Optional + configInfo.colorBlendAttachment.colorBlendOp = vk::BlendOp::eAdd; // Optional + configInfo.colorBlendAttachment.srcAlphaBlendFactor = vk::BlendFactor::eOne; // Optional + configInfo.colorBlendAttachment.dstAlphaBlendFactor = vk::BlendFactor::eZero; // Optional + configInfo.colorBlendAttachment.alphaBlendOp = vk::BlendOp::eAdd; // Optional + + configInfo.colorBlendInfo.flags = vk::PipelineColorBlendStateCreateFlags{}; + configInfo.colorBlendInfo.sType = vk::StructureType::ePipelineColorBlendStateCreateInfo; + configInfo.colorBlendInfo.logicOpEnable = true; + configInfo.colorBlendInfo.logicOp = vk::LogicOp::eCopy; // Optional + configInfo.colorBlendInfo.attachmentCount = 1; + configInfo.colorBlendInfo.pAttachments = &configInfo.colorBlendAttachment; + configInfo.colorBlendInfo.blendConstants[0] = 0.0F; // Optional + configInfo.colorBlendInfo.blendConstants[1] = 0.0F; // Optional + configInfo.colorBlendInfo.blendConstants[2] = 0.0F; // Optional + configInfo.colorBlendInfo.blendConstants[3] = 0.0F; // Optional + + configInfo.subpass = 0; + return configInfo; } std::vector GraphicsPipeline::readFile(const std::string & filePath) { - std::ifstream file(filePath, std::ios::ate | std::ios::binary); + // This function is temporary for testing purposes. Will replace later. + + const Logger logger{"graphics"}; + + std::string currentDir = std::filesystem::current_path().string(); + std::string path = currentDir + "/" + filePath; + + logger.info("Attempting to open file: {}", path); + + std::ifstream file(path, std::ios::ate | std::ios::binary); if (!file.is_open()) { + logger.error("Failed to open file: {}", filePath); throw std::runtime_error("failed to open file: " + filePath); } @@ -48,13 +131,85 @@ namespace gen return buffer; } - void GraphicsPipeline::createGraphicsPipeline() + void GraphicsPipeline::createRenderPass() { + vk::AttachmentDescription colorAttachment{ + {}, + m_device.getSwapChainInfo().imageFormat, + vk::SampleCountFlagBits::e1, + vk::AttachmentLoadOp::eClear, // Clear framebuffer to black before drawing a new frame. + vk::AttachmentStoreOp::eStore, // Store the framebuffer to memory after drawing a new frame. + vk::AttachmentLoadOp::eDontCare, + vk::AttachmentStoreOp::eDontCare, + vk::ImageLayout::eUndefined, + vk::ImageLayout::ePresentSrcKHR}; + + vk::AttachmentReference colorAttachmentRef{0, vk::ImageLayout::eColorAttachmentOptimal}; + + // for the time being we are only doing a single subpass + vk::SubpassDescription subpass{{}, vk::PipelineBindPoint::eGraphics, 0, nullptr, 1, &colorAttachmentRef, nullptr, nullptr, 0, nullptr}; + + vk::SubpassDependency dependency{ + VK_SUBPASS_EXTERNAL, + 0, + vk::PipelineStageFlagBits::eColorAttachmentOutput, + vk::PipelineStageFlagBits::eColorAttachmentOutput, + {}, + vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite}; + + vk::RenderPassCreateInfo renderPassInfo{{}, 1, &colorAttachment, 1, &subpass, 1, &dependency}; + + m_renderPass = m_device.getUniqueDevice()->createRenderPassUnique(renderPassInfo); } - void GraphicsPipeline::createShaderModule(const std::vector & code, vk::ShaderModule * shaderModule) + void GraphicsPipeline::createGraphicsPipeline() { + // This will be removed later. + auto vertShaderCode = readFile("bin/simplePS.spv"); + auto fragShaderCode = readFile("bin/simpleVS.spv"); + + m_vertShaderModule = vk::util::createShaderModule(m_device.getDevice(), vertShaderCode); + m_fragShaderModule = vk::util::createShaderModule(m_device.getDevice(), fragShaderCode); + + vk::PipelineShaderStageCreateInfo vertShaderStageInfo{{}, vk::ShaderStageFlagBits::eVertex, m_vertShaderModule.get(), "main"}; + vk::PipelineShaderStageCreateInfo fragShaderStageInfo{{}, vk::ShaderStageFlagBits::eFragment, m_fragShaderModule.get(), "main"}; + + // vk::PipelineVertexInputStateCreateInfo vertexInputInfo{{}, 0, nullptr, 0, nullptr}; + + std::array shaderStages = {vertShaderStageInfo, fragShaderStageInfo}; + + std::vector dynamicStates = {vk::DynamicState::eViewport, vk::DynamicState::eScissor}; + + vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo{{}, static_cast(dynamicStates.size()), dynamicStates.data()}; + + vk::PipelineVertexInputStateCreateInfo vertexInputInfo{{}, 0, nullptr, 0, nullptr}; + + m_pipelineLayout = m_device.getUniqueDevice()->createPipelineLayoutUnique(vk::PipelineLayoutCreateInfo{{}, 0, nullptr, 0, nullptr}); + + vk::GraphicsPipelineCreateInfo pipelineInfo{ + {}, + 2, + shaderStages.data(), + &vertexInputInfo, + &m_configInfo.inputAssemblyInfo, + &m_configInfo.tessellationInfo, + &m_configInfo.viewportInfo, + &m_configInfo.rasterizationInfo, + &m_configInfo.multisampleInfo, + &m_configInfo.depthStencilInfo, + &m_configInfo.colorBlendInfo, + &dynamicStateCreateInfo, + m_pipelineLayout.get(), + m_renderPass.get(), + m_configInfo.subpass, + nullptr, // optional + -1}; // optional + + m_graphicsPipeline = m_device.getUniqueDevice()->createGraphicsPipelineUnique(nullptr, pipelineInfo).value; } + void GraphicsPipeline::createShaderModule(const std::vector & code) + { + } } // namespace gen \ No newline at end of file diff --git a/engine/src/graphics/vkHelpers.cpp b/engine/src/graphics/vkHelpers.cpp index 608531d..42196bb 100644 --- a/engine/src/graphics/vkHelpers.cpp +++ b/engine/src/graphics/vkHelpers.cpp @@ -120,8 +120,14 @@ namespace vk::util return true; } + vk::UniqueShaderModule createShaderModule(vk::Device device, std::vector const & code) + { + return device.createShaderModuleUnique( + vk::ShaderModuleCreateInfo(vk::ShaderModuleCreateFlags(), code.size(), reinterpret_cast(code.data()))); + } + // Create a Vulkan shader module from HLSL shader code. - vk::UniqueShaderModule createShaderModule(const vk::Device device, vk::ShaderStageFlagBits shaderStage, std::string const & shaderText) + vk::UniqueShaderModule createShaderModuleFromHLSL(const vk::Device device, vk::ShaderStageFlagBits shaderStage, std::string const & shaderText) { std::vector shaderSPV; if (!HLSLtoSPV(shaderStage, shaderText, shaderSPV))