diff --git a/include/Orange/Core/Result.h b/include/Orange/Core/Result.h index 8167bec..9b06bf0 100644 --- a/include/Orange/Core/Result.h +++ b/include/Orange/Core/Result.h @@ -3,6 +3,7 @@ #include #include #include +#include namespace orange { @@ -17,6 +18,10 @@ namespace orange Success = 0, }; + struct VoidResult + { + }; + template class Result { @@ -128,4 +133,51 @@ namespace orange ErrorCode m_error = InvalidCode; bool m_created = false; }; + + // It's like a reference, but allows + // it to be nullptr once, at constructor time + // and never again. + template + class WeakRef + { + public: + WeakRef() {} + WeakRef(std::nullptr_t) = delete; + WeakRef(T& ref) + : m_ptr{ &ref } { } + + WeakRef(const WeakRef& other) + : m_ptr { other.m_ptr } {} + + WeakRef& operator = (T& object) + { + m_ptr = &object; + return *this; + } + + WeakRef& operator = (const WeakRef& other) + { + m_ptr = other.m_ptr; + return *this; + } + + WeakRef& operator = (std::nullptr_t) = delete; + + T* operator -> () const { return m_ptr; } + + T** operator & () { return &m_ptr; } + T* const* operator & () const { return &m_ptr; } + + bool operator == (const WeakRef& other) const { return m_ptr == other.m_ptr; } + bool operator != (const WeakRef& other) const { return m_ptr != other.m_ptr; } + + bool operator == (const T* other) const { return m_ptr == other; } + bool operator != (const T* other) const { return m_ptr != other; } + + bool operator == (std::nullptr_t) const { return m_ptr == nullptr; } + bool operator != (std::nullptr_t) const { return m_ptr != nullptr; } + + private: + T* m_ptr = nullptr; + }; } diff --git a/include/Orange/Render/RenderContext.h b/include/Orange/Render/RenderContext.h index 9544740..dc14d0c 100644 --- a/include/Orange/Render/RenderContext.h +++ b/include/Orange/Render/RenderContext.h @@ -26,6 +26,8 @@ namespace orange VulkanResult CreateFence(bool signalled); VulkanResult CreateSemaphore(); + VulkanResult BeginCommandBuffer(VkCommandBuffer buffer); + VulkanResult EndCommandBuffer(VkCommandBuffer buffer); protected: friend VulkanResult; RenderContext(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device, diff --git a/include/Orange/Render/Swapchain.h b/include/Orange/Render/Swapchain.h index 2951d09..0134654 100644 --- a/include/Orange/Render/Swapchain.h +++ b/include/Orange/Render/Swapchain.h @@ -18,7 +18,12 @@ namespace orange static Result Create(RenderContext& context, VkSurfaceKHR surface); - VkCommandBuffer CommandBuffer() const { return m_commandBuffers[m_currentImage]; } + VkImage Image() const { return m_images[m_currentImage]; } + VkImageView ImageView() const { return m_imageViews[m_currentImage]; } + VkCommandBuffer CommandBuffer() const { return m_commandBuffers[m_currentFrame]; } + VkExtent2D Extent() const { return m_extent; } + + void Present(); protected: friend class Result; Swapchain(RenderContext& context, VkSurfaceKHR surface, VkFormat format, VkExtent2D extent, VkSwapchainKHR swapchain, diff --git a/include/Orange/Render/Window.h b/include/Orange/Render/Window.h index a1c4053..10d2570 100644 --- a/include/Orange/Render/Window.h +++ b/include/Orange/Render/Window.h @@ -19,6 +19,8 @@ namespace orange static const char* GetError(); static Result Create(); + + bool Update(); protected: friend Result; Window(SDL_Window* window); diff --git a/src/Apps/Tools/CubeTest.cpp b/src/Apps/Tools/CubeTest.cpp index 01886b3..0f21d80 100644 --- a/src/Apps/Tools/CubeTest.cpp +++ b/src/Apps/Tools/CubeTest.cpp @@ -3,6 +3,7 @@ #include #include #include +#include using namespace orange; @@ -26,5 +27,34 @@ int main(int argc, char** argv) if (!r_swapchain) return 1; + while (r_window->Update()) + { + r_renderContext->BeginCommandBuffer(r_swapchain->CommandBuffer()); + { + const VkRenderingAttachmentInfoKHR attachmentInfo = + { + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR, + .imageView = r_swapchain->ImageView(), + .imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .clearValue = { .color = { .float32 = { 1.0f, 0.5f, 0.0f, 1.0f } } }, + }; + + const VkRenderingInfo renderInfo = + { + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + .renderArea = { {}, r_swapchain->Extent() }, + .layerCount = 1, + .colorAttachmentCount = 1, + .pColorAttachments = &attachmentInfo, + }; + vkCmdBeginRendering(r_swapchain->CommandBuffer(), &renderInfo); + vkCmdEndRendering(r_swapchain->CommandBuffer()); + } + r_renderContext->EndCommandBuffer(r_swapchain->CommandBuffer()); + r_swapchain->Present(); + } + return 0; } diff --git a/src/Orange/Render/RenderContext_Util.cpp b/src/Orange/Render/RenderContext_Util.cpp index 75d11ca..94f00f2 100644 --- a/src/Orange/Render/RenderContext_Util.cpp +++ b/src/Orange/Render/RenderContext_Util.cpp @@ -33,4 +33,26 @@ namespace orange return VulkanResult::Success(semaphore); } + + VulkanResult RenderContext::BeginCommandBuffer(VkCommandBuffer buffer) + { + VkCommandBufferBeginInfo beginInfo = + { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + }; + + VkResult res = VK_SUCCESS; + if ((res = vkBeginCommandBuffer(buffer, &beginInfo)) != VK_SUCCESS) + return VulkanResult::Error(res); + + return VulkanResult::Success(); + } + VulkanResult RenderContext::EndCommandBuffer(VkCommandBuffer buffer) + { + VkResult res = VK_SUCCESS; + if ((res = vkEndCommandBuffer(buffer)) != VK_SUCCESS) + return VulkanResult::Error(res); + + return VulkanResult::Success(); + } } diff --git a/src/Orange/Render/Swapchain.cpp b/src/Orange/Render/Swapchain.cpp index ab56de5..b1918cd 100644 --- a/src/Orange/Render/Swapchain.cpp +++ b/src/Orange/Render/Swapchain.cpp @@ -154,6 +154,12 @@ namespace orange // TODO: Handle failure uint32_t imageIndex; vkAcquireNextImageKHR(context.Device(), swapchain, ~0u, imageAvailableSemaphores[0], VK_NULL_HANDLE, &imageIndex); + + return Result::Success( + context, surface, r_format->format, extent, swapchain, + swapchainImages, swapchainImageViews, commandBuffers, + imageAvailableSemaphores, renderFinishedSemaphores, inFlightFences, + imageIndex); } Swapchain::~Swapchain() @@ -162,4 +168,51 @@ namespace orange vkDestroyImageView(m_ctx.Device(), imageView, nullptr); vkDestroySwapchainKHR(m_ctx.Device(), m_swapchain, nullptr); } + + void Swapchain::Present() + { + const VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo submitInfo = + { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &m_imageAvailableSemaphores[m_currentFrame], + .pWaitDstStageMask = &waitStage, + .commandBufferCount = 1, + .pCommandBuffers = &m_commandBuffers[m_currentFrame], + .signalSemaphoreCount = 1, + .pSignalSemaphores = &m_renderFinishedSemaphores[m_currentFrame], + }; + + // TODO: Handle failure properly. + if (vkQueueSubmit(m_ctx.Queue(), 1, &submitInfo, m_inFlightFences[m_currentFrame]) != VK_SUCCESS) { + log::err("Failed to submit work"); + return; + } + + VkPresentInfoKHR presentInfo = + { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &m_renderFinishedSemaphores[m_currentFrame], + .swapchainCount = 1, + .pSwapchains = &m_swapchain, + .pImageIndices = &m_currentImage, + }; + + if (vkQueuePresentKHR(m_ctx.Queue(), &presentInfo) != VK_SUCCESS) { + log::err("Failed to submit present"); + return; + } + + // Prepare for next frame. + + m_currentFrame = (m_currentFrame + 1) % DefaultFramesInFlight; + + vkResetCommandBuffer(m_commandBuffers[m_currentFrame], 0u); + + vkWaitForFences(m_ctx.Device(), 1, &m_inFlightFences[m_currentFrame], VK_TRUE, UINT64_MAX); + vkResetFences(m_ctx.Device(), 1, &m_inFlightFences[m_currentFrame]); + vkAcquireNextImageKHR(m_ctx.Device(), m_swapchain, ~0u, m_imageAvailableSemaphores[m_currentFrame], VK_NULL_HANDLE, &m_currentImage); + } } \ No newline at end of file diff --git a/src/Orange/Render/Window.cpp b/src/Orange/Render/Window.cpp index c40bb4c..04bd0ac 100644 --- a/src/Orange/Render/Window.cpp +++ b/src/Orange/Render/Window.cpp @@ -5,6 +5,7 @@ #include #include +#include #include namespace orange @@ -56,4 +57,22 @@ namespace orange { return SDL_GetError(); } + + bool Window::Update() + { + bool quit = false; + + SDL_Event e; + while (SDL_PollEvent(&e)) + { + switch (e.type) + { + case SDL_QUIT: + quit = true; + break; + } + } + + return !quit; + } }