diff --git a/engine/include/sophia/application.hpp b/engine/include/sophia/application.hpp index c4fc790..cb75d9b 100644 --- a/engine/include/sophia/application.hpp +++ b/engine/include/sophia/application.hpp @@ -1,7 +1,14 @@ #pragma once +#include +#include #include +#include +#include +#include +#include #include +#include namespace sophia { @@ -11,20 +18,47 @@ class Application { int width; int height; std::string name; - bool headless = false; }; Application(const Application&) = delete; Application& operator=(const Application&) = delete; Application(const Config& config); - ~Application(); + virtual ~Application(); void run(); + static void setCommandLineArgs(int argc, const char* const* argv); + + protected: + virtual void onCreate() {} + virtual void onUpdate(const FrameInfo& frameInfo) { (void)frameInfo; } + virtual void onRender() {} + virtual void onDestroy() {} + + Canvas canvas; + Camera2D camera; + std::vector args; + private: + friend class Canvas; + + void pushModel(std::vector vertices, std::vector indices); + void clearModels(); + class Impl; std::unique_ptr impl; }; } // namespace sophia + +#define SOPHIA_MAIN(AppType) \ + int main(int argc, const char** argv) { \ + std::cout << __FILE__ << "::" << __LINE__ << '\n'; \ + \ + sophia::Application::setCommandLineArgs(argc, argv); \ + AppType app; \ + app.run(); \ + \ + return EXIT_SUCCESS; \ + } diff --git a/engine/src/application.cpp b/engine/src/application.cpp index 52e1170..88cde93 100644 --- a/engine/src/application.cpp +++ b/engine/src/application.cpp @@ -1,25 +1,257 @@ +#include +#include +#include #include +#include +#include +#include +#include +#include -#include "engine.hpp" +#define GLM_FORCE_RADIANS +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include + +#include "buffer.hpp" +#include "device.hpp" +#include "model.hpp" +#include "pipelines/graphics_pipeline.hpp" +#include "render_context.hpp" +#include "shader.hpp" +#include "systems/model_render_system.hpp" +#include "window.hpp" namespace sophia { -class Application::Impl { - public: - Impl(const Application::Config& config) - : engine{{config.width, config.height, config.name, config.headless}} {} +namespace { - void run() { engine.run(); } - - private: - Engine engine; +struct Vertex { + glm::vec2 position; + glm::vec3 color; }; +struct PushConstants { + glm::mat4 transform; + glm::vec4 color; + glm::vec4 data; +}; + +std::vector commandLineArgs; + +} // namespace + +class Application::Impl { + public: + Impl(Application& application, const Application::Config& config) + : application{application}, + config{config}, + window{config.width, config.height, config.name}, + device{&window}, + renderContext{window, device} {} + + /*void run(const std::string& vertShaderPath, + const std::string& fragShaderPath) { + log::info("Running Render Triangle Test..."); + + RenderContext renderContext{window, device}; + + auto vertexShader = Shader::Builder() + .fromGLSL(vertShaderPath) + .setStage(ShaderStage::VERTEX) + .build(); + auto fragmentShader = Shader::Builder() + .fromGLSL(fragShaderPath) + .setStage(ShaderStage::FRAGMENT) + .build(); + + vk::PushConstantRange pushConstantRange{}; + pushConstantRange.stageFlags = + vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment; + pushConstantRange.offset = 0; + pushConstantRange.size = sizeof(PushConstants); + + vk::PipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.pushConstantRangeCount = 1; + pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange; + + vk::UniquePipelineLayout pipelineLayout = + device.get()->createPipelineLayoutUnique(pipelineLayoutInfo); + + std::vector bindingDescriptions{ + {0, sizeof(Vertex), vk::VertexInputRate::eVertex}}; + std::vector attributeDescriptions{ + {0, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, position)}, + {1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color)}}; + + std::unique_ptr graphicsPipeline = + GraphicsPipeline::Builder(device) + .setVertexShader(*vertexShader) + .setFragmentShader(*fragmentShader) + .setPipelineLayout(pipelineLayout.get()) + .setColorAttachmentFormats({renderContext.getImageFormat()}) + .setVertexInput(bindingDescriptions, attributeDescriptions) + .build(); + + // Oversized triangle covering the full screen so the fragment shader runs + // for every pixel. + std::vector vertices{ + {{-1.0f, -1.0f}, {1.0f, 0.0f, 0.0f}}, + {{3.0f, -1.0f}, {0.0f, 1.0f, 0.0f}}, + {{-1.0f, 3.0f}, {0.0f, 0.0f, 1.0f}}, + }; + + Buffer vertexBuffer{device, sizeof(Vertex), + static_cast(vertices.size()), + vk::BufferUsageFlagBits::eVertexBuffer, + vk::MemoryPropertyFlagBits::eHostVisible | + vk::MemoryPropertyFlagBits::eHostCoherent}; + vertexBuffer.map(); + vertexBuffer.writeToBuffer(vertices.data(), + sizeof(Vertex) * vertices.size()); + vertexBuffer.unmap(); + + + }*/ + + void run() { + ModelRenderSystem modelRenderSystem{device, renderContext}; + + this->application.onCreate(); + + auto startTime = std::chrono::high_resolution_clock::now(); + auto currentTime = startTime; + u32 frameIndex = 0; + + while (!window.shouldClose()) { + glfwPollEvents(); + + auto newTime = std::chrono::high_resolution_clock::now(); + double deltaTime = + std::chrono::duration(newTime - currentTime).count(); + currentTime = newTime; + + double elapsedTime = + std::chrono::duration(currentTime - startTime).count(); + + vk::Extent2D extent = renderContext.getExtent(); + FrameInfo frameInfo{frameIndex, elapsedTime, deltaTime, + glm::vec2(static_cast(extent.width), + static_cast(extent.height)), + this->application.camera}; + this->application.onUpdate(frameInfo); + this->application.onRender(); + this->application.camera.setProjection( + -frameInfo.currentFramebufferExtent.x / 2.f, + frameInfo.currentFramebufferExtent.x / 2.f, + -frameInfo.currentFramebufferExtent.y / 2.f, + frameInfo.currentFramebufferExtent.y / 2.f); + + vk::CommandBuffer commandBuffer = renderContext.beginFrame(); + if (!commandBuffer) { + continue; + } + + renderContext.transitionCurrentImage( + commandBuffer, vk::ImageLayout::eColorAttachmentOptimal); + + vk::ClearValue clearValue{}; + clearValue.color = + vk::ClearColorValue{std::array{0.01f, 0.01f, 0.02f, 1.0f}}; + + vk::RenderingAttachmentInfo colorAttachment{}; + colorAttachment.imageView = renderContext.getCurrentImageView(); + colorAttachment.imageLayout = vk::ImageLayout::eColorAttachmentOptimal; + colorAttachment.loadOp = vk::AttachmentLoadOp::eClear; + colorAttachment.storeOp = vk::AttachmentStoreOp::eStore; + colorAttachment.clearValue = clearValue; + + vk::RenderingInfo renderingInfo{}; + renderingInfo.renderArea = vk::Rect2D{{0, 0}, extent}; + renderingInfo.layerCount = 1; + renderingInfo.colorAttachmentCount = 1; + renderingInfo.pColorAttachments = &colorAttachment; + + commandBuffer.beginRendering(renderingInfo); + + vk::Viewport viewport{0.0f, + 0.0f, + static_cast(extent.width), + static_cast(extent.height), + 0.0f, + 1.0f}; + vk::Rect2D scissor{{0, 0}, extent}; + commandBuffer.setViewport(0, viewport); + commandBuffer.setScissor(0, scissor); + + modelRenderSystem.render(commandBuffer, this->models, frameInfo); + + commandBuffer.endRendering(); + + renderContext.transitionCurrentImage(commandBuffer, + vk::ImageLayout::ePresentSrcKHR); + + renderContext.endFrame(); + + frameIndex++; + } + + this->application.onDestroy(); + + device.waitIdle(); + + auto endTime = std::chrono::high_resolution_clock::now(); + double totalRuntime = + std::chrono::duration(endTime - startTime).count(); + log::info("Application ran for", totalRuntime, "s"); + } + + void pushModel(std::vector vertices, std::vector indices) { + Model::Builder builder{}; + builder.vertices.reserve(vertices.size()); + for (const CanvasVertex& vertex : vertices) { + builder.vertices.push_back({vertex.position, vertex.color}); + } + builder.indices = std::move(indices); + + this->models.push_back(std::make_unique(this->device, builder)); + } + + void clearModels() { + // Models back live GPU vertex buffers and up to MAX_FRAMES_IN_FLIGHT + // command buffers may still be executing against them, so wait for the + // device to go idle before freeing anything. + this->device.waitIdle(); + this->models.clear(); + } + + private: + Application& application; + const Application::Config config; + Window window; + Device device; + RenderContext renderContext; + + std::vector> models; +}; + +void Application::setCommandLineArgs(int argc, const char* const* argv) { + commandLineArgs.assign(argv, argv + argc); +} + Application::Application(const Config& config) - : impl{std::make_unique(config)} {} + : impl{std::make_unique(*this, config)}, + canvas{this}, + args{std::move(commandLineArgs)} {} Application::~Application() = default; void Application::run() { impl->run(); } +void Application::pushModel(std::vector vertices, + std::vector indices) { + impl->pushModel(std::move(vertices), std::move(indices)); +} + +void Application::clearModels() { impl->clearModels(); } + } // namespace sophia