From 3b128315e73c42d1e5fa3dc536e4faea025b3274 Mon Sep 17 00:00:00 2001 From: kotofyt Date: Sat, 30 May 2026 20:43:02 +0300 Subject: [PATCH] work on basic VR --- .gitmodules | 3 + engine/build.cpp | 5 + engine/engine.cpp | 2 +- engine/openxr.cpp | 498 ++++++++++++++++++++ engine/xr.cpp | 0 external/OpenXR-SDK | 1 + game/client/game.cpp | 23 + game/shared/game.cpp | 1 + game/shared/game.h | 2 + launcher/launcher.cpp | 2 - materialsystem/vulkan/commands.h | 3 +- materialsystem/vulkan/commands/draw.cpp | 11 +- materialsystem/vulkan/device_extensions.h | 1 + materialsystem/vulkan/rendercommandlist.cpp | 59 ++- materialsystem/vulkan/rendercontext.cpp | 67 ++- materialsystem/vulkan/vulkan_state.h | 7 + public/ixr.h | 37 +- public/materialsystem/imaterialsystem.h | 23 +- 18 files changed, 716 insertions(+), 29 deletions(-) create mode 100644 engine/openxr.cpp delete mode 100644 engine/xr.cpp create mode 160000 external/OpenXR-SDK diff --git a/.gitmodules b/.gitmodules index 043dbf8..d71f248 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,3 +34,6 @@ [submodule "external/SPIRV-Headers"] path = external/SPIRV-Headers url = https://github.com/KhronosGroup/SPIRV-Headers +[submodule "external/OpenXR-SDK"] + path = external/OpenXR-SDK + url = https://github.com/KhronosGroup/OpenXR-SDK diff --git a/engine/build.cpp b/engine/build.cpp index 49f196b..3fbe077 100644 --- a/engine/build.cpp +++ b/engine/build.cpp @@ -30,6 +30,8 @@ DECLARE_BUILD_STAGE(engine) "miniaudio.cpp", "kottui.cpp", + + "openxr.cpp", }; if ( GET_PROJECT_VALUE(config, "steam") == "true" ) @@ -43,6 +45,8 @@ DECLARE_BUILD_STAGE(engine) "../external/cglm/include", FUNNYSTDLIB"public", EXTERNAL"steamworks/public", + EXTERNAL"Vulkan-Headers/include", + EXTERNAL"Vulkan-Utility-Libraries/include", EXTERNAL }; if ( GET_PROJECT_VALUE(config, "steam") == "true" ) @@ -81,6 +85,7 @@ DECLARE_BUILD_STAGE(engine) ldProject.libraries.AppendTail("shell32"); ldProject.libraries.AppendTail("uuid"); }; + ldProject.libraries.AppendTail("openxr_loader"); CUtlString outputProject = linker->Link(&ldProject); diff --git a/engine/engine.cpp b/engine/engine.cpp index 4569805..b81c7be 100644 --- a/engine/engine.cpp +++ b/engine/engine.cpp @@ -101,7 +101,7 @@ extern "C" void FunnyMain( int argc, char **argv ) pWindow->SetAxisCallback(CallAxisEvent); g_pRenderContext = (IRenderContext*)pRenderSystemFactory(RENDER_CONTEXT_INTERFACE_VERSION, NULL); - g_pRenderContext->SetMainWindowManager(g_pWindowManager); + g_pRenderContext->ConnectInterface(g_pWindowManager, GAME_WINDOW_MANAGER_INTERFACE_VERSION); g_pRenderContext->Init(); g_pRenderContext->RegisterGameWindow(pWindow); diff --git a/engine/openxr.cpp b/engine/openxr.cpp new file mode 100644 index 0000000..7e18338 --- /dev/null +++ b/engine/openxr.cpp @@ -0,0 +1,498 @@ +#include "ixr.h" +#include "openxr/openxr.h" +#include "vulkan/vulkan_core.h" +#undef __cplusplus +#include "vulkan/vk_enum_string_helper.h" +#define __cplusplus 202400L +#define XR_USE_GRAPHICS_API_VULKAN +#include "openxr/openxr_platform.h" +#include "tier0/lib.h" +#include "tier1/utlvector.h" +#include "materialsystem/imaterialsystem.h" + +struct EyeSwapchain_t +{ + XrSwapchain m_swapchain; + uint32_t m_uWidth; + uint32_t m_uHeight; + uint32_t m_uImageIndex; + CUtlVector m_pImages; + IImage *m_pUserImage; +}; + +class COpenXRManager; +class COpenXRHeadset: public IXRHeadset, public IAppSystem +{ +public: + virtual void Init() override; + virtual void Shutdown() override; + + virtual uint32_t GetSurfaceCount() override; + virtual XRRenderSurface_t GetSurface( uint32_t i ) override; + virtual void SetSurfaceImage( uint32_t i, IImage *pImage ) override; + + void Frame(); + void BeforeRender(); + void AfterRender(); + void CopySwapchain(); + void CreateSwapchainForEye( XrViewConfigurationView *pConf, VkFormat eVkFormat, EyeSwapchain_t *pEye ); + + COpenXRManager *m_pManager; + XrSystemId m_systemId; + XrSession m_session; + XrSpace m_space; + + CUtlVector m_eyeSwapchains; + CUtlVector m_projviews; + CUtlVector m_views; + float m_fPredictedTime; + +}; + +class COpenXRController: public IXRController +{ + +}; + +class COpenXRManager: public IXRManager +{ +public: + virtual void Init() override; + virtual void Shutdown() override; + virtual void ConnectInterface( void *pIface, const char *szName ) override; + virtual void Frame() override; + virtual void PreRender() override; + virtual void PostRender() override; + virtual void CopySwapchain() override; + + virtual uint32_t GetHeadsetCount() override; + virtual IXRHeadset *GetHeadset( uint32_t i ) override; + + IRenderContext *m_pRenderContext; + COpenXRHeadset m_headset; + + XrInstance m_instance; +}; +EXPOSE_INTERFACE(COpenXRManager, IXRManager, OPEN_XR_INTERFACE_VERSION); + +void COpenXRManager::Init() +{ + PFN_xrInitializeLoaderKHR fnInitLoader = NULL; + XrResult r = xrGetInstanceProcAddr(NULL, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)&fnInitLoader); + if (r != XR_SUCCESS) + Plat_FatalErrorFunc("Failed to load OpenXR"); + XrApplicationInfo appInfo = {}; + appInfo.apiVersion = XR_API_VERSION_1_1; + V_strncpy(appInfo.applicationName, "funny", XR_MAX_APPLICATION_NAME_SIZE); + V_strncpy(appInfo.engineName, "funny", XR_MAX_ENGINE_NAME_SIZE); + if (r != XR_SUCCESS) + Plat_FatalErrorFunc("Failed to load OpenXR\n"); + const char *extensions[] = { + XR_EXT_DEBUG_UTILS_EXTENSION_NAME, + XR_KHR_VULKAN_ENABLE_EXTENSION_NAME, + }; + + const char *layers[] = { + "XR_APILAYER_LUNARG_core_validation" + }; + + + XrInstanceCreateInfo createInfo = {}; + createInfo.type = XR_TYPE_INSTANCE_CREATE_INFO; + createInfo.applicationInfo = appInfo; + createInfo.enabledExtensionCount = sizeof(extensions)/sizeof(*extensions); + createInfo.enabledExtensionNames = extensions; + createInfo.enabledApiLayerCount = sizeof(layers)/sizeof(*layers); + createInfo.enabledApiLayerNames = layers; + r = xrCreateInstance(&createInfo, &m_instance); + if (r != XR_SUCCESS) + Plat_FatalErrorFunc("xrCreateInstance\n"); + + m_headset.m_pManager = this; + m_headset.Init(); + + XrInstanceProperties props = {}; + props.type = XR_TYPE_INSTANCE_PROPERTIES; + xrGetInstanceProperties(m_instance, &props); + V_printf("OpenXR runtime: %s\n", props.runtimeName); + +} + +void COpenXRManager::Shutdown() +{ + +} +void COpenXRManager::Frame() +{ + m_headset.Frame(); +} + +void COpenXRManager::PreRender() +{ + m_headset.BeforeRender(); +} + +void COpenXRManager::PostRender() +{ + m_headset.AfterRender(); +} +void COpenXRManager::CopySwapchain() +{ + m_headset.CopySwapchain(); + +} + + +void COpenXRHeadset::Init() +{ + + XrSystemGetInfo sgi = {}; + XrResult r; + sgi.type = XR_TYPE_SYSTEM_GET_INFO; + sgi.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + r = xrGetSystem(m_pManager->m_instance, &sgi, &m_systemId); + if (r != XR_SUCCESS) + Plat_FatalErrorFunc("xrGetSystem\n"); + + + XrGraphicsRequirementsVulkanKHR requirements = {}; + requirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR; + PFN_xrGetVulkanGraphicsRequirementsKHR fnVulkanRequirements = NULL; + xrGetInstanceProcAddr(m_pManager->m_instance, "xrGetVulkanGraphicsRequirementsKHR", (PFN_xrVoidFunction*)&fnVulkanRequirements); + if (r != XR_SUCCESS) + Plat_FatalErrorFunc("Failed to get xrGetVulkanGraphicsRequirementsKHR\n"); + fnVulkanRequirements( m_pManager->m_instance, m_systemId, &requirements); + + + PFN_xrGetVulkanGraphicsDeviceKHR fnVulkanDevice = NULL; + xrGetInstanceProcAddr(m_pManager->m_instance, "xrGetVulkanGraphicsDeviceKHR", (PFN_xrVoidFunction*)&fnVulkanDevice); + if (r != XR_SUCCESS) + Plat_FatalErrorFunc("Failed to get xrGetVulkanGraphicsDeviceKHR\n"); + VkPhysicalDevice d = NULL; + fnVulkanDevice(m_pManager->m_instance, m_systemId, (VkInstance)m_pManager->m_pRenderContext->GetVulkanInstance(), &d); + + XrGraphicsBindingVulkan2KHR vkBinding = {}; + vkBinding.type = XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR; + vkBinding.instance = (VkInstance)m_pManager->m_pRenderContext->GetVulkanInstance(); + vkBinding.physicalDevice = (VkPhysicalDevice)m_pManager->m_pRenderContext->GetVulkanPhysicalDevice(); + vkBinding.device = (VkDevice)m_pManager->m_pRenderContext->GetVulkanDevice(); + + XrSessionCreateInfo sessionCreateInfo = {}; + sessionCreateInfo.type = XR_TYPE_SESSION_CREATE_INFO; + sessionCreateInfo.next = &vkBinding; + sessionCreateInfo.systemId = m_systemId; + r = xrCreateSession(m_pManager->m_instance, &sessionCreateInfo, &m_session); + if (r != XR_SUCCESS) + Plat_FatalErrorFunc("xrCreateSession\n"); + + /* create swapchains for rendering pipeline */ + + uint32_t formatCount = 0; + xrEnumerateSwapchainFormats(m_session, 0, &formatCount, NULL); + + CUtlVector formats = formatCount; + xrEnumerateSwapchainFormats(m_session, formatCount, &formatCount, formats.GetData()); + + const VkFormat preferedSurfaceFormats[] = { + VK_FORMAT_B8G8R8A8_UNORM, + VK_FORMAT_R8G8B8A8_UNORM, + VK_FORMAT_R8G8B8A8_SRGB, + }; + + VkFormat eSelectedFormat = preferedSurfaceFormats[0]; + for (auto &format: formats) + { + V_printf("Format: %s\n", string_VkFormat((VkFormat)format)); + for (int i = 0; i < sizeof(preferedSurfaceFormats)/sizeof(VkFormat); i++) + { + if (format == preferedSurfaceFormats[i]) + { + eSelectedFormat = preferedSurfaceFormats[i]; + goto formatPicked; + } + } + } +formatPicked: + + uint32_t viewCount = 0; + xrEnumerateViewConfigurationViews(m_pManager->m_instance, m_systemId, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &viewCount, NULL); + CUtlVector views = viewCount; + for ( auto &v: views ) + { + v.type = XR_TYPE_VIEW_CONFIGURATION_VIEW; + } + xrEnumerateViewConfigurationViews(m_pManager->m_instance, m_systemId, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, viewCount, &viewCount, views.GetData()); + + for ( auto &v: views ) + { + m_eyeSwapchains.AppendTail((EyeSwapchain_t){}); + CreateSwapchainForEye(&v, eSelectedFormat, m_eyeSwapchains.GetData() + m_eyeSwapchains.GetSize() - 1); + } + + XrReferenceSpaceCreateInfo spaceInfo = {}; + spaceInfo.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO; + spaceInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + spaceInfo.poseInReferenceSpace = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}}; + xrCreateReferenceSpace(m_session, &spaceInfo, &m_space); + m_fPredictedTime = 0; + m_views = m_eyeSwapchains.GetSize(); + m_projviews = m_eyeSwapchains.GetSize(); + for ( auto &v: m_views ) + { + v = {}; + v.type = XR_TYPE_VIEW; + } + for ( auto &v: m_projviews ) + { + v = {}; + v.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + } + + XrViewState viewState = {}; + viewState.type = XR_TYPE_VIEW_STATE; + XrViewLocateInfo viewLocale = {}; + viewLocale.type = XR_TYPE_VIEW_LOCATE_INFO; + viewLocale.displayTime = m_fPredictedTime; + viewLocale.space = m_space; + viewLocale.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + uint32_t vc = m_views.GetSize(); + xrLocateViews(m_session, &viewLocale, &viewState, vc, &vc, m_views.GetData()); + +} +void COpenXRHeadset::Shutdown() +{ + +} + +void COpenXRHeadset::Frame() +{ + while (true) + { + XrEventDataBuffer event = {}; + event.type = XR_TYPE_EVENT_DATA_BUFFER; + XrResult r = xrPollEvent(m_pManager->m_instance, &event); + if ( r != XR_SUCCESS ) + break; + + switch (event.type) + { + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: + { + XrEventDataSessionStateChanged *e = (XrEventDataSessionStateChanged*)&event; + if ( e->state == XR_SESSION_STATE_READY ) + { + XrSessionBeginInfo beginInfo = {}; + beginInfo.type = XR_TYPE_SESSION_BEGIN_INFO; + beginInfo.primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + xrBeginSession(m_session, &beginInfo); + } + if ( e->state == XR_SESSION_STATE_STOPPING ) + { + xrEndSession(m_session); + } + break; + } + default: + break; + } + + } + + XrFrameWaitInfo frameWait = {}; + XrFrameState frameState = {}; + frameState.type = XR_TYPE_FRAME_STATE; + frameWait.type = XR_TYPE_FRAME_WAIT_INFO; + xrWaitFrame(m_session, &frameWait, &frameState); + m_fPredictedTime = frameState.predictedDisplayTime; + + + m_views = m_eyeSwapchains.GetSize(); + m_projviews = m_eyeSwapchains.GetSize(); + for ( auto &v: m_views ) + { + v = {}; + v.type = XR_TYPE_VIEW; + } + for ( auto &v: m_projviews ) + { + v = {}; + v.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + } + + XrViewState viewState = {}; + viewState.type = XR_TYPE_VIEW_STATE; + XrViewLocateInfo viewLocale = {}; + viewLocale.type = XR_TYPE_VIEW_LOCATE_INFO; + viewLocale.displayTime = m_fPredictedTime; + viewLocale.space = m_space; + viewLocale.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + uint32_t viewCount = m_views.GetSize(); + xrLocateViews(m_session, &viewLocale, &viewState, viewCount, &viewCount, m_views.GetData()); +} + +void COpenXRHeadset::BeforeRender() +{ + + XrFrameBeginInfo begin = {}; + begin.type = XR_TYPE_FRAME_BEGIN_INFO; + xrBeginFrame(m_session, &begin); + for ( uint32_t i = 0; i < m_views.GetSize(); i++ ) + { + XrSwapchainImageAcquireInfo acq = {}; + acq.type = XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO; + uint32_t idx = 0; + XrResult r = xrAcquireSwapchainImage(m_eyeSwapchains[i].m_swapchain, &acq, &idx); + m_eyeSwapchains[i].m_uImageIndex = idx; + if (r) return; + XrSwapchainImageWaitInfo wait = {}; + wait.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO; + wait.timeout = XR_INFINITE_DURATION; + xrWaitSwapchainImage(m_eyeSwapchains[i].m_swapchain, &wait); + + m_projviews[i].fov = m_views[i].fov; + m_projviews[i].pose = m_views[i].pose; + m_projviews[i].subImage.swapchain = m_eyeSwapchains[i].m_swapchain; + m_projviews[i].subImage.imageRect.extent.height = m_eyeSwapchains[i].m_pImages[idx]->GetImageWidth(); + m_projviews[i].subImage.imageRect.extent.width = m_eyeSwapchains[i].m_pImages[idx]->GetImageHeight(); + m_projviews[i].subImage.imageArrayIndex = 0; + } + +} +void COpenXRHeadset::CopySwapchain() +{ + IRenderContext *pCtx = m_pManager->m_pRenderContext; + IRenderCommandList *pList = pCtx->CreateCommandList(); + pList->StartRecording(); + for ( auto &eye: m_eyeSwapchains) + { + ImageSector_t sector = {}; + sector.m_iWidth = eye.m_uWidth; + sector.m_iHeight = eye.m_uHeight; + pList->BlitImageToImage(eye.m_pUserImage, sector, eye.m_pImages[eye.m_uImageIndex], sector); + } + pList->EndRecording(); + pCtx->SubmitCommandList(pList); + pCtx->DestroyCommandList(pList); +} + +void COpenXRHeadset::AfterRender() +{ + for ( uint32_t i = 0; i < m_views.GetSize(); i++ ) + { + XrSwapchainImageReleaseInfo rel = {}; + rel.type = XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO; + xrReleaseSwapchainImage(m_eyeSwapchains[i].m_swapchain, &rel); + } + + XrCompositionLayerProjection proj = {}; + proj.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; + proj.space = m_space; + proj.viewCount = m_projviews.GetSize(); + proj.views = m_projviews.GetData(); + XrCompositionLayerBaseHeader *compositions[] = { + (XrCompositionLayerBaseHeader*)&proj + }; + + XrFrameEndInfo end = {}; + end.type = XR_TYPE_FRAME_END_INFO; + end.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + end.layerCount = 1; + end.layers = compositions; + end.displayTime = m_fPredictedTime; + xrEndFrame(m_session, &end); +} + +uint32_t COpenXRHeadset::GetSurfaceCount() +{ + V_printf("%i\n", m_eyeSwapchains.GetSize()); + return m_eyeSwapchains.GetSize(); +} + +XRRenderSurface_t COpenXRHeadset::GetSurface( uint32_t i ) +{ + XRRenderSurface_t surface = {}; + surface.m_fFov = m_projviews[i].fov.angleLeft+m_projviews[i].fov.angleRight; + surface.m_uWidth = m_eyeSwapchains[i].m_uWidth; + surface.m_uHeight = m_eyeSwapchains[i].m_uHeight; + return surface; +} + +void COpenXRHeadset::SetSurfaceImage( uint32_t i, IImage *pImage ) +{ + m_eyeSwapchains[i].m_pUserImage = pImage; + +} + +void COpenXRHeadset::CreateSwapchainForEye( XrViewConfigurationView *pConf, VkFormat eVkFormat, EyeSwapchain_t *pEye ) +{ + XrSwapchainCreateInfo createInfo = {}; + createInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO; + createInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_TRANSFER_DST_BIT; + createInfo.format = eVkFormat; + createInfo.sampleCount = pConf->recommendedSwapchainSampleCount; + createInfo.width = pConf->recommendedImageRectWidth; + createInfo.height = pConf->recommendedImageRectHeight; + createInfo.faceCount = 1; + createInfo.arraySize = 1; + createInfo.mipCount = 1; + XrResult r = xrCreateSwapchain(m_session, &createInfo, &pEye->m_swapchain); + if (r != XR_SUCCESS) + Plat_FatalErrorFunc("xrCreateSwapchain\n"); + pEye->m_uWidth = pConf->recommendedImageRectWidth; + pEye->m_uHeight = pConf->recommendedImageRectHeight; + + + + + + uint32_t imageCount = 0; + xrEnumerateSwapchainImages(pEye->m_swapchain, 0, &imageCount, NULL); + CUtlVector images = imageCount; + for ( auto &i: images ) + { + i.type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN2_KHR; + } + xrEnumerateSwapchainImages(pEye->m_swapchain, imageCount, &imageCount, (XrSwapchainImageBaseHeader*)images.GetData()); + pEye->m_pImages = imageCount; + + EImageFormat eFormat = IMAGE_FORMAT_RGBA8_UNORM; + switch(eVkFormat) + { + case VK_FORMAT_R8G8B8A8_UNORM: eFormat = IMAGE_FORMAT_RGBA8_UNORM; break; + case VK_FORMAT_B8G8R8A8_UNORM: eFormat = IMAGE_FORMAT_BGRA8_UNORM; break; + case VK_FORMAT_R8G8B8A8_SRGB: eFormat = IMAGE_FORMAT_RGBA8_SRGB; break; + default: + break; + + } + for ( int i = 0; i < imageCount; i++ ) + { + pEye->m_pImages[i] = m_pManager->m_pRenderContext->CreateImageFromVkImage( + images[i].image, + createInfo.width, + createInfo.height, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + eFormat, + MULTISAMPLE_TYPE_1_SAMPLES); + } + + +} + +uint32_t COpenXRManager::GetHeadsetCount() +{ + return 1; +} + +IXRHeadset *COpenXRManager::GetHeadset( uint32_t i ) +{ + if (i) + return 0; + return &m_headset; +} + +void COpenXRManager::ConnectInterface( void *pIface, const char *szName ) +{ + if (!V_strcmp(szName, RENDER_CONTEXT_INTERFACE_VERSION)) + m_pRenderContext = (IRenderContext*)pIface; +} diff --git a/engine/xr.cpp b/engine/xr.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/external/OpenXR-SDK b/external/OpenXR-SDK new file mode 160000 index 0000000..64f2b37 --- /dev/null +++ b/external/OpenXR-SDK @@ -0,0 +1 @@ +Subproject commit 64f2b37c8c6da3d83c9b4d11865ba1fb752cb8ec diff --git a/game/client/game.cpp b/game/client/game.cpp index 1274601..b35ea31 100644 --- a/game/client/game.cpp +++ b/game/client/game.cpp @@ -73,10 +73,31 @@ void CFunnyGameBridge::Init() CreateInterfaceFn fnEngineFactory = GetEngineFactory(); g_pKotUI = (IKotUIManager*)fnEngineFactory(KOT_UI_INTEFACE_VERSION, NULL); g_pAudioManager = (IAudioManager*)fnEngineFactory(AUDIO_MANAGER_INTERFACE_VERSION, NULL); + g_pXRManager = (IXRManager*)fnEngineFactory(OPEN_XR_INTERFACE_VERSION, NULL); + g_pAudioManager->Init(); g_pKotUI->ConnectInterface(g_pRenderContext, RENDER_CONTEXT_INTERFACE_VERSION); g_pKotUI->Init(); + + g_pXRManager->ConnectInterface(g_pRenderContext, RENDER_CONTEXT_INTERFACE_VERSION); + g_pXRManager->Init(); + g_pRenderContext->ConnectInterface(g_pXRManager, XR_INTERFACE_VERSION); + + + IXRHeadset *pHeadSet = g_pXRManager->GetHeadset(0); + for ( uint32_t i = 0; i < pHeadSet->GetSurfaceCount(); i++ ) + { + XRRenderSurface_t surface = pHeadSet->GetSurface(0); + IImage *pImage = g_pRenderContext->CreateRenderTarget(surface.m_uWidth, surface.m_uHeight, IMAGE_FORMAT_RGBA8_UNORM, MULTISAMPLE_TYPE_1_SAMPLES); + pHeadSet->SetSurfaceImage(i, pImage); + IRenderCommandList *pDoSomething = g_pRenderContext->CreateCommandList(); + pDoSomething->StartRecording(); + pDoSomething->ClearImage(pImage, 1,0,0.5,1); + pDoSomething->EndRecording(); + g_pRenderContext->SubmitCommandList(pDoSomething); + g_pRenderContext->DestroyCommandList(pDoSomething); + } } void CFunnyGameBridge::Tick( float fDelta ) @@ -173,6 +194,8 @@ void CFunnyGameBridge::Frame( float fDelta ) EntitySystem()->NetSendThink(pCurrentServer); } g_pWorldRenderer->Frame(fDelta); + + g_pXRManager->Frame(); } void CFunnyGameBridge::Shutdown() diff --git a/game/shared/game.cpp b/game/shared/game.cpp index edcee56..31f28a6 100644 --- a/game/shared/game.cpp +++ b/game/shared/game.cpp @@ -23,6 +23,7 @@ IPhysicsWorld *g_pPhysicsWorld; IKotUIManager *g_pKotUI; IAudioManager *g_pAudioManager; +IXRManager *g_pXRManager; CreateInterfaceFn GetEngineFactory() { diff --git a/game/shared/game.h b/game/shared/game.h index 762d9e3..3b20380 100644 --- a/game/shared/game.h +++ b/game/shared/game.h @@ -11,6 +11,7 @@ #include "ihumandevice.h" #include "kottui/kottui.h" #include "iaudio.h" +#include "ixr.h" class CEngineVars { @@ -44,6 +45,7 @@ extern IPhysicsWorld *g_pPhysicsWorld; extern IKotUIManager *g_pKotUI; extern IAudioManager *g_pAudioManager; +extern IXRManager *g_pXRManager; #define FUNNY_SECURE_PORT 27015 #define FUNNY_QUERY_PORT 27016 diff --git a/launcher/launcher.cpp b/launcher/launcher.cpp index f05fecd..533171c 100644 --- a/launcher/launcher.cpp +++ b/launcher/launcher.cpp @@ -38,8 +38,6 @@ void *pTier0Lib = NULL; typedef void (*EngineMainFn)(int argc, char** argv); EngineMainFn pEngineMain; -extern "C" void FunnyMain( int argc, char **argv ); - int main( int argc, char **argv ) { #ifdef __linux__ readlink("/proc/self/exe",szLauncherPath, MAX_PATH); diff --git a/materialsystem/vulkan/commands.h b/materialsystem/vulkan/commands.h index b161a78..8804c57 100644 --- a/materialsystem/vulkan/commands.h +++ b/materialsystem/vulkan/commands.h @@ -21,8 +21,7 @@ BEGIN_VULKAN_COMMAND(ClearColor) float g; float b; float a; - IImage *pImage = NULL; - IImage **ppSwapchainImages = NULL; + VkFrameObject_t stImage; END_VULKAN_COMMAND(ClearColor) BEGIN_VULKAN_COMMAND(Begin) diff --git a/materialsystem/vulkan/commands/draw.cpp b/materialsystem/vulkan/commands/draw.cpp index 21833c8..66299d2 100644 --- a/materialsystem/vulkan/commands/draw.cpp +++ b/materialsystem/vulkan/commands/draw.cpp @@ -10,22 +10,13 @@ DECLARE_VULKAN_COMMAND(ClearColor) CVkImage *pImg; - if (pImage) - pImg = (CVkImage*)pImage; - if (ppSwapchainImages) - pImg = (CVkImage*)ppSwapchainImages[iCurrentFrame]; - - if (pImg == NULL) - Plat_FatalErrorFunc("pImage and *ppSwapchainImages are NULL\n"); - - VkClearColorValue color = {.float32 = {r,g,b,a}}; VkImageSubresourceRange range = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = 1, .layerCount = 1, }; - vkCmdClearColorImage(hCommandBuffer, pImg->m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &color, 1, &range); + vkCmdClearColorImage(hCommandBuffer, ((CVkImage*)VulkanGetObject(stImage, iCurrentFrame))->m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &color, 1, &range); } DECLARE_VULKAN_COMMAND(Begin) diff --git a/materialsystem/vulkan/device_extensions.h b/materialsystem/vulkan/device_extensions.h index 30b60b5..71679cc 100644 --- a/materialsystem/vulkan/device_extensions.h +++ b/materialsystem/vulkan/device_extensions.h @@ -1,6 +1,7 @@ REQUIRED_EXTENSION(VK_KHR_SWAPCHAIN) REQUIRED_EXTENSION(VK_KHR_PIPELINE_LIBRARY) REQUIRED_EXTENSION(VK_EXT_GRAPHICS_PIPELINE_LIBRARY) +REQUIRED_EXTENSION(VK_KHR_EXTERNAL_FENCE_CAPABILITIES) OPTIONAL_EXTENSION(VK_KHR_RAY_TRACING_PIPELINE) OPTIONAL_EXTENSION(VK_KHR_ACCELERATION_STRUCTURE) OPTIONAL_EXTENSION(VK_KHR_DEFERRED_HOST_OPERATIONS) diff --git a/materialsystem/vulkan/rendercommandlist.cpp b/materialsystem/vulkan/rendercommandlist.cpp index a1b92d9..5010633 100644 --- a/materialsystem/vulkan/rendercommandlist.cpp +++ b/materialsystem/vulkan/rendercommandlist.cpp @@ -198,6 +198,57 @@ void CVkRenderCommandList::DrawPrimitivesIndexed( uint32_t nIndexCount, uint32_t } +void CVkRenderCommandList::CopyImageToImage( IImage *pSrc, IImage *pDst ) +{ + SwitchRenderingStage(RENDERING_STAGE_POST_RASTER); + +} +void CVkRenderCommandList::BlitImageToImage( IImage *pSrc, ImageSector_t src, IImage *pDst, ImageSector_t dst ) +{ + SwitchRenderingStage(RENDERING_STAGE_POST_RASTER); + CVkBlitCommand *pCmd = CREATE_COMMAND(m_pCommandBufferManager, Blit); + pCmd->stInputImage.m_eObjectType = FRAME_OBJECT_TYPE_SINGLE; + pCmd->stInputImage.m_pSingle = pSrc; + pCmd->stOutputImage.m_eObjectType = FRAME_OBJECT_TYPE_SINGLE; + pCmd->stOutputImage.m_pSingle = pDst; + pCmd->AddDependency(pSrc, DEPENDENCY_MODE_BLIT_IMAGE_SOURCE); + pCmd->AddDependency(pDst, DEPENDENCY_MODE_BLIT_IMAGE_DESTINATION); + pCmd->iSrcMin[0] = src.m_iX; + pCmd->iSrcMin[1] = src.m_iY; + pCmd->iSrcMin[2] = 0; + pCmd->iDstMin[0] = dst.m_iX; + pCmd->iDstMin[1] = dst.m_iY; + pCmd->iDstMin[2] = 0; + pCmd->iSrcMax[0] = src.m_iWidth; + pCmd->iSrcMax[1] = src.m_iHeight; + pCmd->iSrcMax[2] = 1; + pCmd->iDstMax[0] = dst.m_iWidth; + pCmd->iDstMax[1] = dst.m_iHeight; + pCmd->iDstMax[2] = 1; + m_pPostRaster->AddCommand(pCmd); + +} + +void CVkRenderCommandList::ClearImage( IImage *pImage, float fR, float fG, float fB, float fA ) +{ + SwitchRenderingStage(RENDERING_STAGE_POST_RASTER); + CVkClearColorCommand *pCmd = CREATE_COMMAND(m_pCommandBufferManager, ClearColor); + pCmd->AddDependency(pImage, DEPENDENCY_MODE_COLOR_CLEAR_DESTINATION); + pCmd->r = fR; + pCmd->g = fG; + pCmd->b = fB; + pCmd->a = fA; + pCmd->stImage.m_eObjectType = FRAME_OBJECT_TYPE_SINGLE; + pCmd->stImage.m_pSingle = pImage; + m_pPostRaster->AddCommand(pCmd); + +} + +void CVkRenderCommandList::ClearDepth( IImage *pImage, float fVal ) +{ + +} + void CVkRenderCommandList::ResolveImage( IImage *pOriginal, IImage *pResolved ) { SwitchRenderingStage(RENDERING_STAGE_POST_RASTER); @@ -251,8 +302,11 @@ void CVkRenderCommandList::SwitchRenderingStage( EVulkanRenderingStage eStage ) }; if (eStage == RENDERING_STAGE_POST_RASTER) { - m_pPostRaster = m_pCommandBufferManager->CreateCommandBuffer(); - m_pPostRaster->Reset(); + if (m_eCurrentStage != RENDERING_STAGE_POST_RASTER) + { + m_pPostRaster = m_pCommandBufferManager->CreateCommandBuffer(); + m_pPostRaster->Reset(); + } } else { @@ -262,6 +316,7 @@ void CVkRenderCommandList::SwitchRenderingStage( EVulkanRenderingStage eStage ) m_pCommandBuffers.AppendTail(m_pPostRaster); } } + m_eCurrentStage = eStage; } VulkanRenderOutput_t *CVkRenderCommandList::FindOrCreateRenderOutput( uint32_t uIndex ) diff --git a/materialsystem/vulkan/rendercontext.cpp b/materialsystem/vulkan/rendercontext.cpp index 6f582e3..e02ce88 100644 --- a/materialsystem/vulkan/rendercontext.cpp +++ b/materialsystem/vulkan/rendercontext.cpp @@ -13,6 +13,7 @@ #include "materialsystem/igamewindow.h" #include "libraries.h" #include "stb/stb_image.h" +#include "ixr.h" #define REQUIRED_EXTENSION(ext) ext##_EXTENSION_NAME, @@ -137,6 +138,8 @@ VkFormat CVkImage::GetImageFormat( enum EImageFormat eImageFormat ) return VK_FORMAT_R8G8B8A8_UINT; case IMAGE_FORMAT_RGBA8_SINT: return VK_FORMAT_R8G8B8A8_SINT; + case IMAGE_FORMAT_RGBA8_SRGB: + return VK_FORMAT_R8G8B8A8_SRGB; case IMAGE_FORMAT_RGBA16_UINT: return VK_FORMAT_R16G16B16A16_UINT; @@ -451,6 +454,7 @@ public: virtual void Init() override; virtual void Frame( float fDeltaTime ) override; virtual void Shutdown() override; + virtual void ConnectInterface( void *pIface, const char *szName ) override; virtual IVertexBuffer *CreateVertexBuffer( uint32_t nSize ) override; virtual IIndexBuffer *CreateIndexBuffer( uint32_t nSize ) override; @@ -478,8 +482,6 @@ public: virtual void DestroyCommandList( IRenderCommandList *pCommandList ) override; virtual void SubmitCommandList(IRenderCommandList *pList) override; - virtual void SetMainWindowManager( IGameWindowManager *pWindowManager ) override; - virtual void RenderGameWindow( IGameWindow *pWindow ) override; virtual void RegisterGameWindow( IGameWindow *pWindow ) override; virtual void UnregisterGameWindow( IGameWindow *pWindow ) override; @@ -488,6 +490,11 @@ public: virtual void DestroyTextureArray() override; virtual ISampler *GetDefaultSampler() override; + + virtual void *GetVulkanInstance() override; + virtual void *GetVulkanPhysicalDevice() override; + virtual void *GetVulkanDevice() override; + virtual IImage *CreateImageFromVkImage( void *pVkImage, uint32_t x, uint32_t y, uint64_t optimalLayout, EImageFormat eFormat, EMultisampleType eMultisampleType ) override; private: VkPhysicalDevice SelectPhysicalDevice( CUtlVector physicalDevices ); CUtlVector GetDeviceExtensions(); @@ -497,7 +504,8 @@ private: VulkanWindow_t CreateSwapchain( IGameWindow *pWindow ); void DestroySwapchain( uint32_t uIndex ); - IGameWindowManager *m_pWindowManager; + IGameWindowManager *m_pWindowManager = NULL; + IXRManager *m_pXRManager = NULL; IVkCommandBufferManager *m_pCommandBufferManager; IVkCommandBufferManager *m_pAssetsCommandBufferManager; @@ -515,6 +523,14 @@ private: EXPOSE_INTERFACE(CVkRenderContext, IRenderContext, RENDER_CONTEXT_INTERFACE_VERSION); +void CVkRenderContext::ConnectInterface( void *pIface, const char *szName ) +{ + if (!V_strcmp(szName, GAME_WINDOW_MANAGER_INTERFACE_VERSION)) + m_pWindowManager = (IGameWindowManager*)pIface; + if (!V_strcmp(szName, XR_INTERFACE_VERSION)) + m_pXRManager = (IXRManager*)pIface; +} + //----------------------------------------------------------------------------- // Creates vertex buffer. Wrapper over CreateBuffer //----------------------------------------------------------------------------- @@ -660,11 +676,6 @@ void CVkRenderContext::RenderGameWindow( IGameWindow *pWindow ) { } -void CVkRenderContext::SetMainWindowManager( IGameWindowManager *pWindowManager ) -{ - m_pWindowManager = pWindowManager; -} - void CVkRenderContext::RegisterGameWindow( IGameWindow *pWindow ) { VulkanWindow_t window = CreateSwapchain(pWindow); @@ -696,6 +707,35 @@ ISampler *CVkRenderContext::GetDefaultSampler() } +void *CVkRenderContext::GetVulkanInstance() +{ + return s_vkInstance; +} + +void *CVkRenderContext::GetVulkanPhysicalDevice() +{ + return s_vkPhysicalDevice; +} + +void *CVkRenderContext::GetVulkanDevice() +{ + return s_vkDevice; +} + +IImage *CVkRenderContext::CreateImageFromVkImage( void *pVkImage, uint32_t x, uint32_t y, uint64_t optimalLayout, EImageFormat eFormat, EMultisampleType eMultisampleType ) +{ + CVkImage *pImage; + pImage = new CVkImage; + pImage->m_image = (VkImage)pVkImage; + pImage->m_eImageType = IMAGE_TYPE_2D; + pImage->m_eMultisampleType = eMultisampleType; + pImage->m_eFormat = eFormat; + pImage->m_nHeight = x; + pImage->m_nWidth = y; + pImage->m_ePreferredLayout = (VkImageLayout)optimalLayout; + pImage->CreateImageView(); + return pImage; +} VkPipelineLayout g_pLibraryEmptyLayout; static IVkCommandBuffer *s_pPresentCommandBuffer; @@ -746,6 +786,7 @@ void CVkRenderContext::Init() stInstanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; stInstanceCreateInfo.pApplicationInfo = &stApplicationInfo; + enabledInstanceExtensions.AppendTail(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); stInstanceCreateInfo.enabledExtensionCount = enabledInstanceExtensions.GetSize(); stInstanceCreateInfo.ppEnabledExtensionNames = enabledInstanceExtensions.GetData(); @@ -814,6 +855,7 @@ void CVkRenderContext::Init() vk11Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; vk11Features.pNext = &vk12Features; vk11Features.shaderDrawParameters = VK_TRUE; + vk11Features.multiview = VK_TRUE; stDeviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; @@ -915,6 +957,7 @@ void CVkRenderContext::Frame( float fDeltaTime ) uImageIndexes.AppendTail(s.m_uCurrentFrame); } uSwapchainImageIndexes.Resize(m_renderWindows.GetSize()); + vkWaitForFences(s_vkDevice, fences.GetSize(), fences.GetData(), VK_TRUE, UINT64_MAX); vkResetFences(s_vkDevice, fences.GetSize(), fences.GetData()); @@ -925,6 +968,9 @@ void CVkRenderContext::Frame( float fDeltaTime ) VkResult r = vkAcquireNextImageKHR(s_vkDevice, s.m_swapchain, UINT64_MAX, s.m_imageAvailable[s.m_uCurrentFrame], NULL, &uSwapchainImageIndexes[i]); i++; } + + if (m_pXRManager) + m_pXRManager->PreRender(); s_pPresentCommandBuffer = m_pCommandBufferManager->CreateCommandBuffer(); @@ -965,6 +1011,9 @@ void CVkRenderContext::Frame( float fDeltaTime ) s_pPresentCommandBuffer->Render(); s_pPresentCommandBuffer->Submit(0); + if (m_pXRManager) + m_pXRManager->CopySwapchain(); + VkPipelineStageFlags uPipelineStageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo stSubmitInfo = {}; @@ -992,6 +1041,8 @@ void CVkRenderContext::Frame( float fDeltaTime ) vkQueuePresentKHR(s_vkPresentQueue, &stPresentInfo); vkDeviceWaitIdle(s_vkDevice); + if (m_pXRManager) + m_pXRManager->PostRender(); for ( auto &s: m_renderWindows ) { diff --git a/materialsystem/vulkan/vulkan_state.h b/materialsystem/vulkan/vulkan_state.h index ebc9efa..286b150 100644 --- a/materialsystem/vulkan/vulkan_state.h +++ b/materialsystem/vulkan/vulkan_state.h @@ -491,6 +491,11 @@ public: virtual void Barrier( IRenderingObject *pObject, bool bIsRead, bool bIsWrite ) override; + virtual void CopyImageToImage( IImage *pSrc, IImage *pDst ) override; + virtual void BlitImageToImage( IImage *pSrc, ImageSector_t src, IImage *pDst, ImageSector_t dst ) override; + virtual void ClearImage( IImage *pImage, float fR, float fG, float fB, float fA ) override; + virtual void ClearDepth( IImage *pImage, float fVal ) override; + void Submit(); IVkCommandBufferManager *m_pCommandBufferManager; @@ -514,6 +519,8 @@ private: CUtlVector m_pCommandBuffers = {}; CUtlVector m_pScheduledDestroyPostRaster = {}; CUtlVector m_barriers = {}; + + EVulkanRenderingStage m_eCurrentStage = RENDERING_STAGE_SETUP_RASTER; }; diff --git a/public/ixr.h b/public/ixr.h index a535107..95216b9 100644 --- a/public/ixr.h +++ b/public/ixr.h @@ -3,12 +3,47 @@ #include "tier1/interface.h" #include "tier2/iappsystem.h" +#include "materialsystem/imaterialsystem.h" #include "trig.h" -abstract_class IXrManager: public IAppSystem +struct XRRenderSurface_t +{ + uint32_t m_uWidth; + uint32_t m_uHeight; + Vector m_vPosition; + Quat m_vRotation; + float m_fFov; +}; + +abstract_class IXRHeadset { public: + virtual uint32_t GetSurfaceCount() = 0; + virtual XRRenderSurface_t GetSurface( uint32_t i ) = 0; + virtual void SetSurfaceImage( uint32_t i, IImage *pImage ) = 0; + }; +abstract_class IXRController +{ +public: + +}; + +abstract_class IXRManager: public IAppSystem2 +{ +public: + virtual void Frame() = 0; + virtual void PreRender() = 0; + virtual void PostRender() = 0; + virtual void CopySwapchain() = 0; + virtual uint32_t GetHeadsetCount() = 0; + virtual IXRHeadset *GetHeadset( uint32_t i ) = 0; +}; + +#define XR_INTERFACE_VERSION "XR001" +#define OPEN_XR_INTERFACE_VERSION "Open" XR_INTERFACE_VERSION + + #endif diff --git a/public/materialsystem/imaterialsystem.h b/public/materialsystem/imaterialsystem.h index ab36b86..11faefc 100644 --- a/public/materialsystem/imaterialsystem.h +++ b/public/materialsystem/imaterialsystem.h @@ -30,6 +30,7 @@ enum EImageFormat IMAGE_FORMAT_RGBA8_UINT, IMAGE_FORMAT_RGBA8_SINT, + IMAGE_FORMAT_RGBA8_SRGB, IMAGE_FORMAT_RGBA16_UINT, IMAGE_FORMAT_RGBA16_SINT, @@ -260,6 +261,14 @@ public: virtual void PSSetSampler( uint32_t uRegister, ISampler *pImage ) = 0; }; +struct ImageSector_t +{ + int32_t m_iX; + int32_t m_iY; + int32_t m_iWidth; + int32_t m_iHeight; +}; + abstract_class IRenderCommandList { public: @@ -289,6 +298,11 @@ public: virtual void EndRecording() = 0; virtual void Barrier( IRenderingObject *pObject, bool bIsRead, bool bIsWrite ) = 0; + + virtual void CopyImageToImage( IImage *pSrc, IImage *pDst ) = 0; + virtual void BlitImageToImage( IImage *pSrc, ImageSector_t src, IImage *pDst, ImageSector_t dst ) = 0; + virtual void ClearImage( IImage *pImage, float fR, float fG, float fB, float fA ) = 0; + virtual void ClearDepth( IImage *pImage, float fVal ) = 0; }; //----------------------------------------------------------------------------- @@ -296,7 +310,7 @@ public: // // Responsible for the object handling //----------------------------------------------------------------------------- -abstract_class IRenderContext: public IAppSystem +abstract_class IRenderContext: public IAppSystem2 { public: virtual void Frame( float fTime ) = 0; @@ -324,8 +338,6 @@ public: virtual void DestroyCommandList( IRenderCommandList *pCommandList ) = 0; virtual void SubmitCommandList(IRenderCommandList *pList) = 0; - virtual void SetMainWindowManager( IGameWindowManager *pWindowManager ) = 0; - virtual void RenderGameWindow( IGameWindow *pWindow ) = 0; virtual void RegisterGameWindow( IGameWindow *pWindow ) = 0; virtual void UnregisterGameWindow( IGameWindow *pWindow ) = 0; @@ -334,6 +346,11 @@ public: virtual void DestroyTextureArray() = 0; virtual ISampler *GetDefaultSampler() = 0; + + virtual void *GetVulkanInstance() = 0; + virtual void *GetVulkanPhysicalDevice() = 0; + virtual void *GetVulkanDevice() = 0; + virtual IImage *CreateImageFromVkImage( void *pVkImage, uint32_t x, uint32_t y, uint64_t optimalLayout, EImageFormat eFormat, EMultisampleType eMultisampleType ) = 0; }; #define RENDER_CONTEXT_INTERFACE_VERSION "RenderContext001"