5#include "glm/gtc/type_ptr.inl"
10#include <GLFW/glfw3.h>
15#include <vulkan/vulkan_core.h>
17#ifdef RENDERDOC_ENABLED
18 #include "renderdoc_app.h"
48#include <plog/Appenders/ColorConsoleAppender.h>
49#include <plog/Appenders/RollingFileAppender.h>
50#include <plog/Formatters/TxtFormatter.h>
53#include <plog/Severity.h>
54#ifdef RENDERDOC_ENABLED
63#ifdef RENDERDOC_ENABLED
65 if (renderDoc.init()) {
66 renderDoc.printStatus();
67 renderDoc.setCapturePath(
"./vr_captures/debug_frame_{0}.rdc");
68 renderDoc.enableOverlay(
true);
74 this->
module = std::move(module);
84 this->
module->initUI(mirrorView->getGlfwWindow());
85 this->
module->startup();
87 PLOGI <<
"Shutting down module...";
88 this->
module->shutdown();
89 PLOGI <<
"Module shutdown complete. Destroying physics world...";
91 PLOGI <<
"Physics world destroyed. Starting cleanup...";
93 PLOGI <<
"Cleanup complete.";
98 throw std::runtime_error(
"Input handler does not exist yet");
136 throw std::runtime_error(
"Could not initialize glfw!");
138 std::cout <<
"GLFW has been intialized!" << std::endl;
140 if (!glfwVulkanSupported()) {
141 throw std::runtime_error(
"Glfw does not support vulkan");
143 std::cout <<
"GLFW supports vulkan!" << std::endl;
148 constexpr VkDebugUtilsMessageTypeFlagsEXT typeFlags = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
149 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
150 VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
151 constexpr VkDebugUtilsMessageSeverityFlagsEXT severityFlags =
152 VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
153 VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
155 const auto callback = [](VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
156 VkDebugUtilsMessageTypeFlagsEXT messageTypes,
157 const VkDebugUtilsMessengerCallbackDataEXT* callbackData,
void* userData) -> VkBool32
159 if (callbackData ==
nullptr || callbackData->pMessage ==
nullptr) {
165 const char* msg = callbackData->pMessage;
168 if (std::strstr(msg,
"vk_image_collection image") !=
nullptr &&
169 std::strstr(msg,
"accessMask is") !=
nullptr) {
175 if (std::strstr(msg,
"vkCmdPipelineBarrier()") !=
nullptr &&
176 std::strstr(msg,
"using VK_PIPELINE_STAGE_ALL_COMMANDS_BIT") !=
nullptr) {
182 if (std::strstr(msg,
"VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT") !=
nullptr) {
188 switch (messageSeverity) {
189 case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
190 DPLOGI <<
"Vulkan Debug Message: " << msg << std::endl;
192 case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
193 PLOGW <<
"[Vulkan] " << msg << std::endl;
195 case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
196 PLOGE <<
"[Vulkan] " << msg << std::endl;
198 case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
199 PLOGI <<
"[Vulkan] " << msg;
202 PLOGE <<
"This should never be called as the default case is enum_MAX";
203 PLOGF <<
"[Vulkan] " << msg << std::endl;
209 VkDebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfo{};
210 debugUtilsMessengerCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
211 debugUtilsMessengerCreateInfo.messageType = typeFlags;
212 debugUtilsMessengerCreateInfo.messageSeverity = severityFlags;
213 debugUtilsMessengerCreateInfo.pfnUserCallback = callback;
220 createInfo = { .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
223 .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
224 VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
225 VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
226 .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
227 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
228 VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
230 .pUserData =
nullptr };
234 VkDebugUtilsMessageTypeFlagsEXT messageType,
235 const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
243 if (pCallbackData->pMessageIdName !=
nullptr &&
244 std::strcmp(pCallbackData->pMessageIdName,
"VUID-vkCmdDraw-None-09600") == 0) {
248 if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
249 std::cerr <<
"validation layer: " << pCallbackData->pMessage << std::endl;
254 if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
255 PLOGI <<
"[SHADER_DEBUG] " << pCallbackData->pMessage;
264 vkEnumerateInstanceLayerProperties(&layerCount,
nullptr);
266 std::vector<VkLayerProperties> availableLayers(layerCount);
267 vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
270 bool layerFound =
false;
272 for (
const auto& layerProperties : availableLayers) {
273 if (strcmp(layerName, layerProperties.layerName) == 0) {
307#ifdef RENDERDOC_ENABLED
332 const auto & graphicsCommandBuffers =
renderer->getGraphicsCommandBuffers();
333 tracyVulkanContext.reserve( graphicsCommandBuffers.size() );
335 for ( VkCommandBuffer commandBuffer : graphicsCommandBuffers )
337 TracyVkCtx tracyContext = TracyVkContext(
344 tracyVulkanContext.push_back( tracyContext );
347 renderer->setupTracy( tracyVulkanContext );
350 PLOGI <<
"Finished init vulkan";
362 PLOGV << exception.what();
363 PLOGI <<
"Failed to acquire the headset! Retrying in 5 Seconds.";
368 std::this_thread::sleep_for(std::chrono::milliseconds(5000));
383 const double currentTime = glfwGetTime();
385 lastTime = currentTime;
396 auto* meshAsset =
assetManager->getMeshAsset( data.asset );
397 if (meshAsset ==
nullptr)
continue;
399 data.assetData = meshAsset;
425 for (
auto [entity, simulatesPhysics, transform] : physicsTickView.each())
428 if (simulatesPhysics.rigidBody ==
nullptr)
continue;
429 if (simulatesPhysics.isStatic)
continue;
431 if (simulatesPhysics.isKinematic)
434 btTrans.setFromOpenGLMatrix(glm::value_ptr(transform.matrix));
435 simulatesPhysics.rigidBody->getMotionState()->setWorldTransform(btTrans);
443 simulatesPhysics.rigidBody->getMotionState()->getWorldTransform(btTrans);
444 btTrans.getOpenGLMatrix(glm::value_ptr(transform.matrix));
453 for (
auto [entity, nodeRef] : dirtyView.each()) {
454 if (
auto node = nodeRef.node.lock()) {
455 for (
const auto& weakChild : node->getChildren()) {
456 if (
auto child = weakChild.lock()) {
457 child->markDirtyRecursive();
468 auto logicTickView = registry.view<
Ecs::Tick>();
471 for (
auto [entity, tick] : logicTickView.each())
474 tick.tickable->preTick();
478 module->tick( deltaTimeSeconds );
479 for (
auto [entity, tick] : logicTickView.each())
487 for (
auto [entity, tick] : logicTickView.each())
490 tick.tickable->postTick();
510#ifdef RENDERDOC_ENABLED
521 PLOGW <<
"Could not init renderdoc, capture failed!";
529 return glfwGetWindowAttrib(
mirrorView->getGlfwWindow(), GLFW_ICONIFIED );
543 vkWaitForFences(
applicationContext->getVkDevice(), 1, &graphicsFence, VK_TRUE, UINT64_MAX);
549 PLOGI <<
"[ASYNC] About to call endXrFrame()";
551 PLOGI <<
"[ASYNC] endXrFrame() completed";
558 PLOGI <<
"[ASYNC] About to call mirrorView->present()";
560 PLOGI <<
"[ASYNC] mirrorView->present() completed";
570 double lastTime = glfwGetTime();
577 PLOGF <<
"Draw call pointer not valid!";
579 while ( !glfwWindowShouldClose(
mirrorView->getGlfwWindow() ) && !
headset->getIsExitRequested() )
588 if ( glfwWindowShouldClose(
mirrorView->getGlfwWindow() ) )
594 uint32_t swapchainImageIndex = 0;
598 #if defined(HEADLESS)
601 waitFrameResult.
result = beginFrameResult;
604 #elif defined(COMPUTE_DEBUG)
607 waitFrameResult.
result = beginFrameResult;
614 beginFrameResult = waitFrameResult.
result;
645 beginFrameResult =
headset->beginXrFrameAfterWait( swapchainImageIndex );
652 XrTime predictedDisplayTime =
headset->getXrFrameState().predictedDisplayTime;
662 switch ( beginFrameResult )
666 THROW_ERROR(
"Failed to begin xr frame!" );
682 PLOGI <<
"Resuming rendering after skip - syncing timeline";
688 PLOGI <<
"Waiting for last frame in flight to complete";
736 auto pendingCompletions =
assetManager->getMeshAssetPipeline().takePendingMeshletCompletions();
737 renderer->getRenderingDataManager()->processCompletedMeshletGenerations(std::move(pendingCompletions));
743 renderer->getRenderingDataManager()->processPendingMeshLoads();
748 renderer->getRenderingDataManager()->updateIfDirty();
753 renderer->getRenderingDataManager()->updateTransforms();
759 renderer->getCurrentRenderProcess()->syncWithRenderingDataManager(
760 renderer->getRenderingDataManager().get());
780 renderer->renderToXr( swapchainImageIndex,
static_cast<float>(
gameTime ) );
783 VkSemaphore mirrorSemaphore = VK_NULL_HANDLE;
784 bool shouldPresentMirror =
false;
788 const bool skipAfterSync =
renderer->shouldSkipMirrorView();
789 const bool isMirrorVisible =
793 if ( isMirrorVisible )
795 auto mirrorResult =
mirrorView->render( swapchainImageIndex );
798 mirrorSemaphore = mirrorResult.imageAvailableSemaphore;
799 shouldPresentMirror =
true;
807 renderer->submitGraphics( swapchainImageIndex, mirrorSemaphore );
810 #ifdef RENDERDOC_ENABLED
826 VkFence graphicsFence =
renderer->getCurrentRenderProcess()->getGraphicsCompleteFence();
829 [
this, graphicsFence, shouldPresentMirror]() {
845 PLOGI <<
"Skipping render.";
852 PLOGI <<
"Skipping frame fully.";
855 std::this_thread::sleep_for(std::chrono::microseconds(8300));
861 PLOGI <<
"[COMPUTE_DEBUG] Running compute-only frame";
910 auto pendingCompletions =
assetManager->getMeshAssetPipeline().takePendingMeshletCompletions();
911 renderer->getRenderingDataManager()->processCompletedMeshletGenerations(std::move(pendingCompletions));
917 renderer->getRenderingDataManager()->processPendingMeshLoads();
923 renderer->getRenderingDataManager()->updateIfDirty();
928 renderer->getRenderingDataManager()->updateTransforms();
934 renderer->getCurrentRenderProcess()->syncWithRenderingDataManager(
935 renderer->getRenderingDataManager().get());
964 renderer->submitGraphics( 0, VK_NULL_HANDLE );
974 std::this_thread::sleep_for( std::chrono::milliseconds( 8 ) );
980 previousFrameResult = beginFrameResult;
984 PLOGI <<
"Exiting main loop";
988 PLOGI <<
"Engine::cleanup() started";
992 PLOGI <<
"Waiting for async frame completion to finish...";
994 PLOGI <<
"Async frame completion finished";
1002 PLOGI <<
"Signaling timeline semaphore to unblock pending GPU work...";
1003 const auto& timelineSync =
renderer->getTimelineSynchronizer();
1004 uint64_t currentValue = timelineSync.getCurrentValue();
1009 PLOGI <<
"Timeline semaphore: current=" << currentValue <<
", signaling=" << unblockValue;
1010 timelineSync.signalValue(unblockValue);
1015 PLOGI <<
"Waiting for in-flight fences...";
1017 std::vector<VkFence> fences;
1019 if (process->getTransferFenceSubmitted()) {
1020 fences.push_back(process->getTransferCompleteFence());
1022 if (process->getGraphicsFenceSubmitted()) {
1023 fences.push_back(process->getGraphicsCompleteFence());
1026 if (!fences.empty()) {
1027 PLOGI <<
"Waiting for " << fences.size() <<
" fences...";
1028 VkResult result = vkWaitForFences(
1030 static_cast<uint32_t
>(fences.size()),
1035 if (result == VK_TIMEOUT) {
1036 PLOGW <<
"Fence wait timed out - GPU work may be stuck";
1042 PLOGI <<
"Waiting for device to be idle...";
1044 PLOGI <<
"Device is idle";
1047 PLOGI <<
"Cleaning up scene manager...";
1052 PLOGI <<
"Cleaning up asset manager...";
1057 PLOGI <<
"Cleaning up renderer...";
1062 PLOGI <<
"Cleaning up headset...";
1067 PLOGI <<
"Cleaning up mirror view...";
1093 auto now = std::chrono::system_clock::now();
1094 auto time = std::chrono::system_clock::to_time_t(now);
1095 std::ostringstream oss;
1096 oss <<
"GameLog_" << std::put_time(std::localtime(&time),
"%Y-%m-%d_%H-%M-%S") <<
".log";
1097 static std::string logFileName = oss.str();
1099 static plog::RollingFileAppender<plog::TxtFormatter> fileAppender(logFileName.c_str());
1100 static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
1101 plog::init<PLOG_DEFAULT_INSTANCE_ID>(plog::Severity::info, &fileAppender).addAppender(&consoleAppender);
1121 PLOGI <<
"Total mesh count after loading is now: " <<
assetManager->getAllMeshes().size();
const std::vector< const char * > validationLayers
constexpr bool ARE_VALIDATION_LAYERS_ENABLED
constexpr GPU_DEBUGGING_MODE USE_GPU_PRINTF
#define TRACY_ZONE_SCOPED_NAMED_ASSET(name)
#define TRACY_ZONE_SCOPED_FUNCTION
#define TRACY_ZONE_SCOPED_NAMED(name)
static entt::registry & get()
Gets the registry for all components.
The application context is the core class which stores the basic openxr and vulkan objects.
void setEngine(Engine *engineInstance)
set the content pointer for this singleton
EngineCore::Engine * getEngineModule()
gets the pointer to the engine object
static EngineManager & getInstance()
gets a reference to the engine manager
EngineCore::Engine * engine
void initVulkan()
Initialisation of all vulkan related resources.
std::shared_ptr< InputHandler > inputHandler
std::unique_ptr< AssetManager > assetManager
void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT &createInfo)
const std::unique_ptr< AssetManager > & getAssetManager() const
bool checkValidationLayerSupport()
void fixLoadingAssets() const
When assets have been requested but are not loaded yet the static mesh data is in a limbo where it ha...
int isMirrorViewMinimized()
std::future< void > frameCompletionFuture_
void tryCreateApplicationContext()
will try to create a window and if there is no openxr runtime detected it will wait 5 seconds and try...
void createAssetManager()
void completeFrameAsync(VkFence graphicsFence, bool presentMirror)
Runs on background thread: waits for GPU fence, ends XR frame, presents mirror This allows CPU work t...
std::atomic< bool > previousFrameXrComplete_
static VkBool32 debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData)
void cleanup()
Cleanup of pointers for vulkan and subsystems.
VkDebugUtilsMessengerEXT debugMessenger
Renderer * getRenderer() const
Getter for the renderer.
void createPhysicsWorld()
PHYSICS.
void calculateFrameTime(double &lastTime)
double getGlobalTimeSeconds()
Gets the time since the beginning of the simulation (sum of all delta times)
void update(double &gameTime)
void mainLoop()
Main engine loop where the rendering and game steps happen.
ApplicationContext * applicationContext
PhysicsEngine * physicsEngine
std::unique_ptr< SceneManager > sceneManager
double getDeltaTimeSeconds()
gets the time between frames
void processResourceLoadingPipelines()
Collects and processes the pipelines which have finished and dispatches the new steps....
Input::XrInputHandler * xrInputHandler
void processMirrorWindowEvents()
processes glfw window events like key presses or window events
PhysicsEngine * getPhysicsEngine() const
Getter for the pointer to the physics engine.
void createInputManager()
void initializePlogDebugger()
uint32_t getRenderableSceneObjectCount() const
Counts the amount of entities which have renderable components.
std::unique_ptr< GameModule > module
void captureRenderdocFrame()
Captures a frame for render doc if the define RENDERDOC_ENABLED is set.
void run(std::unique_ptr< GameModule > module)
Runs the engine. Is the entry point for this module.
void createVulkanDebugMessenger()
void initRenderDoc()
Initialization point for software driven captures.
void createStartupScene()
Creates the initial scene the engine loads before an actual level is loaded.
std::shared_ptr< InputHandler > getInputHandler()
Gets a shared handle to the input handler. This is used for any type of input.
void destroyPhysicsWorld()
std::unique_ptr< NamedThreadPool > xrFrameThreadPool_
This class houses the components needed to display a vr image on the desktop.
The physics engine manages creating and destroying physics objects for the physics simulation.
void update(float deltaTimeSeconds)
Steps the physics simulation by 1/120th of a second. The idea of the engine is that it should run on ...
The render process class consolidates all the resources that needs to be duplicated for each frame th...
bool getGraphicsFenceSubmitted() const
VkFence & getGraphicsCompleteFence()
bool getTransferFenceSubmitted() const
void setGraphicsFenceSubmitted(bool isSubmitted)
VkFence & getTransferCompleteFence()
void setTransferFenceSubmitted(bool isSubmitted)
static void initializeFunctions(VkInstance instance, VkDevice device)
static const VulkanFunctions & getFunctions()
Log category system implementation.
constexpr uint32_t PIPELINE_STAGE_COUNT
Is a tag for asset loading. When an asset has been rquested for loading but isnt yet created this tag...
A struct which allows the engine to find out which SceneNode needs to propagate a dirty flag down the...
Tag for EnTT which tags entities which should simulate physics.
Tag for everything which wants to receive tick events.
Result from waitForXrFrame indicating what the main loop should do.
bool shouldCallBeginFrame
PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT