diff --git a/kraken/CMakeLists.txt b/kraken/CMakeLists.txt index da4a9bd..eea6c01 100644 --- a/kraken/CMakeLists.txt +++ b/kraken/CMakeLists.txt @@ -61,6 +61,7 @@ add_sources(KROctreeNode.cpp) add_sources(KRParticleSystem.cpp) add_sources(KRParticleSystemNewtonian.cpp) add_sources(KRPointLight.cpp) +add_sources(KRPresentationThread.cpp) add_sources(KRRenderSettings.cpp) add_sources(KRResource+blend.cpp) # add_sources(KRResource+fbx.cpp) # TODO - Locate FBX SDK dependencies diff --git a/kraken/KRContext.cpp b/kraken/KRContext.cpp index d958979..b1b6943 100755 --- a/kraken/KRContext.cpp +++ b/kraken/KRContext.cpp @@ -36,6 +36,7 @@ #include "KRAudioManager.h" #include "KRAudioSample.h" #include "KRBundle.h" +#include "KRPresentationThread.h" #if defined(ANDROID) #include @@ -87,6 +88,7 @@ KRContext::KRContext(const KrInitializeInfo* initializeInfo) , m_topDeviceHandle(0) , m_topSurfaceHandle(0) { + m_presentationThread = std::make_unique(*this); m_resourceMap = (KRResource **)malloc(sizeof(KRResource*) * m_resourceMapSize); memset(m_resourceMap, 0, m_resourceMapSize * sizeof(KRResource*)); m_streamingEnabled = false; @@ -133,13 +135,11 @@ KRContext::KRContext(const KrInitializeInfo* initializeInfo) #endif createDeviceContexts(); - - m_presentationThread = std::thread(&KRContext::presentationThreadFunc, this); + m_presentationThread->start(); } KRContext::~KRContext() { - m_stop = true; - m_presentationThread.join(); + m_presentationThread->stop(); if(m_pSceneManager) { delete m_pSceneManager; m_pSceneManager = NULL; @@ -990,105 +990,6 @@ KrResult KRContext::deleteWindowSurface(const KrDeleteWindowSurfaceInfo* deleteW return KR_SUCCESS; } -void KRContext::presentationThreadFunc() -{ -#if defined(ANDROID) - // TODO - Set thread names on Android -#elif defined(_WIN32) || defined(_WIN64) - // TODO - Set thread names on windows -#else - pthread_setname_np("Kraken - Presentation"); -#endif - - std::chrono::microseconds sleep_duration(15000); - - while (!m_stop) - { - renderFrame(); - std::this_thread::sleep_for(sleep_duration); - } -} - -void KRContext::renderFrame() -{ - // TODO - Eliminate this and use system wide index once Vulkan path is working - static uint64_t frameIndex = 0; - - // TODO - We should use fences to eliminate this mutex - const std::lock_guard surfaceLock(KRContext::g_SurfaceInfoMutex); - - for (auto surfaceItr = m_surfaces.begin(); surfaceItr != m_surfaces.end(); surfaceItr++) { - KRSurface& surface = *(*surfaceItr).second; - KRDevice& device = GetDeviceInfo(surface.m_deviceHandle); - - uint32_t imageIndex = 0; - vkAcquireNextImageKHR(device.m_logicalDevice, surface.m_swapChain, UINT64_MAX, surface.m_imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex); - - // TODO - this will break with more than one surface... Expect to refactor this out - VkCommandBuffer commandBuffer = device.m_graphicsCommandBuffers[imageIndex]; - KRPipeline* testPipeline = m_pPipelineManager->get("vulkan_test"); - - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = 0; - beginInfo.pInheritanceInfo = nullptr; - - if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { - // TODO - Add error handling... - } - - VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} }; - - VkRenderPassBeginInfo renderPassInfo{}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassInfo.renderPass = testPipeline->getRenderPass(); - renderPassInfo.framebuffer = surface.m_swapChainFramebuffers[frameIndex % surface.m_swapChainFramebuffers.size()]; - renderPassInfo.renderArea.offset = { 0, 0 }; - renderPassInfo.renderArea.extent = surface.m_swapChainExtent; - renderPassInfo.clearValueCount = 1; - renderPassInfo.pClearValues = &clearColor; - - vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, testPipeline->getPipeline()); - vkCmdDraw(commandBuffer, 3, 1, 0, 0); - vkCmdEndRenderPass(commandBuffer); - if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { - // TODO - Add error handling... - } - - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - - VkSemaphore waitSemaphores[] = { surface.m_imageAvailableSemaphore }; - VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = waitSemaphores; - submitInfo.pWaitDstStageMask = waitStages; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; - - VkSemaphore signalSemaphores[] = { surface.m_renderFinishedSemaphore }; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = signalSemaphores; - - if (vkQueueSubmit(device.m_graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) { - // TODO - Add error handling... - } - - VkPresentInfoKHR presentInfo{}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = signalSemaphores; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = &surface.m_swapChain; - presentInfo.pImageIndices = &imageIndex; - presentInfo.pResults = nullptr; - vkQueuePresentKHR(device.m_graphicsQueue, &presentInfo); - } - - frameIndex++; -} - KRSurface& KRContext::GetSurfaceInfo(KrSurfaceHandle handle) { auto itr = m_surfaces.find(handle); @@ -1166,3 +1067,8 @@ VkInstance& KRContext::GetVulkanInstance() { return m_vulkanInstance; } + +unordered_map>& KRContext::GetSurfaces() +{ + return m_surfaces; +} diff --git a/kraken/KRContext.h b/kraken/KRContext.h index d04eae5..0c785ca 100755 --- a/kraken/KRContext.h +++ b/kraken/KRContext.h @@ -49,6 +49,7 @@ #include "KRSurface.h" class KRAudioManager; +class KRPresentationThread; class KRContext { public: @@ -159,6 +160,7 @@ public: KRDevice& GetDeviceInfo(KrDeviceHandle handle); KRSurface& GetSurfaceInfo(KrSurfaceHandle handle); + unordered_map>& GetSurfaces(); VkInstance& GetVulkanInstance(); KrSurfaceHandle GetBestDeviceForSurface(const VkSurfaceKHR& surface); @@ -212,10 +214,8 @@ private: unordered_multimap m_resources; - std::thread m_presentationThread; - void presentationThreadFunc(); - std::atomic m_stop; - void renderFrame(); + + std::unique_ptr m_presentationThread; unordered_map> m_devices; KrDeviceHandle m_topDeviceHandle; diff --git a/kraken/KRPresentationThread.cpp b/kraken/KRPresentationThread.cpp new file mode 100644 index 0000000..045c053 --- /dev/null +++ b/kraken/KRPresentationThread.cpp @@ -0,0 +1,167 @@ +// +// KRPresentationThread.cpp +// Kraken Engine +// +// Copyright 2021 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KRPresentationThread.h" + +KRPresentationThread::KRPresentationThread(KRContext& context) + : KRContextObject(context) + , m_requestedState(PresentThreadRequest::stop) + , m_activeState(PresentThreadState::stop) +{ + +} + +KRPresentationThread::~KRPresentationThread() +{ + +} + +void KRPresentationThread::start() +{ + m_requestedState = PresentThreadRequest::run; + m_thread = std::thread(&KRPresentationThread::run, this); +} + +void KRPresentationThread::stop() +{ + m_requestedState = PresentThreadRequest::stop; + m_thread.join(); +} + +void KRPresentationThread::run() +{ +#if defined(ANDROID) + // TODO - Set thread names on Android +#elif defined(_WIN32) || defined(_WIN64) + // TODO - Set thread names on windows +#else + pthread_setname_np("Kraken - Presentation"); +#endif + + std::chrono::microseconds sleep_duration(15000); + + m_activeState = PresentThreadState::run; + while (m_requestedState != PresentThreadRequest::stop) + { + while (m_requestedState == PresentThreadRequest::run) { + m_activeState = PresentThreadState::run; + renderFrame(); + std::this_thread::sleep_for(sleep_duration); + } + while (m_requestedState == PresentThreadRequest::pause) { + m_activeState = PresentThreadState::pause; + std::this_thread::sleep_for(sleep_duration); + } + } + m_activeState = PresentThreadState::stop; +} + +void KRPresentationThread::renderFrame() +{ + // TODO - Eliminate this and use system wide index once Vulkan path is working + static uint64_t frameIndex = 0; + + // TODO - We should use fences to eliminate this mutex + const std::lock_guard surfaceLock(KRContext::g_SurfaceInfoMutex); + + unordered_map>& surfaces = m_pContext->GetSurfaces(); + + for (auto surfaceItr = surfaces.begin(); surfaceItr != surfaces.end(); surfaceItr++) { + KRSurface& surface = *(*surfaceItr).second; + KRDevice& device = m_pContext->GetDeviceInfo(surface.m_deviceHandle); + + uint32_t imageIndex = 0; + vkAcquireNextImageKHR(device.m_logicalDevice, surface.m_swapChain, UINT64_MAX, surface.m_imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex); + + // TODO - this will break with more than one surface... Expect to refactor this out + VkCommandBuffer commandBuffer = device.m_graphicsCommandBuffers[imageIndex]; + KRPipeline* testPipeline = m_pContext->getPipelineManager()->get("vulkan_test"); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; + beginInfo.pInheritanceInfo = nullptr; + + if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { + // TODO - Add error handling... + } + + VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} }; + + VkRenderPassBeginInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = testPipeline->getRenderPass(); + renderPassInfo.framebuffer = surface.m_swapChainFramebuffers[frameIndex % surface.m_swapChainFramebuffers.size()]; + renderPassInfo.renderArea.offset = { 0, 0 }; + renderPassInfo.renderArea.extent = surface.m_swapChainExtent; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, testPipeline->getPipeline()); + vkCmdDraw(commandBuffer, 3, 1, 0, 0); + vkCmdEndRenderPass(commandBuffer); + if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { + // TODO - Add error handling... + } + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = { surface.m_imageAvailableSemaphore }; + VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + VkSemaphore signalSemaphores[] = { surface.m_renderFinishedSemaphore }; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + if (vkQueueSubmit(device.m_graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) { + // TODO - Add error handling... + } + + VkPresentInfoKHR presentInfo{}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphores; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = &surface.m_swapChain; + presentInfo.pImageIndices = &imageIndex; + presentInfo.pResults = nullptr; + vkQueuePresentKHR(device.m_graphicsQueue, &presentInfo); + } + + frameIndex++; +} \ No newline at end of file diff --git a/kraken/KRPresentationThread.h b/kraken/KRPresentationThread.h new file mode 100644 index 0000000..3615915 --- /dev/null +++ b/kraken/KRPresentationThread.h @@ -0,0 +1,76 @@ +// +// KRPresentationThread.h +// Kraken Engine +// +// Copyright 2021 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KREngine-common.h" + +#include "KRContext.h" + +using std::map; +using std::vector; + +#include "KRPipeline.h" + +#ifndef KRPRESENTATIONTHREAD_H +#define KRPRESENTATIONTHREAD_H + +class KRPresentationThread : KRContextObject +{ +public: + KRPresentationThread(KRContext& context); + ~KRPresentationThread(); + void start(); + void stop(); + + enum class PresentThreadRequest + { + stop = 0, + run, + pause + }; + + std::atomic m_requestedState; + + enum class PresentThreadState + { + stop = 0, + run, + pause, + wait_recreate_swapchain + }; + std::atomic m_activeState; + +private: + std::thread m_thread; + void run(); + void renderFrame(); +}; + +#endif // KRPRESENTATIONTHREAD_H \ No newline at end of file