├── installer ├── .gitignore ├── comodo.pfx ├── signtool.exe ├── selfsigncert.pfx ├── README.rtf └── EULA.rtf ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── msbuild.yml ├── openxr-api-layer ├── pch.cpp ├── framework │ ├── .gitignore │ ├── dispatch.h │ ├── layer_apis.py │ ├── log.h │ ├── util.h │ ├── log.cpp │ ├── entry.cpp │ ├── dispatch.cpp │ └── dispatch_generator.py ├── module.def ├── version.h ├── packages.config ├── resource.h ├── openxr-api-layer.json ├── openxr-api-layer-32.json ├── ProjectionVS.hlsl ├── layer.h ├── resource.rc ├── utils │ ├── general.h │ ├── inputs.h │ ├── general.cpp │ ├── graphics.h │ ├── d3d11.cpp │ └── d3d12.cpp ├── pch.h ├── ProjectionPS.hlsl ├── SharpeningCS.hlsl ├── views.h └── openxr-api-layer.vcxproj.filters ├── version.info ├── scripts ├── sed.exe ├── msys-2.0.dll ├── msys-iconv-2.dll ├── msys-intl-8.dll ├── Capture-ETL.bat ├── Uninstall-Layer.ps1 ├── Uninstall-Layer32.ps1 ├── Install-Layer.ps1 ├── Install-Layer32.ps1 └── Tracing.wprp ├── .gitignore ├── .gitmodules ├── .clang-format ├── LICENSE ├── settings.cfg ├── CustomSetup ├── Properties │ └── AssemblyInfo.cs ├── CustomSetup.csproj └── CustomSetupActions.cs ├── README.md ├── XR_APILAYER_MBUCCHIA_quad_views_foveated.sln └── THIRD_PARTY /installer/.gitignore: -------------------------------------------------------------------------------- 1 | Output/ -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.gen.* text eol=lf 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: mbucchia 2 | -------------------------------------------------------------------------------- /openxr-api-layer/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | -------------------------------------------------------------------------------- /version.info: -------------------------------------------------------------------------------- 1 | major=1 2 | minor=1 3 | patch=4 4 | -------------------------------------------------------------------------------- /openxr-api-layer/framework/.gitignore: -------------------------------------------------------------------------------- 1 | dispatch.gen.cpp 2 | dispatch.gen.h 3 | -------------------------------------------------------------------------------- /openxr-api-layer/module.def: -------------------------------------------------------------------------------- 1 | LIBRARY 2 | EXPORTS 3 | xrNegotiateLoaderApiLayerInterface 4 | -------------------------------------------------------------------------------- /scripts/sed.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Quad-Views-Foveated/HEAD/scripts/sed.exe -------------------------------------------------------------------------------- /installer/comodo.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Quad-Views-Foveated/HEAD/installer/comodo.pfx -------------------------------------------------------------------------------- /installer/signtool.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Quad-Views-Foveated/HEAD/installer/signtool.exe -------------------------------------------------------------------------------- /scripts/msys-2.0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Quad-Views-Foveated/HEAD/scripts/msys-2.0.dll -------------------------------------------------------------------------------- /scripts/msys-iconv-2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Quad-Views-Foveated/HEAD/scripts/msys-iconv-2.dll -------------------------------------------------------------------------------- /scripts/msys-intl-8.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Quad-Views-Foveated/HEAD/scripts/msys-intl-8.dll -------------------------------------------------------------------------------- /installer/selfsigncert.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Quad-Views-Foveated/HEAD/installer/selfsigncert.pfx -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | packages/ 3 | bin/ 4 | **/x64/ 5 | **/obj/ 6 | **/__pycache__/ 7 | *.pyc 8 | *.vcxproj.user 9 | *.csproj.user 10 | -------------------------------------------------------------------------------- /openxr-api-layer/version.h: -------------------------------------------------------------------------------- 1 | const unsigned int LayerVersionMajor = 1; 2 | const unsigned int LayerVersionMinor = 1; 3 | const unsigned int LayerVersionPatch = 4; 4 | -------------------------------------------------------------------------------- /scripts/Capture-ETL.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | pushd %~dp0 3 | wpr -start Tracing.wprp -filemode 4 | 5 | echo Reproduce your issue now, then 6 | pause 7 | 8 | wpr -stop QVFR.etl 9 | popd 10 | -------------------------------------------------------------------------------- /openxr-api-layer/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /scripts/Uninstall-Layer.ps1: -------------------------------------------------------------------------------- 1 | $JsonPath = Join-Path "$PSScriptRoot" "openxr-api-layer.json" 2 | Start-Process -FilePath powershell.exe -Verb RunAs -Wait -ArgumentList @" 3 | & { 4 | Remove-ItemProperty -Path HKLM:\Software\Khronos\OpenXR\1\ApiLayers\Implicit -Name '$jsonPath' -Force | Out-Null 5 | } 6 | "@ 7 | -------------------------------------------------------------------------------- /scripts/Uninstall-Layer32.ps1: -------------------------------------------------------------------------------- 1 | $JsonPath = Join-Path "$PSScriptRoot" "openxr-api-layer-32.json" 2 | Start-Process -FilePath powershell.exe -Verb RunAs -Wait -ArgumentList @" 3 | & { 4 | Remove-ItemProperty -Path HKLM:\Software\WOW6432Node\Khronos\OpenXR\1\ApiLayers\Implicit -Name '$jsonPath' -Force | Out-Null 5 | } 6 | "@ 7 | -------------------------------------------------------------------------------- /openxr-api-layer/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by resource.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /scripts/Install-Layer.ps1: -------------------------------------------------------------------------------- 1 | $RegistryPath = "HKLM:\Software\Khronos\OpenXR\1\ApiLayers\Implicit" 2 | $JsonPath = Join-Path "$PSScriptRoot" "openxr-api-layer.json" 3 | Start-Process -FilePath powershell.exe -Verb RunAs -Wait -ArgumentList @" 4 | & { 5 | If (-not (Test-Path $RegistryPath)) { 6 | New-Item -Path $RegistryPath -Force | Out-Null 7 | } 8 | New-ItemProperty -Path $RegistryPath -Name '$jsonPath' -PropertyType DWord -Value 0 -Force | Out-Null 9 | } 10 | "@ 11 | -------------------------------------------------------------------------------- /scripts/Install-Layer32.ps1: -------------------------------------------------------------------------------- 1 | $RegistryPath = "HKLM:\Software\WOW6432Node\Khronos\OpenXR\1\ApiLayers\Implicit" 2 | $JsonPath = Join-Path "$PSScriptRoot" "openxr-api-layer-32.json" 3 | Start-Process -FilePath powershell.exe -Verb RunAs -Wait -ArgumentList @" 4 | & { 5 | If (-not (Test-Path $RegistryPath)) { 6 | New-Item -Path $RegistryPath -Force | Out-Null 7 | } 8 | New-ItemProperty -Path $RegistryPath -Name '$jsonPath' -PropertyType DWord -Value 0 -Force | Out-Null 9 | } 10 | "@ 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/OpenXR-SDK-Source"] 2 | path = external/OpenXR-SDK-Source 3 | url = https://github.com/KhronosGroup/OpenXR-SDK-Source.git 4 | [submodule "external/OpenXR-SDK"] 5 | path = external/OpenXR-SDK 6 | url = https://github.com/KhronosGroup/OpenXR-SDK.git 7 | [submodule "external/OpenXR-MixedReality"] 8 | path = external/OpenXR-MixedReality 9 | url = https://github.com/microsoft/OpenXR-MixedReality.git 10 | [submodule "external/FidelityFX-CAS"] 11 | path = external/FidelityFX-CAS 12 | url = https://github.com/GPUOpen-Effects/FidelityFX-CAS.git 13 | [submodule "external/fmt"] 14 | path = external/fmt 15 | url = https://github.com/fmtlib/fmt.git 16 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | IndentWidth: 4 3 | ColumnLimit: 120 4 | AllowShortBlocksOnASingleLine: false 5 | AllowShortFunctionsOnASingleLine: false 6 | AlwaysBreakTemplateDeclarations: true 7 | BinPackParameters: false 8 | BinPackArguments: false 9 | IndentWrappedFunctionNames: false 10 | KeepEmptyLinesAtTheStartOfBlocks: false 11 | MaxEmptyLinesToKeep: 1 12 | NamespaceIndentation: All 13 | PenaltyReturnTypeOnItsOwnLine: 100 14 | PointerAlignment: Left 15 | SortIncludes: false 16 | SpaceAfterCStyleCast: false 17 | SpaceBeforeAssignmentOperators: true 18 | SpaceBeforeParens: ControlStatements 19 | SpaceInEmptyParentheses: false 20 | SpacesInAngles: false 21 | SpacesInCStyleCastParentheses: false 22 | SpacesInContainerLiterals: false 23 | SpacesInParentheses: false 24 | SpacesInSquareBrackets: false 25 | UseTab: Never 26 | -------------------------------------------------------------------------------- /openxr-api-layer/openxr-api-layer.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_format_version" : "1.0.0", 3 | "api_layer": { 4 | "name": "XR_APILAYER_name", 5 | "library_path": ".\\XR_APILAYER_name.dll", 6 | "api_version": "1.0", 7 | "implementation_version": "1", 8 | "description": "Emulate quad views foveation support", 9 | "instance_extensions": [ 10 | { 11 | "name": "XR_VARJO_quad_views", 12 | "extension_version": 1, 13 | "entrypoints": [] 14 | }, 15 | { 16 | "name": "XR_VARJO_foveated_rendering", 17 | "extension_version": 3, 18 | "entrypoints": [] 19 | } 20 | ], 21 | "functions": { 22 | "xrNegotiateLoaderApiLayerInterface": "xrNegotiateLoaderApiLayerInterface" 23 | }, 24 | "disable_environment": "DISABLE_XR_APILAYER_name" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /openxr-api-layer/openxr-api-layer-32.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_format_version" : "1.0.0", 3 | "api_layer": { 4 | "name": "XR_APILAYER_name", 5 | "library_path": ".\\XR_APILAYER_name-32.dll", 6 | "api_version": "1.0", 7 | "implementation_version": "1", 8 | "description": "Emulate quad views foveation support", 9 | "instance_extensions": [ 10 | { 11 | "name": "XR_VARJO_quad_views", 12 | "extension_version": 1, 13 | "entrypoints": [] 14 | }, 15 | { 16 | "name": "XR_VARJO_foveated_rendering", 17 | "extension_version": 3, 18 | "entrypoints": [] 19 | } 20 | ], 21 | "functions": { 22 | "xrNegotiateLoaderApiLayerInterface": "xrNegotiateLoaderApiLayerInterface" 23 | }, 24 | "disable_environment": "DISABLE_XR_APILAYER_name" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /installer/README.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033{\fonttbl{\f0\fnil\fcharset0 Tahoma;}} 2 | {\colortbl ;\red43\green145\blue175;\red163\green21\blue21;\red0\green0\blue0;\red0\green0\blue255;} 3 | {\*\generator Riched20 10.0.19041}\viewkind4\uc1 4 | \pard\sa200\sl240\slmult1\qj\cf1\f0\fs22\lang9 OpenXR Foveated Rendering with Quad Views - \cf0\fs16 v1.1.4\cf2\fs22\par 5 | \cf3\fs19 This software enables the use of foveated rendering (with or without eye tracking) with applications using OpenXR and the Varjo quad views rendering technique. Two examples of such applications are DCS World and Pavlov VR.\par 6 | Please visit the official page at {\cf0{\field{\*\fldinst{HYPERLINK https://github.com/mbucchia/Quad-Views-Foveated/wiki }}{\fldrslt{https://github.com/mbucchia/Quad-Views-Foveated/wiki\ul0\cf0}}}}\f0\fs19 for detailed instructions on how to download,\lang1033 \lang9 install, and use this software.\par 7 | \b DISCLAIMER: This software is distributed as-is, without any warranties or conditions of any kind. Use at your own risks.\par 8 | \b0 This software was created by Matthieu Bucchianeri.\par 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2023 Matthieu Bucchianeri 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /settings.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # DO NOT EDIT THIS FILE! 3 | # INSTEAD, CREATE A NEW FILE `settings.cfg` UNDER `%LocalAppData%\Quad-Views-Foveated` 4 | # DO NOT COPY THIS FILE, START FROM A BLANK FILE! 5 | # 6 | 7 | # Common settings for all headsets (unless overriden below). 8 | smoothen_focus_view_edges=0.2 9 | sharpen_focus_view=0.7 10 | # Turbo mode is an unsafe feature that should not be enabled by default. 11 | turbo_mode=0 12 | 13 | # Fixed Foveated rendering settings for fallback when eye tracker is not available. 14 | horizontal_fixed_section=0.5 15 | vertical_fixed_section=0.45 16 | 17 | [Oculus] 18 | peripheral_multiplier=0.4 19 | focus_multiplier=1.1 20 | horizontal_focus_section=0.5 21 | vertical_focus_section=0.5 22 | 23 | [Varjo] 24 | horizontal_focus_section=0.29 25 | vertical_focus_section=0.33 26 | # Turbo mode is incompatible with Varjo's deferred swapchain release. Use OpenXR Toolkit Turbo instead. 27 | turbo_mode=0 28 | 29 | [PimaxXR] 30 | # Dynamic Foveated Rendering settings (for Crystal) 31 | horizontal_focus_section=0.33 32 | vertical_focus_section=0.31 33 | 34 | [Windows Mixed Reality] 35 | peripheral_multiplier=0.4 36 | focus_multiplier=1.1 37 | # Dynamic Foveated Rendering settings (for G2 Omnicept Edition) 38 | horizontal_focus_section=0.3 39 | vertical_focus_section=0.3 40 | vertical_focus_offset=0.04 41 | 42 | [SteamVR] 43 | # Turbo Mode causes unexplained errors with SteamVR. 44 | turbo_mode=0 45 | 46 | 47 | # Un-advertise in incompatible apps. 48 | [exe:Contractors] 49 | unadvertise=1 50 | [app:WanderingSpace] 51 | unadvertise=1 52 | -------------------------------------------------------------------------------- /CustomSetup/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("CustomSetup")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("CustomSetup")] 13 | [assembly: AssemblyCopyright("Copyright © 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("b6c07936-a1d2-4a80-b559-b55e3f15cc97")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.1.4.0")] 36 | [assembly: AssemblyFileVersion("1.1.4.0")] 37 | -------------------------------------------------------------------------------- /openxr-api-layer/framework/dispatch.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | namespace openxr_api_layer { 26 | 27 | XrResult XRAPI_CALL xrCreateApiLayerInstance(const XrInstanceCreateInfo* instanceCreateInfo, 28 | const struct XrApiLayerCreateInfo* apiLayerInfo, 29 | XrInstance* instance); 30 | XrResult XRAPI_CALL xrGetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function); 31 | 32 | } // namespace openxr_api_layer 33 | -------------------------------------------------------------------------------- /openxr-api-layer/ProjectionVS.hlsl: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | // A Vertex Shader that draws a full-screen quad and projects the coordinates of two layers. 24 | 25 | cbuffer ConstantBuffer : register(b0) { 26 | float4x4 focusProjection; 27 | }; 28 | 29 | void main(in uint id : SV_VertexID, out float4 position : SV_POSITION, out float2 texcoord : PROJ_COORD0, out float3 projectedFocusCoord : PROJ_COORD1) { 30 | texcoord = float2((id == 1) ? 2.0 : 0.0, (id == 2) ? 2.0 : 0.0); 31 | position = float4(texcoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); 32 | projectedFocusCoord = mul(position, focusProjection).xyw; 33 | } 34 | -------------------------------------------------------------------------------- /openxr-api-layer/framework/layer_apis.py: -------------------------------------------------------------------------------- 1 | # The list of OpenXR functions our layer will override. 2 | override_functions = [ 3 | "xrGetSystem", 4 | "xrGetSystemProperties", 5 | "xrEnumerateViewConfigurations", 6 | "xrGetViewConfigurationProperties", 7 | "xrEnumerateViewConfigurationViews", 8 | "xrEnumerateEnvironmentBlendModes", 9 | "xrCreateReferenceSpace", 10 | "xrDestroySpace", 11 | "xrCreateSession", 12 | "xrDestroySession", 13 | "xrBeginSession", 14 | "xrAttachSessionActionSets", 15 | "xrCreateSwapchain", 16 | "xrDestroySwapchain", 17 | "xrAcquireSwapchainImage", 18 | "xrReleaseSwapchainImage", 19 | "xrLocateViews", 20 | "xrLocateSpace", 21 | "xrWaitFrame", 22 | "xrBeginFrame", 23 | "xrEndFrame", 24 | "xrPollEvent", 25 | "xrSyncActions", 26 | "xrGetVisibilityMaskKHR", 27 | ] 28 | 29 | # The list of OpenXR functions our layer will use from the runtime. 30 | # Might repeat entries from override_functions above. 31 | requested_functions = [ 32 | "xrGetInstanceProperties", 33 | "xrGetSystem", 34 | "xrGetSystemProperties", 35 | "xrCreateReferenceSpace", 36 | "xrDestroySpace", 37 | "xrCreateSwapchain", 38 | "xrDestroySwapchain", 39 | "xrEnumerateSwapchainImages", 40 | "xrAcquireSwapchainImage", 41 | "xrWaitSwapchainImage", 42 | "xrReleaseSwapchainImage", 43 | "xrPathToString", 44 | "xrStringToPath", 45 | "xrCreateActionSet", 46 | "xrCreateAction", 47 | "xrSuggestInteractionProfileBindings", 48 | "xrCreateActionSpace", 49 | "xrLocateViews", 50 | "xrWaitFrame", 51 | "xrBeginFrame", 52 | "xrGetActionStatePose", 53 | "xrSyncActions", 54 | "xrCreateEyeTrackerFB", 55 | "xrGetEyeGazesFB", 56 | ] 57 | 58 | # The list of OpenXR extensions our layer will either override or use. 59 | extensions = ["XR_KHR_visibility_mask", "XR_FB_eye_tracking_social"] 60 | -------------------------------------------------------------------------------- /openxr-api-layer/layer.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include "framework/dispatch.gen.h" 26 | 27 | namespace openxr_api_layer { 28 | 29 | const std::string LayerName = LAYER_NAME; 30 | const std::string LayerPrettyName = "Quad-Views-Foveated"; 31 | extern const std::string VersionString; 32 | 33 | // Singleton accessor. 34 | OpenXrApi* GetInstance(); 35 | 36 | // The path where the DLL is loaded from (eg: to load data files). 37 | extern std::filesystem::path dllHome; 38 | 39 | // The path that is writable (eg: to store logs). 40 | extern std::filesystem::path localAppData; 41 | 42 | extern const std::vector> advertisedExtensions; 43 | extern const std::vector blockedExtensions; 44 | extern const std::vector implicitExtensions; 45 | 46 | } // namespace openxr_api_layer 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Foveated Rendering via Quad Views 2 | 3 | In layperson's terms: 4 | 5 | This software lets you use Eye-Tracked Foveated Rendering (sometimes referred to as Dynamic Foveated Rendering) with your Pimax Crystal, Meta Quest Pro, and other headsets supporting eye tracking via OpenXR in games using the [quad views rendering](https://github.com/mbucchia/Quad-Views-Foveated/wiki/What-is-Quad-Views-rendering%3F) technique like Digital Combat Simulation (DCS) and Pavlov VR. 6 | 7 | In technical terms: 8 | 9 | This software enables OpenXR apps developed with [`XR_VARJO_quad_views`](https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_VARJO_quad_views) and optionally [`XR_VARJO_foveated_rendering`](https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_VARJO_foveated_rendering) to be used on platforms that do not typically support those extensions. It composes each quad view projection layer into a stereo projection layer, and uses the eye tracking support on the device to make the inner projection views follow the eye gaze. 10 | 11 | DISCLAIMER: This software is distributed as-is, without any warranties or conditions of any kind. Use at your own risks. 12 | 13 | # Details and instructions on the [the wiki](https://github.com/mbucchia/Quad-Views-Foveated/wiki)! 14 | 15 | ## Setup 16 | 17 | Download the latest version from the [Releases page](https://github.com/mbucchia/Quad-Views-Foveated/releases). Find the installer program under **Assets**, file `Quad-Views-Foveated-.msi`. 18 | 19 | More information on the [the wiki](https://github.com/mbucchia/Quad-Views-Foveated/wiki)! 20 | 21 | For troubleshooting, the log file can be found at `%LocalAppData%\Quad-Views-Foveated\Quad-Views-Foveated.log`. 22 | 23 | ## Donate 24 | 25 | Donations are welcome and totally optional. Please use [my GitHub sponsorship page](https://github.com/sponsors/mbucchia) to make one-time or recurring donations! 26 | 27 | Thank you! 28 | 29 | ## Special thanks 30 | 31 | Thanks to my beta testers for helping throughout development and release (in alphabetical order): 32 | 33 | - BARRACUDAS 34 | - edmuss 35 | - MastahFR 36 | - mfrisby 37 | - Omniwhatever 38 | - xMcCARYx 39 | -------------------------------------------------------------------------------- /openxr-api-layer/framework/log.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include "pch.h" 26 | 27 | namespace openxr_api_layer::log { 28 | 29 | TRACELOGGING_DECLARE_PROVIDER(g_traceProvider); 30 | 31 | extern TraceLoggingActivity g_traceGlobal; 32 | 33 | #define IsTraceEnabled() TraceLoggingProviderEnabled(g_traceProvider, 0, 0) 34 | 35 | #define TraceLocalActivity(activity) TraceLoggingActivity activity; 36 | 37 | #define TLArg(var, ...) TraceLoggingValue(var, ##__VA_ARGS__) 38 | #define TLPArg(var, ...) TraceLoggingPointer(var, ##__VA_ARGS__) 39 | #ifdef _M_IX86 40 | #define TLXArg TLArg 41 | #else 42 | #define TLXArg TLPArg 43 | #endif 44 | 45 | // General logging function. 46 | void Log(const char* fmt, ...); 47 | static inline void Log(const std::string_view& str) { 48 | Log(str.data()); 49 | } 50 | 51 | // Debug logging function. Can make things very slow (only enabled on Debug builds). 52 | void DebugLog(const char* fmt, ...); 53 | static inline void DebugLog(const std::string_view& str) { 54 | Log(str.data()); 55 | } 56 | 57 | // Error logging function. Goes silent after too many errors. 58 | void ErrorLog(const char* fmt, ...); 59 | static inline void ErrorLog(const std::string_view& str) { 60 | Log(str.data()); 61 | } 62 | 63 | } // namespace openxr_api_layer::log 64 | -------------------------------------------------------------------------------- /openxr-api-layer/resource.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE 9, 1 20 | 21 | #ifdef APSTUDIO_INVOKED 22 | ///////////////////////////////////////////////////////////////////////////// 23 | // 24 | // TEXTINCLUDE 25 | // 26 | 27 | 1 TEXTINCLUDE 28 | BEGIN 29 | "resource.h\0" 30 | END 31 | 32 | 2 TEXTINCLUDE 33 | BEGIN 34 | "#include ""winres.h""\r\n" 35 | "\0" 36 | END 37 | 38 | 3 TEXTINCLUDE 39 | BEGIN 40 | "\r\n" 41 | "\0" 42 | END 43 | 44 | #endif // APSTUDIO_INVOKED 45 | 46 | 47 | ///////////////////////////////////////////////////////////////////////////// 48 | // 49 | // Version 50 | // 51 | 52 | VS_VERSION_INFO VERSIONINFO 53 | FILEVERSION 1,1,4,0 54 | PRODUCTVERSION 1,1,4,0 55 | FILEFLAGSMASK 0x3fL 56 | #ifdef _DEBUG 57 | FILEFLAGS 0x1L 58 | #else 59 | FILEFLAGS 0x0L 60 | #endif 61 | FILEOS 0x40004L 62 | FILETYPE 0x2L 63 | FILESUBTYPE 0x0L 64 | BEGIN 65 | BLOCK "StringFileInfo" 66 | BEGIN 67 | BLOCK "040904b0" 68 | BEGIN 69 | VALUE "FileVersion", "1.1.4.0" 70 | VALUE "InternalName", "XR_APILAYER_MBUCCHIA_quad_views_foveated.dll" 71 | VALUE "LegalCopyright", "Copyright (c) 2023 Matthieu Bucchianeri" 72 | VALUE "OriginalFilename", "XR_APILAYER_MBUCCHIA_quad_views_foveated.dll" 73 | VALUE "ProductName", "Quad-Views-Foveated" 74 | VALUE "ProductVersion", "1.1.4.0" 75 | END 76 | END 77 | BLOCK "VarFileInfo" 78 | BEGIN 79 | VALUE "Translation", 0x409, 1200 80 | END 81 | END 82 | 83 | #endif // English (United States) resources 84 | ///////////////////////////////////////////////////////////////////////////// 85 | 86 | 87 | 88 | #ifndef APSTUDIO_INVOKED 89 | ///////////////////////////////////////////////////////////////////////////// 90 | // 91 | // Generated from the TEXTINCLUDE 3 resource. 92 | // 93 | 94 | 95 | ///////////////////////////////////////////////////////////////////////////// 96 | #endif // not APSTUDIO_INVOKED 97 | -------------------------------------------------------------------------------- /scripts/Tracing.wprp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /.github/workflows/msbuild.yml: -------------------------------------------------------------------------------- 1 | name: MSBuild 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - release/* 8 | tags: 9 | - '[0-9]+.[0-9]+.[0-9]+' 10 | pull_request: 11 | branches: 12 | - main 13 | - release/* 14 | workflow_dispatch: 15 | 16 | env: 17 | SOLUTION_FILE_PATH: XR_APILAYER_MBUCCHIA_quad_views_foveated.sln 18 | BUILD_CONFIGURATION: Release 19 | 20 | jobs: 21 | build: 22 | runs-on: windows-latest 23 | environment: build-and-sign 24 | 25 | steps: 26 | - name: Checkout project 27 | uses: actions/checkout@v2 28 | 29 | - name: Checkout submodules 30 | working-directory: ${{env.GITHUB_WORKSPACE}} 31 | run: git submodule update --init 32 | 33 | - name: Setup DevEnv 34 | uses: seanmiddleditch/gha-setup-vsdevenv@v4 35 | 36 | - name: Setup Python 37 | uses: actions/setup-python@v2.3.1 38 | with: 39 | python-version: 3.8 40 | 41 | - name: Restore NuGet packages 42 | working-directory: ${{env.GITHUB_WORKSPACE}} 43 | run: nuget restore ${{env.SOLUTION_FILE_PATH}} 44 | 45 | - name: Build 46 | env: 47 | PFX_PASSWORD: ${{ secrets.PFX_PASSWORD }} 48 | PFX_NAME: ${{env.PFX_NAME}} 49 | working-directory: ${{env.GITHUB_WORKSPACE}} 50 | run: | 51 | # Need to build vdproj. We must invoke this tool from inside its own folder. 52 | $DisableOutOfProcBuild=$(vswhere -latest -find **\DisableOutOfProcBuild.exe) 53 | Push-Location $(Split-Path $DisableOutOfProcBuild) 54 | & $DisableOutOfProcBuild 55 | Pop-Location 56 | 57 | # Finally, we may build the project. 58 | devenv.com ${{env.SOLUTION_FILE_PATH}} /Build "${{env.BUILD_CONFIGURATION}}|x64" 59 | 60 | - name: Signing 61 | env: 62 | PFX_PASSWORD: ${{ secrets.PFX_PASSWORD }} 63 | PFX_NAME: ${{env.PFX_NAME}} 64 | working-directory: ${{env.GITHUB_WORKSPACE}} 65 | run: | 66 | $pfxName = if ($env:PFX_NAME) { $env:PFX_NAME } else { "selfsigncert" }; 67 | installer/signtool.exe sign /d "OpenXR Quad Views Foveated" /du "https://github.com/mbucchia/Quad-Views-Foveated" /f installer/$pfxName.pfx /p "$env:PFX_PASSWORD" /v installer/output/Quad-Views-Foveated.msi 68 | 69 | - name: Publish 70 | uses: actions/upload-artifact@v2 71 | with: 72 | name: Setup 73 | path: | 74 | installer/output/Quad-Views-Foveated.msi 75 | 76 | - name: Publish 77 | uses: actions/upload-artifact@v2 78 | with: 79 | name: Symbols 80 | path: | 81 | bin/x64/Release/XR_APILAYER_MBUCCHIA_quad_views_foveated.pdb 82 | -------------------------------------------------------------------------------- /openxr-api-layer/framework/util.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include "pch.h" 26 | 27 | namespace xr { 28 | 29 | static inline std::string ToString(XrVersion version) { 30 | return fmt::format("{}.{}.{}", XR_VERSION_MAJOR(version), XR_VERSION_MINOR(version), XR_VERSION_PATCH(version)); 31 | } 32 | 33 | static inline std::string ToString(const XrPosef& pose) { 34 | return fmt::format("p: ({:.3f}, {:.3f}, {:.3f}), o:({:.3f}, {:.3f}, {:.3f}, {:.3f})", 35 | pose.position.x, 36 | pose.position.y, 37 | pose.position.z, 38 | pose.orientation.x, 39 | pose.orientation.y, 40 | pose.orientation.z, 41 | pose.orientation.w); 42 | } 43 | 44 | static inline std::string ToString(const XrFovf& fov) { 45 | return fmt::format( 46 | "(l:{:.3f}, r:{:.3f}, u:{:.3f}, d:{:.3f})", fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown); 47 | } 48 | 49 | static inline std::string ToString(const XrVector2f& vec) { 50 | return fmt::format("({:.3f}, {:.3f})", vec.x, vec.y); 51 | } 52 | 53 | static inline std::string ToString(const XrVector3f& vec) { 54 | return fmt::format("({:.3f}, {:.3f}, {:.3f})", vec.x, vec.y, vec.z); 55 | } 56 | 57 | static inline std::string ToString(const XrRect2Di& rect) { 58 | return fmt::format( 59 | "x:{}, y:{}, w:{}, h:{}", rect.offset.x, rect.offset.y, rect.extent.width, rect.extent.height); 60 | } 61 | 62 | static inline std::string ToString(const XrRect2Df& rect) { 63 | return fmt::format( 64 | "x:{}, y:{}, w:{}, h:{}", rect.offset.x, rect.offset.y, rect.extent.width, rect.extent.height); 65 | } 66 | 67 | } // namespace xr -------------------------------------------------------------------------------- /openxr-api-layer/utils/general.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | namespace xr::math { 26 | 27 | static inline XrVector3f Cross(const XrVector3f& a, const XrVector3f& b) { 28 | return { 29 | a.y * b.z - a.z * b.y, 30 | a.z * b.x - a.x * b.z, 31 | a.x * b.y - a.y * b.x, 32 | }; 33 | } 34 | 35 | } // namespace xr::math 36 | 37 | namespace openxr_api_layer::utils::general { 38 | 39 | struct ITimer { 40 | virtual ~ITimer() = default; 41 | 42 | virtual void start() = 0; 43 | virtual void stop() = 0; 44 | 45 | virtual uint64_t query() const = 0; 46 | }; 47 | 48 | std::shared_ptr createTimer(); 49 | 50 | static inline bool startsWith(const std::string& str, const std::string& substr) { 51 | return str.find(substr) == 0; 52 | } 53 | 54 | static inline bool endsWith(const std::string& str, const std::string& substr) { 55 | const auto pos = str.find(substr); 56 | return pos != std::string::npos && pos == str.size() - substr.size(); 57 | } 58 | 59 | // Both ray and quadCenter poses must be located using the same base space. 60 | bool hitTest(const XrPosef& ray, const XrPosef& quadCenter, const XrExtent2Df& quadSize, XrPosef& hitPose); 61 | 62 | // Get UV coordinates for a point on quad. 63 | XrVector2f getUVCoordinates(const XrVector3f& point, const XrPosef& quadCenter, const XrExtent2Df& quadSize); 64 | static inline POINT getUVCoordinates(const XrVector3f& point, 65 | const XrPosef& quadCenter, 66 | const XrExtent2Df& quadSize, 67 | const XrExtent2Di& quadPixelSize) { 68 | const XrVector2f uv = getUVCoordinates(point, quadCenter, quadSize); 69 | return {static_cast(uv.x * quadPixelSize.width), static_cast(uv.y * quadPixelSize.height)}; 70 | } 71 | 72 | } // namespace openxr_api_layer::utils::general 73 | -------------------------------------------------------------------------------- /openxr-api-layer/framework/log.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "pch.h" 24 | 25 | namespace { 26 | constexpr uint32_t k_maxLoggedErrors = 100; 27 | uint32_t g_globalErrorCount = 0; 28 | } // namespace 29 | 30 | namespace openxr_api_layer::log { 31 | extern std::ofstream logStream; 32 | 33 | // {cbf3adcd-42b1-4c38-830c-91980af201f8} 34 | TRACELOGGING_DEFINE_PROVIDER(g_traceProvider, 35 | "QuadViewsFoveated", 36 | (0xcbf3adcd, 0x42b1, 0x4c38, 0x83, 0x0c, 0x98, 0x98, 0x0a, 0xf2, 0x01, 0xf8)); 37 | 38 | TraceLoggingActivity g_traceActivity; 39 | 40 | namespace { 41 | 42 | // Utility logging function. 43 | void InternalLog(const char* fmt, va_list va) { 44 | const std::time_t now = std::time(nullptr); 45 | 46 | char buf[1024]; 47 | size_t offset = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z: ", std::localtime(&now)); 48 | vsnprintf_s(buf + offset, sizeof(buf) - offset, _TRUNCATE, fmt, va); 49 | OutputDebugStringA(buf); 50 | if (logStream.is_open()) { 51 | logStream << buf; 52 | logStream.flush(); 53 | } 54 | } 55 | } // namespace 56 | 57 | void Log(const char* fmt, ...) { 58 | va_list va; 59 | va_start(va, fmt); 60 | InternalLog(fmt, va); 61 | va_end(va); 62 | } 63 | 64 | void ErrorLog(const char* fmt, ...) { 65 | if (g_globalErrorCount++ < k_maxLoggedErrors) { 66 | va_list va; 67 | va_start(va, fmt); 68 | InternalLog(fmt, va); 69 | va_end(va); 70 | if (g_globalErrorCount == k_maxLoggedErrors) { 71 | Log("Maximum number of errors logged. Going silent."); 72 | } 73 | } 74 | } 75 | 76 | void DebugLog(const char* fmt, ...) { 77 | #ifdef _DEBUG 78 | va_list va; 79 | va_start(va, fmt); 80 | InternalLog(fmt, va); 81 | va_end(va); 82 | #endif 83 | } 84 | 85 | } // namespace openxr_api_layer::log 86 | -------------------------------------------------------------------------------- /openxr-api-layer/pch.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | // Uncomment below the graphics frameworks used by the layer. 26 | 27 | #define XR_USE_GRAPHICS_API_D3D11 28 | #define XR_USE_GRAPHICS_API_D3D12 29 | 30 | // Standard library. 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #define _USE_MATH_DEFINES 50 | #include 51 | 52 | using namespace std::chrono_literals; 53 | 54 | // Windows header files. 55 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 56 | #define NOMINMAX 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | 64 | using Microsoft::WRL::ComPtr; 65 | 66 | // Graphics APIs. 67 | #include 68 | #ifdef XR_USE_GRAPHICS_API_D3D11 69 | #include 70 | #endif 71 | #ifdef XR_USE_GRAPHICS_API_D3D12 72 | #include 73 | #endif 74 | 75 | // OpenXR + Windows-specific definitions. 76 | #define XR_NO_PROTOTYPES 77 | #define XR_USE_PLATFORM_WIN32 78 | #include 79 | #include 80 | 81 | // OpenXR loader interfaces. 82 | #include 83 | 84 | // OpenXR/DirectX utilities. 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | 93 | // FMT formatter. 94 | #include 95 | 96 | #if defined(XR_USE_GRAPHICS_API_D3D11) || defined(XR_USE_GRAPHICS_API_D3D12) 97 | // Utilities framework. 98 | #include 99 | #endif 100 | 101 | #include 102 | -------------------------------------------------------------------------------- /openxr-api-layer/ProjectionPS.hlsl: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | // A Pixel Shader that paints the content of a layer given the specified coordinates. 24 | 25 | cbuffer ConstantBuffer : register(b0) { 26 | float smoothingArea; 27 | bool ignoreAlpha; 28 | bool isUnpremultipliedAlpha; 29 | bool debugFocusView; 30 | }; 31 | 32 | SamplerState sourceSampler : register(s0); 33 | Texture2D sourceStereoTexture : register(t0); 34 | Texture2D sourceFocusTexture : register(t1); 35 | 36 | float4 premultiplyAlpha(float4 color) { 37 | return float4(color.rgb * color.a, color.a); 38 | } 39 | 40 | float4 unpremultiplyAlpha(float4 color) { 41 | if (color.a != 0) { 42 | return float4(color.rgb / color.a, color.a); 43 | } else { 44 | return 0; 45 | } 46 | } 47 | 48 | float4 main(in float4 position : SV_POSITION, in float2 texcoord : PROJ_COORD0, in float3 projectedFocusCoord : PROJ_COORD1) : SV_TARGET { 49 | // Convert to texcoord and pick the pixel from each layer. 50 | float4 color0 = sourceStereoTexture.Sample(sourceSampler, texcoord); 51 | 52 | float2 layer1ProjectedCoordNdc = projectedFocusCoord.xy / projectedFocusCoord.z; 53 | float2 layer1TexCoord = layer1ProjectedCoordNdc * float2(0.5f, -0.5f) + 0.5f; 54 | // For pixels outside of the focus view, the sampler will give us a fully transparent pixel. 55 | float4 color1 = sourceFocusTexture.Sample(sourceSampler, layer1TexCoord); 56 | 57 | if (ignoreAlpha) { 58 | color0.a = color1.a = 1; 59 | } 60 | 61 | if (!isUnpremultipliedAlpha) { 62 | color0 = unpremultiplyAlpha(color0); 63 | color1 = unpremultiplyAlpha(color1); 64 | } 65 | 66 | // Do a smooth transition with alpha-blending around the edges. 67 | float isInside = all(abs(layer1ProjectedCoordNdc) < 1); 68 | if (smoothingArea) { 69 | float2 s = smoothstep(float2(0, 0), float2(smoothingArea, smoothingArea), layer1TexCoord) - 70 | smoothstep(float2(1, 1) - float2(smoothingArea, smoothingArea), float2(1, 1), layer1TexCoord); 71 | color1.a = isInside * max(0.5, s.x * s.y); 72 | } else { 73 | color1.a = isInside; 74 | } 75 | 76 | color0 = premultiplyAlpha(color0); 77 | color1 = premultiplyAlpha(color1); 78 | 79 | // Blend the two pixels. 80 | float4 color; 81 | if (!debugFocusView) { 82 | color = color1 + color0 * (1 - color1.a); 83 | } else { 84 | color = color1; 85 | } 86 | 87 | return float4(color.rgb, color0.a); 88 | } 89 | -------------------------------------------------------------------------------- /CustomSetup/CustomSetup.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {B6C07936-A1D2-4A80-B559-B55E3F15CC97} 8 | Library 9 | Properties 10 | CustomSetup 11 | CustomSetup 12 | v4.7.2 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | ..\bin\x64\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | ..\bin\x64\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | Component 48 | 49 | 50 | 51 | 52 | 53 | if not defined PFX_PASSWORD goto skip_signing 54 | if not defined PFX_NAME set PFX_NAME=selfsigncert 55 | $(SolutionDir)\installer\signtool.exe sign /d "OpenXR Quad Views Foveated" /du "https://mbucchia.github.io/Quad-Views-Foveated/" /f $(SolutionDir)\installer\%25PFX_NAME%25.pfx /p "%25PFX_PASSWORD%25" /v $(TargetPath) 56 | REM The installer picks up the file from obj... Sign this one too. 57 | $(SolutionDir)\installer\signtool.exe sign /d "OpenXR Quad Views Foveated" /du "https://mbucchia.github.io/Quad-Views-Foveated/" /f $(SolutionDir)\installer\%25PFX_NAME%25.pfx /p "%25PFX_PASSWORD%25" /v $(ProjectDir)\obj\$(ConfigurationName)\$(TargetFilename) 58 | :skip_signing 59 | 60 | 61 | 62 | if not exist $(SolutionDir)\version.info goto :skip_version 63 | for /f "delims== tokens=1,2" %25%25G in ($(SolutionDir)\version.info) do set %25%25G=%25%25H 64 | $(SolutionDir)\scripts\sed.exe -i "s/^\[assembly: AssemblyVersion(\".*\")\]$/\[assembly: AssemblyVersion(\"%25major%25.%25minor%25.%25patch%25.0\")\]/g" $(ProjectDir)\Properties\AssemblyInfo.cs 65 | $(SolutionDir)\scripts\sed.exe -i "s/^\[assembly: AssemblyFileVersion(\".*\")\]$/\[assembly: AssemblyFileVersion(\"%25major%25.%25minor%25.%25patch%25.0\")\]/g" $(ProjectDir)\Properties\AssemblyInfo.cs 66 | :skip_version 67 | 68 | 69 | -------------------------------------------------------------------------------- /openxr-api-layer/SharpeningCS.hlsl: -------------------------------------------------------------------------------- 1 | // CAS Sample 2 | // 3 | // Copyright(c) 2019 Advanced Micro Devices, Inc.All rights reserved. 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files(the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions : 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 15 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 18 | // THE SOFTWARE. 19 | 20 | cbuffer cb : register(b0) { 21 | uint4 const0; 22 | uint4 const1; 23 | }; 24 | 25 | Texture2D InputTexture : register(t0); 26 | RWTexture2D OutputTexture : register(u0); 27 | 28 | #define A_GPU 1 29 | #define A_HLSL 1 30 | 31 | #if CAS_SAMPLE_FP16 32 | 33 | #define A_HALF 1 34 | #define CAS_PACKED_ONLY 1 35 | 36 | #endif 37 | 38 | #include "ffx_a.h" 39 | 40 | #if CAS_SAMPLE_FP16 41 | 42 | AH3 CasLoadH(ASW2 p) { 43 | return InputTexture.Load(ASU3(p, 0)).rgb; 44 | } 45 | 46 | // Lets you transform input from the load into a linear color space between 0 and 1. See ffx_cas.h 47 | // In this case, our input is already linear and between 0 and 1 48 | void CasInputH(inout AH2 r, inout AH2 g, inout AH2 b) { 49 | } 50 | 51 | #else 52 | 53 | AF3 CasLoad(ASU2 p) { 54 | return InputTexture.Load(int3(p, 0)).rgb; 55 | } 56 | 57 | // Lets you transform input from the load into a linear color space between 0 and 1. See ffx_cas.h 58 | // In this case, our input is already linear and between 0 and 1 59 | void CasInput(inout AF1 r, inout AF1 g, inout AF1 b) { 60 | } 61 | 62 | #endif 63 | 64 | #include "ffx_cas.h" 65 | 66 | [numthreads(WIDTH, HEIGHT, DEPTH)] 67 | void main(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID) { 68 | // Do remapping of local xy in workgroup for a more PS-like swizzle pattern. 69 | AU2 gxy = ARmp8x8(LocalThreadId.x) + AU2(WorkGroupId.x << 4u, WorkGroupId.y << 4u); 70 | 71 | bool sharpenOnly; 72 | #if CAS_SAMPLE_SHARPEN_ONLY 73 | sharpenOnly = true; 74 | #else 75 | sharpenOnly = false; 76 | #endif 77 | 78 | #if CAS_SAMPLE_FP16 79 | 80 | // Filter. 81 | AH4 c0, c1; 82 | AH2 cR, cG, cB; 83 | 84 | CasFilterH(cR, cG, cB, gxy, const0, const1, sharpenOnly); 85 | CasDepack(c0, c1, cR, cG, cB); 86 | OutputTexture[ASU2(gxy)] = AF4(c0); 87 | OutputTexture[ASU2(gxy) + ASU2(8, 0)] = AF4(c1); 88 | gxy.y += 8u; 89 | 90 | CasFilterH(cR, cG, cB, gxy, const0, const1, sharpenOnly); 91 | CasDepack(c0, c1, cR, cG, cB); 92 | OutputTexture[ASU2(gxy)] = AF4(c0); 93 | OutputTexture[ASU2(gxy) + ASU2(8, 0)] = AF4(c1); 94 | 95 | #else 96 | 97 | // Filter. 98 | AF3 c; 99 | 100 | CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly); 101 | OutputTexture[ASU2(gxy)] = AF4(c, 1); 102 | gxy.x += 8u; 103 | 104 | CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly); 105 | OutputTexture[ASU2(gxy)] = AF4(c, 1); 106 | gxy.y += 8u; 107 | 108 | CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly); 109 | OutputTexture[ASU2(gxy)] = AF4(c, 1); 110 | gxy.x -= 8u; 111 | 112 | CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly); 113 | OutputTexture[ASU2(gxy)] = AF4(c, 1); 114 | 115 | #endif 116 | } -------------------------------------------------------------------------------- /openxr-api-layer/views.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | namespace xr { 26 | 27 | namespace QuadView { 28 | constexpr uint32_t Left = 0; 29 | constexpr uint32_t Right = 1; 30 | constexpr uint32_t FocusLeft = 2; 31 | constexpr uint32_t FocusRight = 3; 32 | constexpr uint32_t Count = 4; 33 | } // namespace QuadView 34 | 35 | namespace math { 36 | 37 | static XrFovf ComputeBoundingFov(const XrFovf& fullFov, const XrVector2f& min, const XrVector2f& max) { 38 | const float width = std::max(0.01f, max.x - min.x); 39 | const float height = std::max(0.01f, max.y - min.y); 40 | const XrVector2f center = (min + max) / 2.f; 41 | 42 | const auto fullProjection = ComposeProjectionMatrix(fullFov, {0.001f, 100.f}); 43 | // clang-format off 44 | const auto boundingFov = 45 | DirectX::XMMATRIX(2.f / width, 0.f, 0.f, 0.f, 46 | 0.f, 2.f / height, 0.f, 0.f, 47 | 0.f, 0.f, 1.f, 0.f, 48 | -(2.f * center.x) / width, 1*-(2.f * center.y) / height, 0.f, 1.f); 49 | // clang-format on 50 | DirectX::XMFLOAT4X4 projection; 51 | DirectX::XMStoreFloat4x4(&projection, DirectX::XMMatrixMultiply(fullProjection, boundingFov)); 52 | return DecomposeProjectionMatrix(projection); 53 | } 54 | 55 | static bool ProjectPoint(const XrView& eyeInViewSpace, 56 | const XrVector3f& forward, 57 | XrVector2f& projectedPosition) { 58 | // 1) Compute the view space to camera transform for this eye. 59 | const auto cameraProjection = ComposeProjectionMatrix(eyeInViewSpace.fov, {0.001f, 100.f}); 60 | const auto cameraView = LoadXrPose(eyeInViewSpace.pose); 61 | const auto viewToCamera = DirectX::XMMatrixMultiply(cameraProjection, cameraView); 62 | 63 | // 2) Transform the 3D point to camera space. 64 | const auto projectedInCameraSpace = 65 | DirectX::XMVector3Transform(DirectX::XMVectorSet(forward.x, forward.y, forward.z, 1.f), viewToCamera); 66 | 67 | // 3) Project the 3D point in camera space to a 2D point in normalized device coordinates. 68 | // 4) Output NDC (-1,+1) 69 | XrVector4f point; 70 | xr::math::StoreXrVector4(&point, projectedInCameraSpace); 71 | if (std::abs(point.w) < FLT_EPSILON) { 72 | return false; 73 | } 74 | projectedPosition.x = point.x / point.w; 75 | projectedPosition.y = point.y / point.w; 76 | 77 | return true; 78 | } 79 | 80 | } // namespace math 81 | 82 | } // namespace xr 83 | -------------------------------------------------------------------------------- /XR_APILAYER_MBUCCHIA_quad_views_foveated.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.7.34031.279 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openxr-api-layer", "openxr-api-layer\openxr-api-layer.vcxproj", "{93D573D0-634F-4BA0-8FE0-FB63D7D00A05}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Files", "Solution Files", "{A53ED6CB-95D3-4833-8A16-C6A588F16F6E}" 9 | ProjectSection(SolutionItems) = preProject 10 | .clang-format = .clang-format 11 | .gitattributes = .gitattributes 12 | .gitignore = .gitignore 13 | LICENSE = LICENSE 14 | README.md = README.md 15 | version.info = version.info 16 | EndProjectSection 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{BA775DE9-671E-4E3B-92AC-9828C2AAF490}" 19 | ProjectSection(SolutionItems) = preProject 20 | scripts\Capture-ETL.bat = scripts\Capture-ETL.bat 21 | scripts\Install-Layer.ps1 = scripts\Install-Layer.ps1 22 | scripts\Install-Layer32.ps1 = scripts\Install-Layer32.ps1 23 | scripts\Tracing.wprp = scripts\Tracing.wprp 24 | scripts\Uninstall-Layer.ps1 = scripts\Uninstall-Layer.ps1 25 | scripts\Uninstall-Layer32.ps1 = scripts\Uninstall-Layer32.ps1 26 | EndProjectSection 27 | EndProject 28 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub", "GitHub", "{AE910B95-8E3F-42D4-B121-8C477A679441}" 29 | ProjectSection(SolutionItems) = preProject 30 | .github\workflows\msbuild.yml = .github\workflows\msbuild.yml 31 | EndProjectSection 32 | EndProject 33 | Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "installer", "installer\installer.vdproj", "{8CD923BD-6B3B-4409-8290-FDAC00DCB59D}" 34 | EndProject 35 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomSetup", "CustomSetup\CustomSetup.csproj", "{B6C07936-A1D2-4A80-B559-B55E3F15CC97}" 36 | ProjectSection(ProjectDependencies) = postProject 37 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05} = {93D573D0-634F-4BA0-8FE0-FB63D7D00A05} 38 | EndProjectSection 39 | EndProject 40 | Global 41 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 42 | Debug|Win32 = Debug|Win32 43 | Debug|x64 = Debug|x64 44 | Release|Win32 = Release|Win32 45 | Release|x64 = Release|x64 46 | EndGlobalSection 47 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 48 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Debug|Win32.ActiveCfg = Debug|Win32 49 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Debug|Win32.Build.0 = Debug|Win32 50 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Debug|x64.ActiveCfg = Debug|x64 51 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Debug|x64.Build.0 = Debug|x64 52 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Release|Win32.ActiveCfg = Release|Win32 53 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Release|Win32.Build.0 = Release|Win32 54 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Release|x64.ActiveCfg = Release|x64 55 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Release|x64.Build.0 = Release|x64 56 | {8CD923BD-6B3B-4409-8290-FDAC00DCB59D}.Debug|Win32.ActiveCfg = Debug 57 | {8CD923BD-6B3B-4409-8290-FDAC00DCB59D}.Debug|x64.ActiveCfg = Debug 58 | {8CD923BD-6B3B-4409-8290-FDAC00DCB59D}.Release|Win32.ActiveCfg = Release 59 | {8CD923BD-6B3B-4409-8290-FDAC00DCB59D}.Release|x64.ActiveCfg = Release 60 | {8CD923BD-6B3B-4409-8290-FDAC00DCB59D}.Release|x64.Build.0 = Release 61 | {B6C07936-A1D2-4A80-B559-B55E3F15CC97}.Debug|Win32.ActiveCfg = Debug|Any CPU 62 | {B6C07936-A1D2-4A80-B559-B55E3F15CC97}.Debug|x64.ActiveCfg = Debug|Any CPU 63 | {B6C07936-A1D2-4A80-B559-B55E3F15CC97}.Release|Win32.ActiveCfg = Release|Any CPU 64 | {B6C07936-A1D2-4A80-B559-B55E3F15CC97}.Release|x64.ActiveCfg = Release|Any CPU 65 | {B6C07936-A1D2-4A80-B559-B55E3F15CC97}.Release|x64.Build.0 = Release|Any CPU 66 | EndGlobalSection 67 | GlobalSection(SolutionProperties) = preSolution 68 | HideSolutionNode = FALSE 69 | EndGlobalSection 70 | GlobalSection(NestedProjects) = preSolution 71 | {BA775DE9-671E-4E3B-92AC-9828C2AAF490} = {A53ED6CB-95D3-4833-8A16-C6A588F16F6E} 72 | {AE910B95-8E3F-42D4-B121-8C477A679441} = {A53ED6CB-95D3-4833-8A16-C6A588F16F6E} 73 | EndGlobalSection 74 | GlobalSection(ExtensibilityGlobals) = postSolution 75 | SolutionGuid = {07E77829-9766-4585-AC6C-0A28BA014E77} 76 | EndGlobalSection 77 | EndGlobal 78 | -------------------------------------------------------------------------------- /openxr-api-layer/openxr-api-layer.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {060fbbc6-44b1-4494-b904-cb9719cec138} 14 | 15 | 16 | {6373cac3-099d-4c47-8bd7-9127fde36131} 17 | 18 | 19 | {18c0442a-dcc5-451b-9beb-33acd4efbd5f} 20 | 21 | 22 | {f859919c-f06c-4ab3-93c7-9fb1cfc3df3e} 23 | 24 | 25 | 26 | 27 | Header Files 28 | 29 | 30 | Header Files 31 | 32 | 33 | Framework 34 | 35 | 36 | Framework 37 | 38 | 39 | Framework 40 | 41 | 42 | Framework 43 | 44 | 45 | Utilities 46 | 47 | 48 | Utilities 49 | 50 | 51 | Utilities 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files 58 | 59 | 60 | 61 | 62 | Source Files 63 | 64 | 65 | Source Files 66 | 67 | 68 | Framework 69 | 70 | 71 | Framework 72 | 73 | 74 | Framework 75 | 76 | 77 | Framework 78 | 79 | 80 | Utilities 81 | 82 | 83 | Utilities 84 | 85 | 86 | Utilities 87 | 88 | 89 | Utilities 90 | 91 | 92 | Utilities 93 | 94 | 95 | fmt 96 | 97 | 98 | 99 | 100 | Framework 101 | 102 | 103 | Framework 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | Shader Files 114 | 115 | 116 | Shader Files 117 | 118 | 119 | Shader Files 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /openxr-api-layer/utils/inputs.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include "general.h" 26 | 27 | namespace openxr_api_layer::utils::inputs { 28 | 29 | namespace Hands { 30 | constexpr uint32_t Left = 0; 31 | constexpr uint32_t Right = 1; 32 | constexpr uint32_t Count = 2; 33 | }; // namespace Hands 34 | 35 | // Input methods to use. 36 | enum class InputMethod { 37 | // Use the motion controller position and aim. 38 | MotionControllerSpatial = (1 << 0), 39 | 40 | // Use the motion controller buttons. 41 | MotionControllerButtons = (1 << 1), 42 | 43 | // Use the motion controller haptics. 44 | MotionControllerHaptics = (1 << 2), 45 | }; 46 | DEFINE_ENUM_FLAG_OPERATORS(InputMethod); 47 | 48 | // Common denominator of what is supported on all controllers. 49 | enum class MotionControllerButton { 50 | Select = 0, 51 | Menu, 52 | Squeeze, 53 | ThumbstickClick, 54 | }; 55 | 56 | // A container for user session data. 57 | // This class is meant to be extended by a caller before use with IInputFramework::setSessionData() and 58 | // IInputFramework::getSessionData(). 59 | struct IInputSessionData { 60 | virtual ~IInputSessionData() = default; 61 | }; 62 | 63 | // A collection of hooks and utilities to perform inputs in the layer. 64 | struct IInputFramework { 65 | virtual ~IInputFramework() = default; 66 | 67 | virtual XrSession getSessionHandle() const = 0; 68 | 69 | virtual void setSessionData(std::unique_ptr sessionData) = 0; 70 | virtual IInputSessionData* getSessionDataPtr() const = 0; 71 | 72 | virtual void blockApplicationInput(bool blocked) = 0; 73 | 74 | // Can only be called if the MotionControllerSpatial input method was requested. 75 | virtual XrSpaceLocationFlags locateMotionController(uint32_t side, XrSpace baseSpace, XrPosef& pose) const = 0; 76 | virtual XrSpace getMotionControllerSpace(uint32_t side) const = 0; 77 | 78 | // Can only be called if the MotionControllerButtons input method was requested. 79 | virtual bool getMotionControllerButtonState(uint32_t side, MotionControllerButton button) const = 0; 80 | virtual XrVector2f getMotionControllerThumbstickState(uint32_t side) const = 0; 81 | 82 | // Can only be called if the MotionControllerHaptics input method was requested. 83 | virtual void pulseMotionControllerHaptics(uint32_t side, float strength) const = 0; 84 | 85 | template 86 | typename SessionData* getSessionData() const { 87 | return reinterpret_cast(getSessionDataPtr()); 88 | } 89 | }; 90 | 91 | // A factory to create input frameworks for each session. 92 | struct IInputFrameworkFactory { 93 | virtual ~IInputFrameworkFactory() = default; 94 | 95 | // Must be called after chaining to the upstream xrGetInstanceProcAddr() implementation. 96 | virtual void xrGetInstanceProcAddr_post(XrInstance instance, 97 | const char* name, 98 | PFN_xrVoidFunction* function) = 0; 99 | 100 | virtual IInputFramework* getInputFramework(XrSession session) = 0; 101 | }; 102 | 103 | std::shared_ptr createInputFrameworkFactory(const XrInstanceCreateInfo& info, 104 | XrInstance instance, 105 | PFN_xrGetInstanceProcAddr xrGetInstanceProcAddr, 106 | InputMethod methods); 107 | 108 | } // namespace openxr_api_layer::utils::inputs 109 | -------------------------------------------------------------------------------- /openxr-api-layer/framework/entry.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2021-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "pch.h" 24 | 25 | #include 26 | 27 | #include "dispatch.h" 28 | #include "log.h" 29 | #include "version.h" 30 | 31 | namespace openxr_api_layer { 32 | // The path where the DLL is loaded from (eg: to load data files). 33 | std::filesystem::path dllHome; 34 | 35 | // The path that is writable (eg: to store logs). 36 | std::filesystem::path localAppData; 37 | 38 | namespace log { 39 | // The file logger. 40 | std::ofstream logStream; 41 | } // namespace log 42 | 43 | const std::string VersionString = fmt::format("v{}.{}.{}", LayerVersionMajor, LayerVersionMinor, LayerVersionPatch); 44 | 45 | } // namespace openxr_api_layer 46 | 47 | using namespace openxr_api_layer; 48 | using namespace openxr_api_layer::log; 49 | 50 | extern "C" { 51 | 52 | // Entry point for the loader. 53 | XrResult __declspec(dllexport) XRAPI_CALL 54 | xrNegotiateLoaderApiLayerInterface(const XrNegotiateLoaderInfo* const loaderInfo, 55 | const char* const apiLayerName, 56 | XrNegotiateApiLayerRequest* const apiLayerRequest) { 57 | TraceLoggingWrite(g_traceProvider, "xrNegotiateLoaderApiLayerInterface"); 58 | 59 | // Retrieve the path of the DLL. 60 | if (dllHome.empty()) { 61 | HMODULE module; 62 | if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 63 | (LPCSTR)&dllHome, 64 | &module)) { 65 | char path[_MAX_PATH]; 66 | GetModuleFileNameA(module, path, sizeof(path)); 67 | dllHome = std::filesystem::path(path).parent_path(); 68 | } 69 | } 70 | 71 | localAppData = std::filesystem::path(getenv("LOCALAPPDATA")) / LayerPrettyName; 72 | CreateDirectoryA(localAppData.string().c_str(), nullptr); 73 | 74 | // Start logging to file. 75 | if (!logStream.is_open()) { 76 | std::string logFile = (localAppData / (LayerPrettyName + ".log")).string(); 77 | logStream.open(logFile, std::ios_base::ate); 78 | } 79 | 80 | DebugLog("--> xrNegotiateLoaderApiLayerInterface\n"); 81 | 82 | if (apiLayerName && std::string_view(apiLayerName) != LAYER_NAME) { 83 | ErrorLog(fmt::format("Invalid apiLayerName \"{}\"\n", apiLayerName)); 84 | return XR_ERROR_INITIALIZATION_FAILED; 85 | } 86 | 87 | if (!loaderInfo || !apiLayerRequest || loaderInfo->structType != XR_LOADER_INTERFACE_STRUCT_LOADER_INFO || 88 | loaderInfo->structVersion != XR_LOADER_INFO_STRUCT_VERSION || 89 | loaderInfo->structSize != sizeof(XrNegotiateLoaderInfo) || 90 | apiLayerRequest->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST || 91 | apiLayerRequest->structVersion != XR_API_LAYER_INFO_STRUCT_VERSION || 92 | apiLayerRequest->structSize != sizeof(XrNegotiateApiLayerRequest) || 93 | loaderInfo->minInterfaceVersion > XR_CURRENT_LOADER_API_LAYER_VERSION || 94 | loaderInfo->maxInterfaceVersion < XR_CURRENT_LOADER_API_LAYER_VERSION || 95 | loaderInfo->maxInterfaceVersion > XR_CURRENT_LOADER_API_LAYER_VERSION || 96 | loaderInfo->maxApiVersion < XR_CURRENT_API_VERSION || loaderInfo->minApiVersion > XR_CURRENT_API_VERSION) { 97 | ErrorLog("xrNegotiateLoaderApiLayerInterface validation failed\n"); 98 | return XR_ERROR_INITIALIZATION_FAILED; 99 | } 100 | 101 | // Setup our layer to intercept OpenXR calls. 102 | apiLayerRequest->layerInterfaceVersion = XR_CURRENT_LOADER_API_LAYER_VERSION; 103 | apiLayerRequest->layerApiVersion = XR_CURRENT_API_VERSION; 104 | apiLayerRequest->getInstanceProcAddr = reinterpret_cast(xrGetInstanceProcAddr); 105 | apiLayerRequest->createApiLayerInstance = reinterpret_cast(xrCreateApiLayerInstance); 106 | 107 | DebugLog("<-- xrNegotiateLoaderApiLayerInterface\n"); 108 | 109 | Log(fmt::format("{} layer ({}) is active\n", LayerName, VersionString)); 110 | 111 | TraceLoggingWrite(g_traceProvider, "xrNegotiateLoaderApiLayerInterface_Complete"); 112 | 113 | return XR_SUCCESS; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /CustomSetup/CustomSetupActions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Configuration; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Windows.Forms; 10 | 11 | // Reference: https://www.c-sharpcorner.com/article/how-to-perform-custom-actions-and-upgrade-using-visual-studio-installer/ 12 | namespace SetupCustomActions 13 | { 14 | [RunInstaller(true)] 15 | public partial class CustomActions : System.Configuration.Install.Installer 16 | { 17 | public CustomActions() 18 | { 19 | } 20 | 21 | private class WindowWrapper : IWin32Window 22 | { 23 | private readonly IntPtr hwnd; 24 | 25 | public IntPtr Handle 26 | { 27 | get { return hwnd; } 28 | } 29 | 30 | public WindowWrapper(IntPtr handle) 31 | { 32 | hwnd = handle; 33 | } 34 | } 35 | 36 | protected override void OnAfterInstall(IDictionary savedState) 37 | { 38 | var installPath = Path.GetDirectoryName(base.Context.Parameters["AssemblyPath"]); 39 | 40 | // https://stackoverflow.com/questions/6213498/custom-installer-in-net-showing-form-behind-installer 41 | var proc = Process.GetProcessesByName("msiexec").FirstOrDefault(p => p.MainWindowTitle == "OpenXR-Quad-Views-Foveated"); 42 | var owner = proc != null ? new WindowWrapper(proc.MainWindowHandle) : null; 43 | 44 | Microsoft.Win32.RegistryKey key; 45 | { 46 | key = Microsoft.Win32.Registry.LocalMachine.CreateSubKey("SOFTWARE\\Khronos\\OpenXR\\1\\ApiLayers\\Implicit"); 47 | var jsonPath = installPath + "\\openxr-api-layer.json"; 48 | 49 | var existingValues = key.GetValueNames(); 50 | 51 | ReOrderApiLayers(key, jsonPath); 52 | 53 | // Issue the appropriate compatibility warnings. 54 | foreach (var value in existingValues) 55 | { 56 | if (value.EndsWith("\\XR_APILAYER_MBUCCHIA_toolkit.json")) 57 | { 58 | MessageBox.Show(owner, "OpenXR Toolkit was detected on your system.\n\n" + 59 | "OpenXR Toolkit is compatible with applications such as DCS World or Pavlov VR when they use quad views rendering with this tool.\n" + 60 | "However you might need to clear all prior OpenXR Toolkit settings for these applications, by using OpenXR Toolkit Safe Mode then selecting Menu -> Restore Defaults in the application.", 61 | "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); 62 | } 63 | } 64 | 65 | key.Close(); 66 | } 67 | { 68 | key = Microsoft.Win32.Registry.LocalMachine.CreateSubKey("SOFTWARE\\WOW6432Node\\Khronos\\OpenXR\\1\\ApiLayers\\Implicit"); 69 | var jsonPath = installPath + "\\openxr-api-layer-32.json"; 70 | 71 | ReOrderApiLayers(key, jsonPath); 72 | 73 | key.Close(); 74 | } 75 | 76 | base.OnAfterInstall(savedState); 77 | } 78 | 79 | private void ReOrderApiLayers(Microsoft.Win32.RegistryKey key, string jsonPath) 80 | { 81 | // We want to add our layer near the very beginning, so that any other layer like the OpenXR Toolkit layer are following us. 82 | // However, the layer seem to confuse XRNeckSafer and OpenXR-MotionCompensation, so we want to add ourselves after them. 83 | // We delete all entries, re-create the XRNeckSafer and OpenXR-MotionCompensation ones if needed, then create our own, and re-create the remaining entries. 84 | 85 | List existingValues = new List(key.GetValueNames()); 86 | var entriesValues = new Dictionary(); 87 | foreach (var value in existingValues) 88 | { 89 | entriesValues.Add(value, key.GetValue(value)); 90 | key.DeleteValue(value); 91 | } 92 | 93 | void PullToFront(string endsWith) 94 | { 95 | var index = existingValues.FindIndex(entry => entry.EndsWith(endsWith)); 96 | if (index > 0) 97 | { 98 | existingValues.Insert(0, existingValues[index]); 99 | existingValues.RemoveAt(1 + index); 100 | } 101 | } 102 | 103 | // Start at the beginning. 104 | existingValues.Insert(0, jsonPath); 105 | entriesValues.Remove(jsonPath); 106 | entriesValues.Add(jsonPath, 0); 107 | 108 | // Do not re-create keys for previous versions of our layer. 109 | existingValues.RemoveAll(entry => entry.EndsWith("OpenXR-Meta-Foveated\\openxr-api-layer.json")); 110 | 111 | // XRNeckSafer must always be first. OXRMC can be next. 112 | // We pull them in the opposite order. 113 | PullToFront("\\XR_APILAYER_NOVENDOR_motion_compensation.json"); 114 | PullToFront("\\XR_APILAYER_NOVENDOR_XRNeckSafer.json"); 115 | 116 | // Now we create the keys in the desired order. 117 | foreach (var value in existingValues) 118 | { 119 | key.SetValue(value, entriesValues[value]); 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /openxr-api-layer/utils/general.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "pch.h" 24 | 25 | #include "general.h" 26 | 27 | namespace { 28 | 29 | using namespace openxr_api_layer::utils; 30 | using namespace DirectX; 31 | 32 | class CpuTimer : public general::ITimer { 33 | using clock = std::chrono::high_resolution_clock; 34 | 35 | public: 36 | void start() override { 37 | m_timeStart = clock::now(); 38 | } 39 | 40 | void stop() override { 41 | m_duration += clock::now() - m_timeStart; 42 | } 43 | 44 | uint64_t query() const override { 45 | const auto duration = std::chrono::duration_cast(m_duration); 46 | m_duration = clock::duration::zero(); 47 | return duration.count(); 48 | } 49 | 50 | private: 51 | clock::time_point m_timeStart; 52 | mutable clock::duration m_duration{0}; 53 | }; 54 | 55 | // Taken from 56 | // https://github.com/microsoft/OpenXR-MixedReality/blob/main/samples/SceneUnderstandingUwp/Scene_Placement.cpp 57 | bool XM_CALLCONV rayIntersectQuad(DirectX::FXMVECTOR rayPosition, 58 | DirectX::FXMVECTOR rayDirection, 59 | DirectX::FXMVECTOR v0, 60 | DirectX::FXMVECTOR v1, 61 | DirectX::FXMVECTOR v2, 62 | DirectX::FXMVECTOR v3, 63 | XrPosef* hitPose, 64 | float& distance) { 65 | // Not optimal. Should be possible to determine which triangle to test. 66 | bool hit = TriangleTests::Intersects(rayPosition, rayDirection, v0, v1, v2, distance); 67 | if (!hit) { 68 | hit = TriangleTests::Intersects(rayPosition, rayDirection, v3, v2, v0, distance); 69 | } 70 | if (hit && hitPose != nullptr) { 71 | FXMVECTOR hitPosition = XMVectorAdd(rayPosition, XMVectorScale(rayDirection, distance)); 72 | FXMVECTOR plane = XMPlaneFromPoints(v0, v2, v1); 73 | 74 | // p' = p - (n . p + d) * n 75 | // Project the ray position onto the plane 76 | float t = XMVectorGetX(XMVector3Dot(plane, rayPosition)) + XMVectorGetW(plane); 77 | FXMVECTOR projPoint = XMVectorSubtract(rayPosition, XMVectorMultiply(XMVectorSet(t, t, t, 0), plane)); 78 | 79 | // From the projected ray position, look towards the hit position and make the plane's normal "up" 80 | FXMVECTOR forward = XMVectorSubtract(hitPosition, projPoint); 81 | XMMATRIX virtualToGazeOrientation = XMMatrixLookToRH(hitPosition, forward, plane); 82 | xr::math::StoreXrPose(hitPose, XMMatrixInverse(nullptr, virtualToGazeOrientation)); 83 | } 84 | return hit; 85 | } 86 | 87 | } // namespace 88 | 89 | namespace openxr_api_layer::utils::general { 90 | 91 | std::shared_ptr createTimer() { 92 | return std::make_shared(); 93 | } 94 | 95 | bool hitTest(const XrPosef& ray, const XrPosef& quadCenter, const XrExtent2Df& quadSize, XrPosef& hitPose) { 96 | using namespace DirectX; 97 | 98 | // Taken from 99 | // https://github.com/microsoft/OpenXR-MixedReality/blob/main/samples/SceneUnderstandingUwp/Scene_Placement.cpp 100 | 101 | // Clockwise order 102 | const float halfWidth = quadSize.width / 2.0f; 103 | const float halfHeight = quadSize.height / 2.0f; 104 | auto v0 = XMVectorSet(-halfWidth, -halfHeight, 0, 1); 105 | auto v1 = XMVectorSet(-halfWidth, halfHeight, 0, 1); 106 | auto v2 = XMVectorSet(halfWidth, halfHeight, 0, 1); 107 | auto v3 = XMVectorSet(halfWidth, -halfHeight, 0, 1); 108 | const auto matrix = xr::math::LoadXrPose(quadCenter); 109 | v0 = XMVector4Transform(v0, matrix); 110 | v1 = XMVector4Transform(v1, matrix); 111 | v2 = XMVector4Transform(v2, matrix); 112 | v3 = XMVector4Transform(v3, matrix); 113 | 114 | XMVECTOR rayPosition = xr::math::LoadXrVector3(ray.position); 115 | 116 | const auto forward = XMVectorSet(0, 0, -1, 0); 117 | const auto rotation = xr::math::LoadXrQuaternion(ray.orientation); 118 | XMVECTOR rayDirection = XMVector3Rotate(forward, rotation); 119 | 120 | float distance = 0.0f; 121 | return rayIntersectQuad(rayPosition, rayDirection, v0, v1, v2, v3, &hitPose, distance); 122 | } 123 | 124 | // https://gamedev.stackexchange.com/questions/136652/uv-world-mapping-in-shader-with-unity/136720#136720 125 | XrVector2f getUVCoordinates(const XrVector3f& point, const XrPosef& quadCenter, const XrExtent2Df& quadSize) { 126 | using namespace xr::math; 127 | 128 | const XrVector3f normal = 129 | Pose::Multiply(Pose::MakePose(quadCenter.orientation, XrVector3f{}), Pose::Translation({0, 0, 1})).position; 130 | 131 | XrVector3f uDirection, vDirection; 132 | vDirection = {0, 0, 1}; 133 | if (std::abs(normal.y) < 1.0f) { 134 | vDirection = Normalize(XrVector3f{0, 1, 0} - normal.y * normal); 135 | } 136 | 137 | uDirection = Normalize(Cross(normal, vDirection)); 138 | 139 | return { 140 | (-Dot(uDirection, point) + (quadSize.width / 2.f)) / quadSize.width, 141 | (-Dot(vDirection, point) + (quadSize.height / 2.f)) / quadSize.height, 142 | }; 143 | } 144 | 145 | } // namespace openxr_api_layer::utils::general 146 | -------------------------------------------------------------------------------- /installer/EULA.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033{\fonttbl{\f0\fnil\fcharset0 LiberationSans-Bold;}{\f1\fnil\fcharset0 LiberationSans;}{\f2\fnil\fcharset0 Calibri;}} 2 | {\*\generator Riched20 10.0.19041}\viewkind4\uc1 3 | \pard\b\f0\fs25\lang9 END USER LICENSE AGREEMENT\par 4 | \par 5 | \b0\f1\fs19 The herein contained End User License Agreement (the "\b\f0 Agreement\b0\f1 " or "\b\f0 License\b0\f1 " or "\b\f0 EULA\b0\f1 ") shall be considered a legally binding agreement between you (an individual or an entity, hereinafter "\b\f0 Licensee\b0\f1 " or "\b\f0 You\b0\f1 " or "\b\f0 Your\b0\f1 ") and Matthieu Bucchianeri for the use of the specified software: OpenXR Quad Views Foveated, which may include related printed material, media and any other components and/or software modules, including but not limited to required drivers (the "\b\f0 Product\b0\f1 "). Other aspects of the Product may also include, but are not limited to, software updates and any upgrades necessary that Matthieu Bucchianeri may supply to You or make available to You, or that You obtain after the initial copy of the Product, and as such that said items are not accompanied by a separate license agreement or terms of use.\par 6 | 7 | \pard\tx8640\b\f0 BY WAY OF THE INSTALLATION, COPYING, DOWNLOADING, ACCESSING OR OTHERWISE USE OF THIS PRODUCT, YOU ARE AGREEING TO BE LEGALLY BOUND BY THE HEREIN CONTAINED TERMS OF THIS LICENSE AGREEMENT. IF YOU DO NOT AGREE TO BE BOUND BY THE TERMS OF THIS EULA, YOU THEN HAVE NO RIGHTS TO THE PRODUCT AND SHOULD THEREFORE NOT INSTALL, COPY, DOWNLOAD, ACCESS NOR USE THE PRODUCT.\par 8 | 9 | \pard THIS PRODUCT IS PROTECTED BY COPYRIGHT LAWS, AS WELL AS ANY OTHER INTELLECTUAL PROPERTY LAWS. THIS PRODUCT IS LICENSED AND NOT SOLD.\par 10 | \par 11 | 1. DEFINITIONS AND INTERPRETATIONS\par 12 | \b0\f1 1.01 "Agreement" or "License" or "EULA" shall mean this End User License Agreement.\par 13 | 1.02 "Licensee" or "You" or "Your" shall mean You, the individual or business entity licensing the Product under of the terms of this Agreement.\par 14 | 1.03 "Intellectual Property" means current and future worldwide rights under patent law, copyright law, trade secret law, trademark law, moral rights law, and other similar rights.\par 15 | 1.04 "Update" means maintenance of, or a fix to, a version of Product, including, but not limited to: a hot fix, patch, or enhancement, none of which function as a standalone service or other software package and which do not have an additional cost for any existing Licensee.\par 16 | 1.05 "Upgrade" means a major, standalone version of Product, which may include additional applications, features, or functionality.\par 17 | 1.06 A person includes a natural person, corporate or unincorporated body (whether or not having separate legal personality) and that person's legal and personal representatives, successors and permitted assigns.\par 18 | 1.07 Words in the singular shall include the plural and vice versa.\par 19 | 1.08 A reference to one gender shall include a reference to the other genders.\par 20 | 1.09 A reference to a statute, statutory provision or subordinate legislation is a reference to it as it is in force from time to time, taking account of any amendment or reenactment and includes any statute, statutory provision or subordinate legislation which it amends or re-enacts; provided that, as between the Parties, no such amendment or re-enactment shall apply for the purposes of this Agreement to the extent that it would impose any new or extended obligation, liability or restriction on, or otherwise adversely affect the rights of, any Party.\par 21 | 1.10 A reference to writing or written includes e-mail.\par 22 | 1.11 Any obligation in this Agreement on a person not to do something includes an obligation not to agree or allow that thing to be done.\par 23 | 1.12 Any phrase introduced by the terms "including", "include", "in particular" or any similar expression shall be construed as illustrative and shall not limit the sense of the words preceding those terms.\par 24 | 1.13 References to articles, sections, or clauses are to the articles, sections, and clauses of this Agreement.\par 25 | 1.14 "We", "us", and "our", means Matthieu Bucchianeri.\par 26 | \par 27 | \b\f0 2. LICENSE GRANT. \b0\f1 Matthieu Bucchianeri shall grant to You a non-exclusive license for the use and installation of the Product subject to all the terms and conditions set forth herein. Furthermore, this EULA shall also govern any and all software Updates and Upgrades provided by Matthieu Bucchianeri that would replace, overwrite and/or supplement the original installed version of the Product, unless those other Updates and Upgrades are covered under a separate license, in which case the terms of that license will govern.\par 28 | \b\f0\par 29 | 3. TERMINATION. \b0\f1 Should You breach this EULA, Your right to the use of the Product will immediately terminate and shall terminate without any notice being given. However, all provisions of this EULA, with the exception of the License grant, shall survive termination and will remain in effect. Upon termination of the License grant, You \b\f0 MUST \b0\f1 destroy any and all copies of the Product.\par 30 | \b\f0\par 31 | 4. UPDATES/UPGRADES\b0\f1 . Matthieu Bucchianeri may find the need to make available Updates or Upgrades for the Product, in accordance with the herein contained terms and conditions of this EULA. It shall be at the sole discretion of Matthieu Bucchianeri to make conditional releases of said Updates or Upgrades to You upon Your acceptance of another EULA or execution of another separate agreement. Should You elect to install and make use of these updates, You are therefore agreeing to be subject to all applicable license, terms and conditions of this EULA and/or any other agreement.\par 32 | \b\f0\par 33 | 5. DISCLAIMER OF WARRANTY. \b0\f1 Matthieu Bucchianeri shall use reasonable efforts consistent with prevailing industry standards to maintain Product in a manner which minimizes errors and interruptions.\par 34 | \b\f0 HOWEVER, MATTHIEU BUCCHIANERI DOES NOT WARRANT THAT PRODUCT WILL BE UNINTERRUPTED OR ERROR FREE; NOR DOES IT MAKE ANY WARRANTY AS TO THE RESULTS THAT MAY BE OBTAINED FROM USE OF PRODUCT. TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, EXCEPT AS EXPRESSLY PROVIDED FOR HEREIN AND NOT WITHSTANDING ANYTHING TO THE CONTRARY, NEITHER PARTY OR ANY OFFICER, DIRECTOR, SUBSIDIARY, AFFILIATE, OR EMPLOYEE OF EITHER PARTY, MAKES ANY OTHER WARRANTY OF ANY KIND, WHETHER EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR USE, AND NONINFRINGEMENT. NEITHER MATTHIEU BUCCHIANERI NOR ANY OFFICER, DIRECTOR, SUBSIDIARY, AFFILIATE, OR EMPLOYEE OF MATTHIEU BUCCHIANERI MAKES ANY REPRESENTATION OR WARRANTY ABOUT ANY CONTENT OR INFORMATION MADE ACCESSIBLE BY OR THROUGH PRODUCT.\par 35 | \par 36 | 6. LIMITATION OF LIABILITY AND REMEDIES\b0\f1 . In spite of \b ANY DAMAGES\b0 that You may or may not incur for \b ANY REASON\b0 , which may include, but are not limited to, any and all direct or general damages, the entire liability of Matthieu Bucchianeri and/or any of the aforementioned suppliers covered under the herein contained provisions of this EULA, along with Your exclusive remedy with regards to all of the foregoing, shall hereby be \b WAIVED\b0 by You for this Product. The aforementioned limitations, exclusions and any disclaimers shall apply to the maximum extent allowable by law, even should any proposed remedy fail its essential purpose.\par 37 | \b\f0\par 38 | 7. EXPORT CONTROLS\b0\f1 . By installing the Product, You hereby agree that You will comply with any and all applicable export laws, restrictions and all regulations of the U.S. Department of Commerce, U.S. Department of Treasury, and any other U.S. or foreign agency or authority with regards to this provision of the EULA. You expressly agree not to export or re-export, nor allow the export or re-export of the offered content in violation of any such law, restriction or regulation, including without limitation, export or re-export to any country subject to any and all applicable U.S. trade embargoes or to any prohibited destination, in any group specified in the current "Supplement No. 1 to Part 740 or the Commerce Control List specified in the then current Supplement No. 1 to Part 738 of the U.S. Export Administration Regulations (or any successor supplement or regulations)."\par 39 | \b\f0\par 40 | 8. U.S. GOVERNMENT END USERS\b0\f1 . The offered content is licensed by the U.S. Government with RESTRICTED RIGHTS. The use, duplication of, or the disclosure by the U.S. Government, shall be subject to restrictions in accordance with DFARS 252.227-7013 of the Technical Data and Computer Software clause, and 48 DCR 52.227-19 of the Commercial Computer Software clause, as applicable.\par 41 | \b\f0\par 42 | 9. MISCELLANEOUS\par 43 | 9.01 SUCCESSORS AND ASSIGNS\b0\f1 . This EULA, in its entirety, shall be legally binding upon and inure to the benefit of Matthieu Bucchianeri and You, our respective successors and permitted assigns.\par 44 | \b\f0 9.02 SEVERABILITY\b0\f1 . If any provision of this Agreement is held to be illegal, invalid or unenforceable by a court of competent jurisdiction, the remaining provisions shall not be affected.\par 45 | \b\f0 9.03 WAIVER\b0\f1 . If there is any waiver of any breach or failure to enforce any of the provisions contained herein, it shall not be deemed as a future waiver of said terms or a waiver of any other provision of this EULA.\par 46 | \b\f0 9.04 AMENDMENTS\b0\f1 . Any waiver, supplementation, modification or amendment to any provision of this EULA, shall only be effective when done so in writing and signed off by Matthieu Bucchianeri and You.\par 47 | \b\f0 9.05 GOVERNING LAW\b0\f1 . This EULA shall be governed solely by the laws of the State of Washington and of the United States. Should any action arise out of or in relation to this EULA, such action may be brought exclusively in the appropriate federal or state court in Bellevue, Washington, and as such, You and Matthieu Bucchianeri irrevocably consent to the jurisdiction of said court and venue for Bellevue, Washington.\par 48 | \b\f0 9.06 ASSIGNMENTS\b0\f1 . You may not assign or transfer any part of this Licensee without the written consent of Matthieu Bucchianeri, except that, if a change of control occurs (including a sale or merger), the Party experiencing the change of control may ensure this License remains in full force and effect by providing written notice to the other Party within thirty (30) days after the change of control.\par 49 | \b\f0 9.07 VALID AND BINDING \b0\f1 . This Agreement constitutes a valid and legally binding obligation of the Parties, enforceable against the Parties in accordance with its terms, subject in all respects to the effects of bankruptcy, insolvency, fraudulent conveyance, reorganization, moratorium and other laws relating to or affecting creditors' rights generally and general equitable principles.\par 50 | \b\f0 9.08 EFFECT OF TITLE AND HEADINGS\b0\f1 . The title of the Agreement and the headings of Sections, and Clauses are included for convenience and shall not affect the meaning of the Agreement or the Section.\par 51 | \b\f0 9.09 FORCE MAJEURE\b0\f1 . If either Party is prevented from performing or is unable to perform any of its obligations under this License due to causes beyond the reasonable control of the Party invoking this provision, including but not limited to acts of God, acts of civil or military authorities, riots or civil disobedience, wars, strikes or labor disputes (each, a "\b\f0 Force Majeure Event\b0\f1 "), such Party's performance shall be excused and the time for performance shall be extended accordingly provided that the Party immediately takes all reasonably necessary steps to resume full performance.\f2\fs22\par 52 | } 53 | -------------------------------------------------------------------------------- /openxr-api-layer/framework/dispatch.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2021-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "pch.h" 24 | 25 | #include 26 | 27 | #include "dispatch.h" 28 | #include "log.h" 29 | 30 | using namespace openxr_api_layer::log; 31 | 32 | namespace openxr_api_layer { 33 | 34 | // Entry point for creating the layer. 35 | XrResult XRAPI_CALL xrCreateApiLayerInstance(const XrInstanceCreateInfo* const instanceCreateInfo, 36 | const struct XrApiLayerCreateInfo* const apiLayerInfo, 37 | XrInstance* const instance) { 38 | TraceLocalActivity(local); 39 | TraceLoggingWriteStart(local, "xrCreateApiLayerInstance"); 40 | 41 | if (!apiLayerInfo || apiLayerInfo->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO || 42 | apiLayerInfo->structVersion != XR_API_LAYER_CREATE_INFO_STRUCT_VERSION || 43 | apiLayerInfo->structSize != sizeof(XrApiLayerCreateInfo) || !apiLayerInfo->nextInfo || 44 | apiLayerInfo->nextInfo->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO || 45 | apiLayerInfo->nextInfo->structVersion != XR_API_LAYER_NEXT_INFO_STRUCT_VERSION || 46 | apiLayerInfo->nextInfo->structSize != sizeof(XrApiLayerNextInfo) || !apiLayerInfo->nextInfo->layerName || 47 | std::string_view(apiLayerInfo->nextInfo->layerName) != LAYER_NAME || 48 | !apiLayerInfo->nextInfo->nextGetInstanceProcAddr || !apiLayerInfo->nextInfo->nextCreateApiLayerInstance) { 49 | ErrorLog("xrCreateApiLayerInstance validation failed\n"); 50 | return XR_ERROR_INITIALIZATION_FAILED; 51 | } 52 | 53 | // Dump the other layers. 54 | { 55 | auto info = apiLayerInfo->nextInfo; 56 | while (info) { 57 | TraceLoggingWriteTagged(local, "xrCreateApiLayerInstance", TLArg(info->layerName, "LayerName")); 58 | Log(fmt::format("Using layer: {}\n", info->layerName)); 59 | info = info->next; 60 | } 61 | } 62 | 63 | // Only request implicit extensions that are supported. 64 | // 65 | // While the OpenXR standard states that xrEnumerateInstanceExtensionProperties() can be queried without an 66 | // instance, this does not stand for API layers, since API layers implementation might rely on the next 67 | // xrGetInstanceProcAddr() pointer, which is not (yet) populated if no instance is created. 68 | // We create a dummy instance in order to do these checks. 69 | std::vector filteredImplicitExtensions; 70 | if (!implicitExtensions.empty()) { 71 | XrInstance dummyInstance = XR_NULL_HANDLE; 72 | 73 | // Call the chain to create a dummy instance. Request no extensions in order to speed things up. 74 | XrInstanceCreateInfo dummyCreateInfo = *instanceCreateInfo; 75 | dummyCreateInfo.enabledExtensionCount = 0; 76 | 77 | XrApiLayerCreateInfo chainApiLayerInfo = *apiLayerInfo; 78 | chainApiLayerInfo.nextInfo = apiLayerInfo->nextInfo->next; 79 | 80 | if (XR_SUCCEEDED(apiLayerInfo->nextInfo->nextCreateApiLayerInstance( 81 | &dummyCreateInfo, &chainApiLayerInfo, &dummyInstance))) { 82 | PFN_xrDestroyInstance xrDestroyInstance; 83 | CHECK_XRCMD(apiLayerInfo->nextInfo->nextGetInstanceProcAddr( 84 | dummyInstance, "xrDestroyInstance", reinterpret_cast(&xrDestroyInstance))); 85 | PFN_xrGetSystem xrGetSystem = nullptr; 86 | CHECK_XRCMD(apiLayerInfo->nextInfo->nextGetInstanceProcAddr( 87 | dummyInstance, "xrGetSystem", reinterpret_cast(&xrGetSystem))); 88 | PFN_xrGetSystemProperties xrGetSystemProperties = nullptr; 89 | CHECK_XRCMD(apiLayerInfo->nextInfo->nextGetInstanceProcAddr( 90 | dummyInstance, 91 | "xrGetSystemProperties", 92 | reinterpret_cast(&xrGetSystemProperties))); 93 | 94 | // Check the available extensions. 95 | PFN_xrEnumerateInstanceExtensionProperties xrEnumerateInstanceExtensionProperties; 96 | CHECK_XRCMD(apiLayerInfo->nextInfo->nextGetInstanceProcAddr( 97 | dummyInstance, 98 | "xrEnumerateInstanceExtensionProperties", 99 | reinterpret_cast(&xrEnumerateInstanceExtensionProperties))); 100 | 101 | uint32_t extensionsCount = 0; 102 | CHECK_XRCMD(xrEnumerateInstanceExtensionProperties(nullptr, 0, &extensionsCount, nullptr)); 103 | std::vector extensions(extensionsCount, {XR_TYPE_EXTENSION_PROPERTIES}); 104 | CHECK_XRCMD(xrEnumerateInstanceExtensionProperties( 105 | nullptr, extensionsCount, &extensionsCount, extensions.data())); 106 | 107 | for (const std::string& extensionName : implicitExtensions) { 108 | const auto matchExtensionName = [&](const XrExtensionProperties& properties) { 109 | return properties.extensionName == extensionName; 110 | }; 111 | if (std::find_if(extensions.cbegin(), extensions.cend(), matchExtensionName) != extensions.cend()) { 112 | filteredImplicitExtensions.push_back(extensionName); 113 | } else { 114 | Log(fmt::format("Cannot satisfy implicit extension request: {}\n", extensionName)); 115 | } 116 | } 117 | 118 | // Workaround: the Vive runtime does not seem to like our flow of destroying the instance 119 | // mid-initialization. We skip destruction and we will just create a second instance. 120 | if (xrGetSystem && xrGetSystemProperties) { 121 | XrSystemGetInfo getInfo{XR_TYPE_SYSTEM_GET_INFO}; 122 | getInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; 123 | XrSystemId systemId; 124 | if (XR_SUCCEEDED(xrGetSystem(dummyInstance, &getInfo, &systemId))) { 125 | XrSystemProperties systemProperties{XR_TYPE_SYSTEM_PROPERTIES}; 126 | CHECK_XRCMD(xrGetSystemProperties(dummyInstance, systemId, &systemProperties)); 127 | if (std::string(systemProperties.systemName).find("Vive Reality system") != std::string::npos) { 128 | xrDestroyInstance = nullptr; 129 | } 130 | } 131 | } 132 | 133 | if (xrDestroyInstance) { 134 | xrDestroyInstance(dummyInstance); 135 | } 136 | } 137 | } 138 | 139 | // Dump the requested extensions. 140 | XrInstanceCreateInfo chainInstanceCreateInfo = *instanceCreateInfo; 141 | std::vector newEnabledExtensionNames; 142 | for (uint32_t i = 0; i < chainInstanceCreateInfo.enabledExtensionCount; i++) { 143 | const std::string_view ext(chainInstanceCreateInfo.enabledExtensionNames[i]); 144 | TraceLoggingWriteTagged(local, "xrCreateApiLayerInstance", TLArg(ext.data(), "ExtensionName")); 145 | 146 | if (std::find(blockedExtensions.cbegin(), blockedExtensions.cend(), ext) == blockedExtensions.cend()) { 147 | Log(fmt::format("Requested extension: {}\n", ext)); 148 | newEnabledExtensionNames.push_back(ext.data()); 149 | } else { 150 | Log(fmt::format("Blocking extension: {}\n", ext)); 151 | } 152 | } 153 | for (const auto& ext : filteredImplicitExtensions) { 154 | Log(fmt::format("Requesting extension: {}\n", ext)); 155 | newEnabledExtensionNames.push_back(ext.c_str()); 156 | } 157 | chainInstanceCreateInfo.enabledExtensionNames = newEnabledExtensionNames.data(); 158 | chainInstanceCreateInfo.enabledExtensionCount = (uint32_t)newEnabledExtensionNames.size(); 159 | 160 | // Call the chain to create the instance. 161 | XrApiLayerCreateInfo chainApiLayerInfo = *apiLayerInfo; 162 | chainApiLayerInfo.nextInfo = apiLayerInfo->nextInfo->next; 163 | XrResult result = 164 | apiLayerInfo->nextInfo->nextCreateApiLayerInstance(&chainInstanceCreateInfo, &chainApiLayerInfo, instance); 165 | if (result == XR_SUCCESS) { 166 | // Create our layer. 167 | openxr_api_layer::GetInstance()->SetGetInstanceProcAddr(apiLayerInfo->nextInfo->nextGetInstanceProcAddr, 168 | *instance); 169 | openxr_api_layer::GetInstance()->SetGrantedExtensions(filteredImplicitExtensions); 170 | 171 | // Forward the xrCreateInstance() call to the layer. 172 | try { 173 | result = openxr_api_layer::GetInstance()->xrCreateInstance(instanceCreateInfo); 174 | } catch (std::exception& exc) { 175 | TraceLoggingWriteTagged(local, "xrCreateInstance_Error", TLArg(exc.what(), "Error")); 176 | ErrorLog(fmt::format("xrCreateInstance: {}\n", exc.what())); 177 | result = XR_ERROR_RUNTIME_FAILURE; 178 | } 179 | 180 | // Cleanup attempt before returning an error. 181 | if (XR_FAILED(result)) { 182 | PFN_xrDestroyInstance xrDestroyInstance = nullptr; 183 | if (XR_SUCCEEDED(apiLayerInfo->nextInfo->nextGetInstanceProcAddr( 184 | *instance, "xrDestroyInstance", reinterpret_cast(&xrDestroyInstance)))) { 185 | xrDestroyInstance(*instance); 186 | } 187 | } 188 | } 189 | 190 | TraceLoggingWriteStop(local, "xrCreateApiLayerInstance", TLArg(xr::ToCString(result), "Result")); 191 | if (XR_FAILED(result)) { 192 | ErrorLog(fmt::format("xrCreateApiLayerInstance failed with {}\n", xr::ToCString(result))); 193 | } 194 | 195 | return result; 196 | } 197 | 198 | // Forward the xrGetInstanceProcAddr() call to the dispatcher. 199 | XrResult XRAPI_CALL xrGetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) { 200 | TraceLocalActivity(local); 201 | TraceLoggingWriteStart(local, "xrGetInstanceProcAddr"); 202 | 203 | XrResult result; 204 | try { 205 | if (std::string_view(name) != "xrEnumerateInstanceExtensionProperties") { 206 | result = openxr_api_layer::GetInstance()->xrGetInstanceProcAddr(instance, name, function); 207 | } else { 208 | // We must always call our xrEnumerateInstanceExtensionProperties() override in order to be consistent 209 | // with the list of extensions defined in our JSON. 210 | result = openxr_api_layer::GetInstance()->xrGetInstanceProcAddrInternal(instance, name, function); 211 | } 212 | } catch (std::exception& exc) { 213 | TraceLoggingWriteTagged(local, "xrGetInstanceProcAddr_Error", TLArg(exc.what(), "Error")); 214 | ErrorLog(fmt::format("xrGetInstanceProcAddr: {}\n", exc.what())); 215 | result = XR_ERROR_RUNTIME_FAILURE; 216 | } 217 | 218 | TraceLoggingWriteStop(local, "xrGetInstanceProcAddr", TLArg(xr::ToCString(result), "Result")); 219 | 220 | return result; 221 | } 222 | 223 | } // namespace openxr_api_layer 224 | -------------------------------------------------------------------------------- /openxr-api-layer/utils/graphics.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include "general.h" 26 | 27 | namespace openxr_api_layer::utils::graphics { 28 | 29 | enum class Api { 30 | #ifdef XR_USE_GRAPHICS_API_D3D11 31 | D3D11, 32 | #endif 33 | #ifdef XR_USE_GRAPHICS_API_D3D12 34 | D3D12, 35 | #endif 36 | }; 37 | enum class CompositionApi { 38 | #ifdef XR_USE_GRAPHICS_API_D3D11 39 | D3D11, 40 | #endif 41 | }; 42 | 43 | // Type traits for all graphics APIs. 44 | 45 | #ifdef XR_USE_GRAPHICS_API_D3D11 46 | struct D3D11 { 47 | static constexpr Api Api = Api::D3D11; 48 | 49 | using Device = ID3D11Device*; 50 | using Context = ID3D11DeviceContext*; 51 | using Texture = ID3D11Texture2D*; 52 | using Fence = ID3D11Fence*; 53 | }; 54 | #endif 55 | 56 | #ifdef XR_USE_GRAPHICS_API_D3D12 57 | struct D3D12 { 58 | static constexpr Api Api = Api::D3D12; 59 | 60 | using Device = ID3D12Device*; 61 | using Context = ID3D12CommandQueue*; 62 | using Texture = ID3D12Resource*; 63 | using Fence = ID3D12Fence*; 64 | }; 65 | #endif 66 | 67 | // We (arbitrarily) use DXGI as a common conversion point for all graphics APIs. 68 | using GenericFormat = DXGI_FORMAT; 69 | 70 | struct ShareableHandle { 71 | wil::unique_handle ntHandle; 72 | HANDLE handle{nullptr}; 73 | bool isNtHandle{}; 74 | Api origin{}; 75 | }; 76 | 77 | // A timer on the GPU. 78 | struct IGraphicsTimer : openxr_api_layer::utils::general::ITimer { 79 | virtual ~IGraphicsTimer() = default; 80 | 81 | virtual Api getApi() const = 0; 82 | }; 83 | 84 | // A fence. 85 | struct IGraphicsFence { 86 | virtual ~IGraphicsFence() = default; 87 | 88 | virtual Api getApi() const = 0; 89 | virtual void* getNativeFencePtr() const = 0; 90 | virtual ShareableHandle getFenceHandle() const = 0; 91 | 92 | virtual void signal(uint64_t value) = 0; 93 | virtual void waitOnDevice(uint64_t value) = 0; 94 | virtual void waitOnCpu(uint64_t value) = 0; 95 | 96 | virtual bool isShareable() const = 0; 97 | 98 | template 99 | typename ApiTraits::Fence getNativeFence() const { 100 | if (ApiTraits::Api != getApi()) { 101 | throw std::runtime_error("Api mismatch"); 102 | } 103 | return reinterpret_cast(getNativeFencePtr()); 104 | } 105 | }; 106 | 107 | // A texture. 108 | struct IGraphicsTexture { 109 | virtual ~IGraphicsTexture() = default; 110 | 111 | virtual Api getApi() const = 0; 112 | virtual void* getNativeTexturePtr() const = 0; 113 | virtual ShareableHandle getTextureHandle() const = 0; 114 | 115 | virtual const XrSwapchainCreateInfo& getInfo() const = 0; 116 | virtual bool isShareable() const = 0; 117 | 118 | template 119 | typename ApiTraits::Texture getNativeTexture() const { 120 | if (ApiTraits::Api != getApi()) { 121 | throw std::runtime_error("Api mismatch"); 122 | } 123 | return reinterpret_cast(getNativeTexturePtr()); 124 | } 125 | }; 126 | 127 | // A graphics device and execution context. 128 | struct IGraphicsDevice { 129 | virtual ~IGraphicsDevice() = default; 130 | 131 | virtual Api getApi() const = 0; 132 | virtual void* getNativeDevicePtr() const = 0; 133 | virtual void* getNativeContextPtr() const = 0; 134 | 135 | virtual std::shared_ptr createTimer() = 0; 136 | virtual std::shared_ptr createFence(bool shareable = true) = 0; 137 | virtual std::shared_ptr openFence(const ShareableHandle& handle) = 0; 138 | virtual std::shared_ptr createTexture(const XrSwapchainCreateInfo& info, 139 | bool shareable = true) = 0; 140 | virtual std::shared_ptr openTexture(const ShareableHandle& handle, 141 | const XrSwapchainCreateInfo& info) = 0; 142 | virtual std::shared_ptr openTexturePtr(void* nativeTexturePtr, 143 | const XrSwapchainCreateInfo& info) = 0; 144 | 145 | virtual void copyTexture(IGraphicsTexture* from, IGraphicsTexture* to) = 0; 146 | 147 | virtual GenericFormat translateToGenericFormat(int64_t format) const = 0; 148 | virtual int64_t translateFromGenericFormat(GenericFormat format) const = 0; 149 | 150 | virtual LUID getAdapterLuid() const = 0; 151 | 152 | template 153 | typename ApiTraits::Device getNativeDevice() const { 154 | if (ApiTraits::Api != getApi()) { 155 | throw std::runtime_error("Api mismatch"); 156 | } 157 | return reinterpret_cast(getNativeDevicePtr()); 158 | } 159 | 160 | template 161 | typename ApiTraits::Context getNativeContext() const { 162 | if (ApiTraits::Api != getApi()) { 163 | throw std::runtime_error("Api mismatch"); 164 | } 165 | return reinterpret_cast(getNativeContextPtr()); 166 | } 167 | 168 | template 169 | std::shared_ptr openTexture(typename ApiTraits::Texture nativeTexture, 170 | const XrSwapchainCreateInfo& info) { 171 | if (ApiTraits::Api != getApi()) { 172 | throw std::runtime_error("Api mismatch"); 173 | } 174 | return openTexturePtr(reinterpret_cast(nativeTexture), info); 175 | } 176 | }; 177 | 178 | // Modes of use of wrapped swapchains. 179 | enum class SwapchainMode { 180 | // The swapchain must be submittable to the upstream xrEndFrame() implementation. 181 | // A non-submittable swapchain does not have an XrSwapchain handle. 182 | Submit = (1 << 0), 183 | 184 | // The swapchain will be accessed for reading during composition in the layer's xrEndFrame() implementation. 185 | // A readable swapchain might require a copy to the composition device before composition. 186 | Read = (1 << 1), 187 | 188 | // The swapchain will be access for writing during composition in the layer's xrEndFrame() implementation. 189 | // A writable swapchain might require a copy from the composition device after composition. 190 | Write = (1 << 2), 191 | }; 192 | DEFINE_ENUM_FLAG_OPERATORS(SwapchainMode); 193 | 194 | struct ISwapchainImage; 195 | 196 | // A swapchain. 197 | struct ISwapchain { 198 | virtual ~ISwapchain() = default; 199 | 200 | // Only for manipulating swapchains created through createSwapchain(). 201 | virtual ISwapchainImage* acquireImage(bool wait = true) = 0; 202 | virtual void waitImage() = 0; 203 | virtual void releaseImage() = 0; 204 | 205 | virtual ISwapchainImage* getLastReleasedImage() const = 0; 206 | virtual void commitLastReleasedImage() = 0; 207 | 208 | virtual const XrSwapchainCreateInfo& getInfoOnCompositionDevice() const = 0; 209 | virtual int64_t getFormatOnApplicationDevice() const = 0; 210 | virtual ISwapchainImage* getImage(uint32_t index) const = 0; 211 | virtual uint32_t getLength() const = 0; 212 | 213 | // Can only be called if the swapchain is submittable. 214 | virtual XrSwapchain getSwapchainHandle() const = 0; 215 | virtual XrSwapchainSubImage getSubImage() const = 0; 216 | }; 217 | 218 | // A swapchain image. 219 | struct ISwapchainImage { 220 | virtual ~ISwapchainImage() = default; 221 | 222 | virtual IGraphicsTexture* getApplicationTexture() const = 0; 223 | virtual IGraphicsTexture* getTextureForRead() const = 0; 224 | virtual IGraphicsTexture* getTextureForWrite() const = 0; 225 | 226 | virtual uint32_t getIndex() const = 0; 227 | }; 228 | 229 | // A container for user session data. 230 | // This class is meant to be extended by a caller before use with ICompositionFramework::setSessionData() and 231 | // ICompositionFramework::getSessionData(). 232 | struct ICompositionSessionData { 233 | virtual ~ICompositionSessionData() = default; 234 | }; 235 | 236 | // A collection of hooks and utilities to perform composition in the layer. 237 | struct ICompositionFramework { 238 | virtual ~ICompositionFramework() = default; 239 | 240 | virtual XrSession getSessionHandle() const = 0; 241 | 242 | virtual void setSessionData(std::unique_ptr sessionData) = 0; 243 | virtual ICompositionSessionData* getSessionDataPtr() const = 0; 244 | 245 | // Create a swapchain without an XrSwapchain handle. 246 | virtual std::shared_ptr createSwapchain(const XrSwapchainCreateInfo& infoOnApplicationDevice, 247 | SwapchainMode mode) = 0; 248 | 249 | // Must be called at the beginning of the layer's xrEndFrame() implementation to serialize application commands 250 | // prior to composition. 251 | virtual void serializePreComposition() = 0; 252 | 253 | // Must be called before chaining to the upstream xrEndFrame() implementation to serialize composition commands 254 | // prior to submission. 255 | virtual void serializePostComposition() = 0; 256 | 257 | virtual IGraphicsDevice* getCompositionDevice() const = 0; 258 | virtual IGraphicsDevice* getApplicationDevice() const = 0; 259 | virtual int64_t getPreferredSwapchainFormatOnApplicationDevice(XrSwapchainUsageFlags usageFlags, 260 | bool preferSRGB = true) const = 0; 261 | 262 | template 263 | typename SessionData* getSessionData() const { 264 | return reinterpret_cast(getSessionDataPtr()); 265 | } 266 | }; 267 | 268 | // A factory to create composition frameworks for each session. 269 | struct ICompositionFrameworkFactory { 270 | virtual ~ICompositionFrameworkFactory() = default; 271 | 272 | // Must be called after chaining to the upstream xrGetInstanceProcAddr() implementation. 273 | virtual void xrGetInstanceProcAddr_post(XrInstance instance, 274 | const char* name, 275 | PFN_xrVoidFunction* function) = 0; 276 | 277 | virtual ICompositionFramework* getCompositionFramework(XrSession session) = 0; 278 | }; 279 | 280 | std::shared_ptr 281 | createCompositionFrameworkFactory(const XrInstanceCreateInfo& info, 282 | XrInstance instance, 283 | PFN_xrGetInstanceProcAddr xrGetInstanceProcAddr, 284 | CompositionApi compositionApi); 285 | 286 | namespace internal { 287 | 288 | #ifdef XR_USE_GRAPHICS_API_D3D11 289 | std::shared_ptr createD3D11CompositionDevice(LUID adapterLuid); 290 | std::shared_ptr wrapApplicationDevice(const XrGraphicsBindingD3D11KHR& bindings); 291 | #endif 292 | 293 | #ifdef XR_USE_GRAPHICS_API_D3D12 294 | std::shared_ptr wrapApplicationDevice(const XrGraphicsBindingD3D12KHR& bindings); 295 | #endif 296 | 297 | } // namespace internal 298 | 299 | } // namespace openxr_api_layer::utils::graphics 300 | -------------------------------------------------------------------------------- /THIRD_PARTY: -------------------------------------------------------------------------------- 1 | The Khronos Group OpenXR SDK 2 | ----------------------------- 3 | 4 | Copyright (c) 2017-2021, The Khronos Group Inc. 5 | 6 | Apache License 7 | Version 2.0, January 2004 8 | http://www.apache.org/licenses/ 9 | 10 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 11 | 12 | 1. Definitions. 13 | 14 | "License" shall mean the terms and conditions for use, reproduction, 15 | and distribution as defined by Sections 1 through 9 of this document. 16 | 17 | "Licensor" shall mean the copyright owner or entity authorized by 18 | the copyright owner that is granting the License. 19 | 20 | "Legal Entity" shall mean the union of the acting entity and all 21 | other entities that control, are controlled by, or are under common 22 | control with that entity. For the purposes of this definition, 23 | "control" means (i) the power, direct or indirect, to cause the 24 | direction or management of such entity, whether by contract or 25 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 26 | outstanding shares, or (iii) beneficial ownership of such entity. 27 | 28 | "You" (or "Your") shall mean an individual or Legal Entity 29 | exercising permissions granted by this License. 30 | 31 | "Source" form shall mean the preferred form for making modifications, 32 | including but not limited to software source code, documentation 33 | source, and configuration files. 34 | 35 | "Object" form shall mean any form resulting from mechanical 36 | transformation or translation of a Source form, including but 37 | not limited to compiled object code, generated documentation, 38 | and conversions to other media types. 39 | 40 | "Work" shall mean the work of authorship, whether in Source or 41 | Object form, made available under the License, as indicated by a 42 | copyright notice that is included in or attached to the work 43 | (an example is provided in the Appendix below). 44 | 45 | "Derivative Works" shall mean any work, whether in Source or Object 46 | form, that is based on (or derived from) the Work and for which the 47 | editorial revisions, annotations, elaborations, or other modifications 48 | represent, as a whole, an original work of authorship. For the purposes 49 | of this License, Derivative Works shall not include works that remain 50 | separable from, or merely link (or bind by name) to the interfaces of, 51 | the Work and Derivative Works thereof. 52 | 53 | "Contribution" shall mean any work of authorship, including 54 | the original version of the Work and any modifications or additions 55 | to that Work or Derivative Works thereof, that is intentionally 56 | submitted to Licensor for inclusion in the Work by the copyright owner 57 | or by an individual or Legal Entity authorized to submit on behalf of 58 | the copyright owner. For the purposes of this definition, "submitted" 59 | means any form of electronic, verbal, or written communication sent 60 | to the Licensor or its representatives, including but not limited to 61 | communication on electronic mailing lists, source code control systems, 62 | and issue tracking systems that are managed by, or on behalf of, the 63 | Licensor for the purpose of discussing and improving the Work, but 64 | excluding communication that is conspicuously marked or otherwise 65 | designated in writing by the copyright owner as "Not a Contribution." 66 | 67 | "Contributor" shall mean Licensor and any individual or Legal Entity 68 | on behalf of whom a Contribution has been received by Licensor and 69 | subsequently incorporated within the Work. 70 | 71 | 2. Grant of Copyright License. Subject to the terms and conditions of 72 | this License, each Contributor hereby grants to You a perpetual, 73 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 74 | copyright license to reproduce, prepare Derivative Works of, 75 | publicly display, publicly perform, sublicense, and distribute the 76 | Work and such Derivative Works in Source or Object form. 77 | 78 | 3. Grant of Patent License. Subject to the terms and conditions of 79 | this License, each Contributor hereby grants to You a perpetual, 80 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 81 | (except as stated in this section) patent license to make, have made, 82 | use, offer to sell, sell, import, and otherwise transfer the Work, 83 | where such license applies only to those patent claims licensable 84 | by such Contributor that are necessarily infringed by their 85 | Contribution(s) alone or by combination of their Contribution(s) 86 | with the Work to which such Contribution(s) was submitted. If You 87 | institute patent litigation against any entity (including a 88 | cross-claim or counterclaim in a lawsuit) alleging that the Work 89 | or a Contribution incorporated within the Work constitutes direct 90 | or contributory patent infringement, then any patent licenses 91 | granted to You under this License for that Work shall terminate 92 | as of the date such litigation is filed. 93 | 94 | 4. Redistribution. You may reproduce and distribute copies of the 95 | Work or Derivative Works thereof in any medium, with or without 96 | modifications, and in Source or Object form, provided that You 97 | meet the following conditions: 98 | 99 | (a) You must give any other recipients of the Work or 100 | Derivative Works a copy of this License; and 101 | 102 | (b) You must cause any modified files to carry prominent notices 103 | stating that You changed the files; and 104 | 105 | (c) You must retain, in the Source form of any Derivative Works 106 | that You distribute, all copyright, patent, trademark, and 107 | attribution notices from the Source form of the Work, 108 | excluding those notices that do not pertain to any part of 109 | the Derivative Works; and 110 | 111 | (d) If the Work includes a "NOTICE" text file as part of its 112 | distribution, then any Derivative Works that You distribute must 113 | include a readable copy of the attribution notices contained 114 | within such NOTICE file, excluding those notices that do not 115 | pertain to any part of the Derivative Works, in at least one 116 | of the following places: within a NOTICE text file distributed 117 | as part of the Derivative Works; within the Source form or 118 | documentation, if provided along with the Derivative Works; or, 119 | within a display generated by the Derivative Works, if and 120 | wherever such third-party notices normally appear. The contents 121 | of the NOTICE file are for informational purposes only and 122 | do not modify the License. You may add Your own attribution 123 | notices within Derivative Works that You distribute, alongside 124 | or as an addendum to the NOTICE text from the Work, provided 125 | that such additional attribution notices cannot be construed 126 | as modifying the License. 127 | 128 | You may add Your own copyright statement to Your modifications and 129 | may provide additional or different license terms and conditions 130 | for use, reproduction, or distribution of Your modifications, or 131 | for any such Derivative Works as a whole, provided Your use, 132 | reproduction, and distribution of the Work otherwise complies with 133 | the conditions stated in this License. 134 | 135 | 5. Submission of Contributions. Unless You explicitly state otherwise, 136 | any Contribution intentionally submitted for inclusion in the Work 137 | by You to the Licensor shall be under the terms and conditions of 138 | this License, without any additional terms or conditions. 139 | Notwithstanding the above, nothing herein shall supersede or modify 140 | the terms of any separate license agreement you may have executed 141 | with Licensor regarding such Contributions. 142 | 143 | 6. Trademarks. This License does not grant permission to use the trade 144 | names, trademarks, service marks, or product names of the Licensor, 145 | except as required for reasonable and customary use in describing the 146 | origin of the Work and reproducing the content of the NOTICE file. 147 | 148 | 7. Disclaimer of Warranty. Unless required by applicable law or 149 | agreed to in writing, Licensor provides the Work (and each 150 | Contributor provides its Contributions) on an "AS IS" BASIS, 151 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 152 | implied, including, without limitation, any warranties or conditions 153 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 154 | PARTICULAR PURPOSE. You are solely responsible for determining the 155 | appropriateness of using or redistributing the Work and assume any 156 | risks associated with Your exercise of permissions under this License. 157 | 158 | 8. Limitation of Liability. In no event and under no legal theory, 159 | whether in tort (including negligence), contract, or otherwise, 160 | unless required by applicable law (such as deliberate and grossly 161 | negligent acts) or agreed to in writing, shall any Contributor be 162 | liable to You for damages, including any direct, indirect, special, 163 | incidental, or consequential damages of any character arising as a 164 | result of this License or out of the use or inability to use the 165 | Work (including but not limited to damages for loss of goodwill, 166 | work stoppage, computer failure or malfunction, or any and all 167 | other commercial damages or losses), even if such Contributor 168 | has been advised of the possibility of such damages. 169 | 170 | 9. Accepting Warranty or Additional Liability. While redistributing 171 | the Work or Derivative Works thereof, You may choose to offer, 172 | and charge a fee for, acceptance of support, warranty, indemnity, 173 | or other liability obligations and/or rights consistent with this 174 | License. However, in accepting such obligations, You may act only 175 | on Your own behalf and on Your sole responsibility, not on behalf 176 | of any other Contributor, and only if You agree to indemnify, 177 | defend, and hold each Contributor harmless for any liability 178 | incurred by, or claims asserted against, such Contributor by reason 179 | of your accepting any such warranty or additional liability. 180 | 181 | Microsoft OpenXR Mixed Reality SDK 182 | ----------------------------------- 183 | 184 | Copyright (c) Microsoft Corporation. 185 | 186 | MIT License 187 | 188 | Permission is hereby granted, free of charge, to any person obtaining a copy 189 | of this software and associated documentation files (the "Software"), to deal 190 | in the Software without restriction, including without limitation the rights 191 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 192 | copies of the Software, and to permit persons to whom the Software is 193 | furnished to do so, subject to the following conditions: 194 | 195 | The above copyright notice and this permission notice shall be included in all 196 | copies or substantial portions of the Software. 197 | 198 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 199 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 200 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 201 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 202 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 203 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 204 | SOFTWARE. 205 | 206 | AMD FidelityFX Contrast Adaptive Sharpening 207 | -------------------------------------------- 208 | 209 | Copyright (c) 2020 Advanced Micro Devices, Inc. All rights reserved. 210 | 211 | Permission is hereby granted, free of charge, to any person obtaining a copy 212 | of this software and associated documentation files (the "Software"), to deal 213 | in the Software without restriction, including without limitation the rights 214 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 215 | copies of the Software, and to permit persons to whom the Software is 216 | furnished to do so, subject to the following conditions: 217 | 218 | The above copyright notice and this permission notice shall be included in 219 | all copies or substantial portions of the Software. 220 | 221 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 222 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 223 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 224 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 225 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 226 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 227 | THE SOFTWARE. 228 | 229 | fmtlib 230 | ------- 231 | 232 | Copyright (c) 2012 - present, Victor Zverovich 233 | 234 | Permission is hereby granted, free of charge, to any person obtaining 235 | a copy of this software and associated documentation files (the 236 | "Software"), to deal in the Software without restriction, including 237 | without limitation the rights to use, copy, modify, merge, publish, 238 | distribute, sublicense, and/or sell copies of the Software, and to 239 | permit persons to whom the Software is furnished to do so, subject to 240 | the following conditions: 241 | 242 | The above copyright notice and this permission notice shall be 243 | included in all copies or substantial portions of the Software. 244 | 245 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 246 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 247 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 248 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 249 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 250 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 251 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 252 | 253 | --- Optional exception to the license --- 254 | 255 | As an exception, if, as a result of your compiling your source code, portions 256 | of this Software are embedded into a machine-executable object form of such 257 | source code, you may redistribute such embedded portions in such object form 258 | without including the above copyright and permission notices. 259 | -------------------------------------------------------------------------------- /openxr-api-layer/framework/dispatch_generator.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright(c) 2021-2023 Matthieu Bucchianeri 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this softwareand associated documentation files(the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions : 11 | # 12 | # The above copyright noticeand this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | import os 23 | import re 24 | import sys 25 | 26 | # Import dependencies from the OpenXR SDK. 27 | cur_dir = os.path.abspath(os.path.dirname(__file__)) 28 | base_dir = os.path.abspath(os.path.join(cur_dir, '..', '..')) 29 | sdk_dir = os.path.join(base_dir, 'external', 'OpenXR-SDK-Source') 30 | sys.path.append(os.path.join(sdk_dir, 'specification', 'scripts')) 31 | sys.path.append(os.path.join(sdk_dir, 'src', 'scripts')) 32 | 33 | from automatic_source_generator import AutomaticSourceOutputGenerator, AutomaticSourceGeneratorOptions 34 | from reg import Registry 35 | from generator import write 36 | from xrconventions import OpenXRConventions 37 | 38 | # Import configuration. 39 | import layer_apis 40 | 41 | # Sanity checks on the configuration file 42 | for func in ['xrCreateInstance', 'xrDestroyInstance', 'xrEnumerateInstanceExtensionProperties']: 43 | if func in layer_apis.override_functions: 44 | raise Exception("{func}() is implicitly overriden and shall not be specified in override_functions. Use the {func}() virtual method.") 45 | if func in layer_apis.requested_functions: 46 | raise Exception("{func}() cannot be specified in requested_functions") 47 | 48 | if 'xrGetInstanceProcAddr' in layer_apis.override_functions: 49 | raise Exception("xrGetInstanceProcAddr() is implicitly overriden and shall not be specified in override_functions. Use the xrGetInstanceProcAddr() virtual method.") 50 | if 'xrGetInstanceProcAddr' in layer_apis.requested_functions: 51 | raise Exception("xrGetInstanceProcAddr() cannot be specified in requested_functions. Use the m_xrGetInstanceProcAddr() class member.") 52 | 53 | 54 | class DispatchGenOutputGenerator(AutomaticSourceOutputGenerator): 55 | '''Common generator utilities and formatting.''' 56 | def outputGeneratedHeaderWarning(self): 57 | warning = '''// *********** THIS FILE IS GENERATED - DO NOT EDIT ***********''' 58 | write(warning, file=self.outFile) 59 | 60 | def outputCopywriteHeader(self): 61 | copyright = '''// MIT License 62 | // 63 | // Copyright(c) 2021-2023 Matthieu Bucchianeri 64 | // 65 | // Permission is hereby granted, free of charge, to any person obtaining a copy 66 | // of this softwareand associated documentation files(the "Software"), to deal 67 | // in the Software without restriction, including without limitation the rights 68 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 69 | // copies of the Software, and to permit persons to whom the Software is 70 | // furnished to do so, subject to the following conditions : 71 | // 72 | // The above copyright noticeand this permission notice shall be included in all 73 | // copies or substantial portions of the Software. 74 | // 75 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 76 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 77 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 78 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 79 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 80 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 81 | // SOFTWARE. 82 | ''' 83 | write(copyright, file=self.outFile) 84 | 85 | def outputGeneratedAuthorNote(self): 86 | pass 87 | 88 | def makeParametersList(self, cmd): 89 | parameters_list = "" 90 | for param in cmd.params: 91 | if parameters_list: 92 | parameters_list += ', ' 93 | parameters_list += param.cdecl.strip() 94 | 95 | return parameters_list 96 | 97 | def makeArgumentsList(self, cmd): 98 | arguments_list = "" 99 | for param in cmd.params: 100 | if arguments_list: 101 | arguments_list += ', ' 102 | arguments_list += param.name 103 | 104 | return arguments_list 105 | 106 | class DispatchGenCppOutputGenerator(DispatchGenOutputGenerator): 107 | '''Generator for dispatch.gen.cpp.''' 108 | def beginFile(self, genOpts): 109 | DispatchGenOutputGenerator.beginFile(self, genOpts) 110 | preamble = '''#include "pch.h" 111 | 112 | #include 113 | 114 | #include "dispatch.h" 115 | #include "log.h" 116 | 117 | using namespace openxr_api_layer::log; 118 | 119 | namespace openxr_api_layer 120 | {''' 121 | write(preamble, file=self.outFile) 122 | 123 | def endFile(self): 124 | generated_wrappers = self.genWrappers() 125 | generated_get_instance_proc_addr = self.genGetInstanceProcAddr() 126 | generated_create_instance = self.genCreateInstance() 127 | 128 | postamble = ''' std::unique_ptr g_instance; 129 | 130 | void ResetInstance() { 131 | g_instance.reset(); 132 | } 133 | 134 | } // namespace openxr_api_layer 135 | ''' 136 | 137 | contents = f''' 138 | // Auto-generated wrappers for the requested APIs. 139 | {generated_wrappers} 140 | 141 | // Auto-generated dispatcher handler. 142 | {generated_get_instance_proc_addr} 143 | 144 | // Auto-generated create instance handler. 145 | {generated_create_instance} 146 | 147 | {postamble}''' 148 | 149 | write(contents, file=self.outFile) 150 | DispatchGenOutputGenerator.endFile(self) 151 | 152 | def genWrappers(self): 153 | generated = '' 154 | 155 | for cur_cmd in self.core_commands + self.ext_commands: 156 | if cur_cmd.name in (layer_apis.override_functions + ['xrDestroyInstance', 'xrEnumerateInstanceExtensionProperties']): 157 | parameters_list = self.makeParametersList(cur_cmd) 158 | arguments_list = self.makeArgumentsList(cur_cmd) 159 | 160 | if cur_cmd.return_type is not None: 161 | generated += f''' 162 | XrResult XRAPI_CALL {cur_cmd.name}({parameters_list}) 163 | {{ 164 | TraceLocalActivity(local); 165 | TraceLoggingWriteStart(local, "{cur_cmd.name}"); 166 | 167 | XrResult result; 168 | try 169 | {{ 170 | result = openxr_api_layer::GetInstance()->{cur_cmd.name}({arguments_list}); 171 | }} 172 | catch (std::exception& exc) 173 | {{ 174 | TraceLoggingWriteTagged(local, "{cur_cmd.name}_Error", TLArg(exc.what(), "Error")); 175 | ErrorLog(fmt::format("{cur_cmd.name}: {{}}\\n", exc.what())); 176 | result = XR_ERROR_RUNTIME_FAILURE; 177 | }} 178 | 179 | TraceLoggingWriteStop(local, "{cur_cmd.name}", TLArg(xr::ToCString(result), "Result")); 180 | if (XR_FAILED(result)) {{ 181 | ErrorLog(fmt::format("{cur_cmd.name} failed with {{}}\\n", xr::ToCString(result))); 182 | }} 183 | 184 | return result; 185 | }} 186 | ''' 187 | else: 188 | generated += f''' 189 | void XRAPI_CALL {cur_cmd.name}({parameters_list}) 190 | {{ 191 | TraceLocalActivity(local); 192 | TraceLoggingWriteStart(local, "{cur_cmd.name}"); 193 | 194 | try 195 | {{ 196 | openxr_api_layer::GetInstance()->{cur_cmd.name}({arguments_list}); 197 | }} 198 | catch (std::exception& exc) 199 | {{ 200 | TraceLoggingWriteTagged(local, "{cur_cmd.name}_Error", TLArg(exc.what(), "Error")); 201 | ErrorLog(fmt::format("{cur_cmd.name}: {{}}\\n", exc.what())); 202 | }} 203 | 204 | TraceLoggingWriteStop(local, "{cur_cmd.name}")); 205 | }} 206 | ''' 207 | 208 | return generated 209 | 210 | def genCreateInstance(self): 211 | generated = ''' XrResult OpenXrApi::xrCreateInstance(const XrInstanceCreateInfo* createInfo) 212 | { 213 | ''' 214 | 215 | for cur_cmd in self.core_commands: 216 | if cur_cmd.name in layer_apis.requested_functions: 217 | generated += f''' if (XR_FAILED(m_xrGetInstanceProcAddr(m_instance, "{cur_cmd.name}", reinterpret_cast(&m_{cur_cmd.name})))) 218 | {{ 219 | throw std::runtime_error("Failed to resolve {cur_cmd.name}"); 220 | }} 221 | ''' 222 | 223 | # Functions from extensions are allowed to be null. 224 | for cur_cmd in self.ext_commands: 225 | if cur_cmd.name in layer_apis.requested_functions: 226 | generated += f''' m_xrGetInstanceProcAddr(m_instance, "{cur_cmd.name}", reinterpret_cast(&m_{cur_cmd.name})); 227 | ''' 228 | 229 | generated += ''' m_applicationName = createInfo->applicationInfo.applicationName; 230 | return XR_SUCCESS; 231 | }''' 232 | 233 | return generated 234 | 235 | def genGetInstanceProcAddr(self): 236 | generated = ''' XrResult OpenXrApi::xrGetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) 237 | { 238 | return xrGetInstanceProcAddrInternal(instance, name, function); 239 | } 240 | 241 | XrResult OpenXrApi::xrGetInstanceProcAddrInternal(XrInstance instance, const char* name, PFN_xrVoidFunction* function) 242 | { 243 | XrResult result = m_xrGetInstanceProcAddr(instance, name, function); 244 | 245 | const std::string apiName(name); 246 | 247 | if (apiName == "xrDestroyInstance") 248 | { 249 | m_xrDestroyInstance = reinterpret_cast(*function); 250 | *function = reinterpret_cast(openxr_api_layer::xrDestroyInstance); 251 | } 252 | ''' 253 | 254 | for cur_cmd in self.core_commands: 255 | if cur_cmd.name in layer_apis.override_functions + ['xrEnumerateInstanceExtensionProperties']: 256 | generated += f''' else if (apiName == "{cur_cmd.name}") 257 | {{ 258 | m_{cur_cmd.name} = reinterpret_cast(*function); 259 | *function = reinterpret_cast(openxr_api_layer::{cur_cmd.name}); 260 | }} 261 | ''' 262 | 263 | # Always advertise extension functions. 264 | for cur_cmd in self.ext_commands: 265 | if cur_cmd.name in layer_apis.override_functions: 266 | generated += f''' else if (apiName == "{cur_cmd.name}") 267 | {{ 268 | m_{cur_cmd.name} = reinterpret_cast(*function); 269 | *function = reinterpret_cast(openxr_api_layer::{cur_cmd.name}); 270 | result = XR_SUCCESS; 271 | }} 272 | ''' 273 | 274 | generated += ''' 275 | 276 | return result; 277 | }''' 278 | 279 | return generated 280 | 281 | 282 | class DispatchGenHOutputGenerator(DispatchGenOutputGenerator): 283 | '''Generator for dispatch.gen.h.''' 284 | def beginFile(self, genOpts): 285 | DispatchGenOutputGenerator.beginFile(self, genOpts) 286 | preamble = '''#pragma once 287 | 288 | namespace openxr_api_layer 289 | { 290 | 291 | void ResetInstance(); 292 | extern const std::vector> advertisedExtensions; 293 | 294 | class OpenXrApi 295 | { 296 | private: 297 | XrInstance m_instance{ XR_NULL_HANDLE }; 298 | std::string m_applicationName; 299 | std::vector m_grantedExtensions; 300 | 301 | protected: 302 | OpenXrApi() = default; 303 | 304 | PFN_xrGetInstanceProcAddr m_xrGetInstanceProcAddr{ nullptr }; 305 | 306 | public: 307 | virtual ~OpenXrApi() = default; 308 | 309 | XrInstance GetXrInstance() const 310 | { 311 | return m_instance; 312 | } 313 | 314 | const std::string& GetApplicationName() const 315 | { 316 | return m_applicationName; 317 | } 318 | 319 | const std::vector& GetGrantedExtensions() const 320 | { 321 | return m_grantedExtensions; 322 | } 323 | 324 | void SetGetInstanceProcAddr(PFN_xrGetInstanceProcAddr pfn_xrGetInstanceProcAddr, XrInstance instance) 325 | { 326 | m_xrGetInstanceProcAddr = pfn_xrGetInstanceProcAddr; 327 | m_instance = instance; 328 | } 329 | 330 | void SetGrantedExtensions(const std::vector& grantedExtensions) 331 | { 332 | m_grantedExtensions = grantedExtensions; 333 | } 334 | 335 | // Specially-handled by the auto-generated code. 336 | virtual XrResult xrGetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function); 337 | virtual XrResult xrCreateInstance(const XrInstanceCreateInfo* createInfo); 338 | 339 | // Make sure to destroy the singleton instance. 340 | virtual XrResult xrDestroyInstance(XrInstance instance) { 341 | // Invoking ResetInstance() is equivalent to `delete this;' so we must take precautions. 342 | PFN_xrDestroyInstance finalDestroyInstance = m_xrDestroyInstance; 343 | ResetInstance(); 344 | return finalDestroyInstance(instance); 345 | } 346 | 347 | // Make sure we enumerate the layer's extensions, specifically when another API layer may resolve our implementation 348 | // of xrEnumerateInstanceExtensionProperties() instead of the loaders. 349 | virtual XrResult xrEnumerateInstanceExtensionProperties(const char* layerName, 350 | uint32_t propertyCapacityInput, 351 | uint32_t* propertyCountOutput, 352 | XrExtensionProperties* properties) { 353 | XrResult result = XR_ERROR_RUNTIME_FAILURE; 354 | if (!layerName || std::string_view(layerName) != LAYER_NAME) { 355 | result = m_xrEnumerateInstanceExtensionProperties(layerName, propertyCapacityInput, propertyCountOutput, properties); 356 | } 357 | 358 | if (XR_SUCCEEDED(result)) { 359 | if (!layerName || std::string_view(layerName) == LAYER_NAME) { 360 | const uint32_t baseOffset = *propertyCountOutput; 361 | *propertyCountOutput += (uint32_t)advertisedExtensions.size(); 362 | if (propertyCapacityInput) { 363 | if (propertyCapacityInput < *propertyCountOutput) { 364 | result = XR_ERROR_SIZE_INSUFFICIENT; 365 | } else { 366 | result = XR_SUCCESS; 367 | for (uint32_t i = baseOffset; i < *propertyCountOutput; i++) { 368 | if (properties[i].type != XR_TYPE_EXTENSION_PROPERTIES) { 369 | result = XR_ERROR_VALIDATION_FAILURE; 370 | break; 371 | } 372 | 373 | strcpy_s(properties[i].extensionName, advertisedExtensions[i - baseOffset].first.c_str()); 374 | properties[i].extensionVersion = advertisedExtensions[i - baseOffset].second; 375 | } 376 | } 377 | } 378 | } 379 | } 380 | 381 | return result; 382 | } 383 | 384 | private: 385 | XrResult xrGetInstanceProcAddrInternal(XrInstance instance, const char* name, PFN_xrVoidFunction* function); 386 | friend XrResult XRAPI_CALL xrGetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function); 387 | 388 | PFN_xrDestroyInstance m_xrDestroyInstance{nullptr}; 389 | PFN_xrEnumerateInstanceExtensionProperties m_xrEnumerateInstanceExtensionProperties{nullptr}; 390 | ''' 391 | write(preamble, file=self.outFile) 392 | 393 | def endFile(self): 394 | generated_virtual_methods = self.genVirtualMethods() 395 | 396 | postamble = ''' 397 | }; 398 | 399 | extern std::unique_ptr g_instance; 400 | 401 | } // namespace openxr_api_layer 402 | ''' 403 | 404 | contents = f''' 405 | // Auto-generated entries for the requested APIs. 406 | {generated_virtual_methods} 407 | 408 | {postamble}''' 409 | 410 | write(contents, file=self.outFile) 411 | 412 | DispatchGenOutputGenerator.endFile(self) 413 | 414 | def genVirtualMethods(self): 415 | generated = '' 416 | 417 | commands_to_include = list(set(layer_apis.override_functions + layer_apis.requested_functions)) 418 | for cur_cmd in self.core_commands + self.ext_commands: 419 | if cur_cmd.name in commands_to_include: 420 | parameters_list = self.makeParametersList(cur_cmd) 421 | arguments_list = self.makeArgumentsList(cur_cmd) 422 | 423 | generated += ''' 424 | public:''' 425 | 426 | if cur_cmd.return_type is not None: 427 | generated += f''' 428 | virtual XrResult {cur_cmd.name}({parameters_list}) 429 | {{ 430 | return m_{cur_cmd.name}({arguments_list}); 431 | }} 432 | ''' 433 | else: 434 | generated += f''' 435 | virtual void {cur_cmd.name}({parameters_list}) 436 | {{ 437 | m_{cur_cmd.name}({arguments_list}); 438 | }} 439 | ''' 440 | 441 | generated += f''' private: 442 | PFN_{cur_cmd.name} m_{cur_cmd.name}{{ nullptr }}; 443 | ''' 444 | 445 | return generated 446 | 447 | def makeREstring(strings, default=None): 448 | """Turn a list of strings into a regexp string matching exactly those strings.""" 449 | if strings or default is None: 450 | return '^(' + '|'.join((re.escape(s) for s in strings)) + ')$' 451 | return default 452 | 453 | if __name__ == '__main__': 454 | conventions = OpenXRConventions() 455 | featuresPat = 'XR_VERSION_1_0' 456 | extensionsPat = makeREstring(layer_apis.extensions) 457 | 458 | registry = Registry(DispatchGenCppOutputGenerator(diagFile=None), 459 | AutomaticSourceGeneratorOptions(conventions = conventions, 460 | filename = 'dispatch.gen.cpp', 461 | directory = cur_dir, 462 | apiname = 'openxr', 463 | profile = None, 464 | versions = featuresPat, 465 | emitversions = featuresPat, 466 | defaultExtensions = 'openxr', 467 | addExtensions = None, 468 | removeExtensions = None, 469 | emitExtensions = extensionsPat)) 470 | registry.loadFile(os.path.join(sdk_dir, 'specification', 'registry', 'xr.xml')) 471 | registry.apiGen() 472 | 473 | registry = Registry(DispatchGenHOutputGenerator(diagFile=None), 474 | AutomaticSourceGeneratorOptions(conventions = conventions, 475 | filename = 'dispatch.gen.h', 476 | directory = cur_dir, 477 | apiname = 'openxr', 478 | profile = None, 479 | versions = featuresPat, 480 | emitversions = featuresPat, 481 | defaultExtensions = 'openxr', 482 | addExtensions = None, 483 | removeExtensions = None, 484 | emitExtensions = extensionsPat)) 485 | registry.loadFile(os.path.join(sdk_dir, 'specification', 'registry', 'xr.xml')) 486 | registry.apiGen() 487 | -------------------------------------------------------------------------------- /openxr-api-layer/utils/d3d11.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "pch.h" 24 | 25 | #ifdef XR_USE_GRAPHICS_API_D3D11 26 | 27 | #include "log.h" 28 | #include "graphics.h" 29 | 30 | #pragma comment(lib, "dxgi.lib") 31 | #pragma comment(lib, "d3d11.lib") 32 | 33 | namespace { 34 | 35 | using namespace openxr_api_layer::log; 36 | using namespace openxr_api_layer::utils::graphics; 37 | 38 | constexpr bool PreferNtHandle = false; 39 | 40 | struct D3D11Timer : IGraphicsTimer { 41 | D3D11Timer(ID3D11Device* device) { 42 | TraceLocalActivity(local); 43 | TraceLoggingWriteStart(local, "D3D11Timer_Create"); 44 | 45 | device->GetImmediateContext(m_context.ReleaseAndGetAddressOf()); 46 | 47 | D3D11_QUERY_DESC queryDesc; 48 | ZeroMemory(&queryDesc, sizeof(D3D11_QUERY_DESC)); 49 | queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT; 50 | CHECK_HRCMD(device->CreateQuery(&queryDesc, m_timeStampDis.ReleaseAndGetAddressOf())); 51 | queryDesc.Query = D3D11_QUERY_TIMESTAMP; 52 | CHECK_HRCMD(device->CreateQuery(&queryDesc, m_timeStampStart.ReleaseAndGetAddressOf())); 53 | CHECK_HRCMD(device->CreateQuery(&queryDesc, m_timeStampEnd.ReleaseAndGetAddressOf())); 54 | 55 | TraceLoggingWriteStop(local, "D3D11Timer_Create", TLPArg(this, "Timer")); 56 | } 57 | 58 | ~D3D11Timer() override { 59 | TraceLocalActivity(local); 60 | TraceLoggingWriteStart(local, "D3D11Timer_Destroy", TLPArg(this, "Timer")); 61 | TraceLoggingWriteStop(local, "D3D11Timer_Destroy"); 62 | } 63 | 64 | Api getApi() const override { 65 | return Api::D3D11; 66 | } 67 | 68 | void start() override { 69 | TraceLocalActivity(local); 70 | TraceLoggingWriteStart(local, "D3D11Timer_Start", TLPArg(this, "Timer")); 71 | 72 | m_context->Begin(m_timeStampDis.Get()); 73 | m_context->End(m_timeStampStart.Get()); 74 | 75 | TraceLoggingWriteStop(local, "D3D11Timer_Start"); 76 | } 77 | 78 | void stop() override { 79 | TraceLocalActivity(local); 80 | TraceLoggingWriteStart(local, "D3D11Timer_Stop", TLPArg(this, "Timer")); 81 | 82 | m_context->End(m_timeStampEnd.Get()); 83 | m_context->End(m_timeStampDis.Get()); 84 | m_valid = true; 85 | 86 | TraceLoggingWriteStop(local, "D3D11Timer_Stop"); 87 | } 88 | 89 | uint64_t query() const override { 90 | TraceLocalActivity(local); 91 | TraceLoggingWriteStart(local, "D3D11Timer_Query", TLPArg(this, "Timer"), TLArg(m_valid, "Valid")); 92 | 93 | uint64_t duration = 0; 94 | if (m_valid) { 95 | UINT64 startime = 0, endtime = 0; 96 | D3D11_QUERY_DATA_TIMESTAMP_DISJOINT disData = {0}; 97 | 98 | if (m_context->GetData(m_timeStampStart.Get(), &startime, sizeof(UINT64), 0) == S_OK && 99 | m_context->GetData(m_timeStampEnd.Get(), &endtime, sizeof(UINT64), 0) == S_OK && 100 | m_context->GetData( 101 | m_timeStampDis.Get(), &disData, sizeof(D3D11_QUERY_DATA_TIMESTAMP_DISJOINT), 0) == S_OK && 102 | !disData.Disjoint) { 103 | duration = static_cast(((endtime - startime) * 1e6) / disData.Frequency); 104 | } 105 | m_valid = false; 106 | } 107 | 108 | TraceLoggingWriteStop(local, "D3D11Timer_Query", TLArg(duration, "Duration")); 109 | 110 | return duration; 111 | } 112 | 113 | ComPtr m_context; 114 | ComPtr m_timeStampDis; 115 | ComPtr m_timeStampStart; 116 | ComPtr m_timeStampEnd; 117 | 118 | // Can the timer be queried (it might still only read 0). 119 | mutable bool m_valid{false}; 120 | }; 121 | 122 | struct D3D11Fence : IGraphicsFence { 123 | D3D11Fence(ID3D11Fence* fence, bool shareable) : m_fence(fence), m_isShareable(shareable) { 124 | TraceLocalActivity(local); 125 | TraceLoggingWriteStart( 126 | local, "D3D11Fence_Create", TLPArg(fence, "D3D11Fence"), TLArg(shareable, "Shareable")); 127 | 128 | // Query the necessary flavors of device context which will let us use fences. 129 | ComPtr device; 130 | m_fence->GetDevice(device.ReleaseAndGetAddressOf()); 131 | ComPtr context; 132 | device->GetImmediateContext(context.ReleaseAndGetAddressOf()); 133 | CHECK_HRCMD(context->QueryInterface(m_context.ReleaseAndGetAddressOf())); 134 | 135 | TraceLoggingWriteStop(local, "D3D11Fence_Create", TLPArg(this, "Fence")); 136 | } 137 | 138 | ~D3D11Fence() override { 139 | TraceLocalActivity(local); 140 | TraceLoggingWriteStart(local, "D3D11Fence_Destroy", TLPArg(this, "Fence")); 141 | TraceLoggingWriteStop(local, "D3D11Fence_Destroy"); 142 | } 143 | 144 | Api getApi() const override { 145 | return Api::D3D11; 146 | } 147 | 148 | void* getNativeFencePtr() const override { 149 | return m_fence.Get(); 150 | } 151 | 152 | ShareableHandle getFenceHandle() const override { 153 | TraceLocalActivity(local); 154 | TraceLoggingWriteStart(local, "D3D11Fence_Export", TLPArg(this, "Fence")); 155 | 156 | if (!m_isShareable) { 157 | throw std::runtime_error("Fence is not shareable"); 158 | } 159 | 160 | ShareableHandle handle{}; 161 | CHECK_HRCMD(m_fence->CreateSharedHandle(nullptr, GENERIC_ALL, nullptr, handle.ntHandle.put())); 162 | handle.isNtHandle = true; 163 | handle.origin = Api::D3D11; 164 | 165 | TraceLoggingWriteStop(local, "D3D11Fence_Export", TLPArg(handle.ntHandle.get(), "Handle")); 166 | 167 | return handle; 168 | } 169 | 170 | void signal(uint64_t value) override { 171 | TraceLocalActivity(local); 172 | TraceLoggingWriteStart(local, "D3D11Fence_Signal", TLPArg(this, "Fence"), TLArg(value, "Value")); 173 | 174 | CHECK_HRCMD(m_context->Signal(m_fence.Get(), value)); 175 | m_context->Flush(); 176 | 177 | TraceLoggingWriteStop(local, "D3D11Fence_Signal"); 178 | } 179 | 180 | void waitOnDevice(uint64_t value) override { 181 | TraceLocalActivity(local); 182 | TraceLoggingWriteStart( 183 | local, "D3D11Fence_Wait", TLPArg(this, "Fence"), TLArg("Device", "WaitType"), TLArg(value, "Value")); 184 | 185 | CHECK_HRCMD(m_context->Wait(m_fence.Get(), value)); 186 | 187 | TraceLoggingWriteStop(local, "D3D11Fence_Wait"); 188 | } 189 | 190 | void waitOnCpu(uint64_t value) override { 191 | TraceLocalActivity(local); 192 | TraceLoggingWriteStart( 193 | local, "D3D11Fence_Wait", TLPArg(this, "Fence"), TLArg("Host", "WaitType"), TLArg(value, "Value")); 194 | 195 | wil::unique_handle eventHandle; 196 | CHECK_HRCMD(m_context->Signal(m_fence.Get(), value)); 197 | m_context->Flush(); 198 | *eventHandle.put() = CreateEventEx(nullptr, L"D3D Fence", 0, EVENT_ALL_ACCESS); 199 | CHECK_HRCMD(m_fence->SetEventOnCompletion(value, eventHandle.get())); 200 | WaitForSingleObject(eventHandle.get(), INFINITE); 201 | ResetEvent(eventHandle.get()); 202 | 203 | TraceLoggingWriteStop(local, "D3D11Fence_Wait"); 204 | } 205 | 206 | bool isShareable() const override { 207 | return m_isShareable; 208 | } 209 | 210 | const ComPtr m_fence; 211 | const bool m_isShareable; 212 | 213 | ComPtr m_context; 214 | }; 215 | 216 | struct D3D11Texture : IGraphicsTexture { 217 | D3D11Texture(ID3D11Texture2D* texture) : m_texture(texture) { 218 | TraceLocalActivity(local); 219 | TraceLoggingWriteStart(local, "D3D11Texture_Create", TLPArg(texture, "D3D11Texture")); 220 | 221 | D3D11_TEXTURE2D_DESC desc; 222 | m_texture->GetDesc(&desc); 223 | TraceLoggingWriteTagged(local, 224 | "D3D11Texture_Create", 225 | TLArg(desc.Width, "Width"), 226 | TLArg(desc.Height, "Height"), 227 | TLArg(desc.ArraySize, "ArraySize"), 228 | TLArg(desc.MipLevels, "MipCount"), 229 | TLArg(desc.SampleDesc.Count, "SampleCount"), 230 | TLArg((int)desc.Format, "Format"), 231 | TLArg((int)desc.Usage, "Usage"), 232 | TLArg(desc.BindFlags, "BindFlags"), 233 | TLArg(desc.CPUAccessFlags, "CPUAccessFlags"), 234 | TLArg(desc.MiscFlags, "MiscFlags")); 235 | 236 | // Construct the API-agnostic info descriptor. 237 | m_info.format = (int64_t)desc.Format; 238 | m_info.width = desc.Width; 239 | m_info.height = desc.Height; 240 | m_info.arraySize = desc.ArraySize; 241 | m_info.mipCount = desc.MipLevels; 242 | m_info.sampleCount = desc.SampleDesc.Count; 243 | m_info.faceCount = 1; 244 | m_info.usageFlags = 0; 245 | if (desc.BindFlags & D3D11_BIND_RENDER_TARGET) { 246 | m_info.usageFlags |= XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; 247 | } 248 | if (desc.BindFlags & D3D11_BIND_DEPTH_STENCIL) { 249 | m_info.usageFlags |= XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; 250 | } 251 | if (desc.BindFlags & D3D11_BIND_SHADER_RESOURCE) { 252 | m_info.usageFlags |= XR_SWAPCHAIN_USAGE_SAMPLED_BIT; 253 | } 254 | if (desc.BindFlags & D3D11_BIND_UNORDERED_ACCESS) { 255 | m_info.usageFlags |= XR_SWAPCHAIN_USAGE_UNORDERED_ACCESS_BIT; 256 | } 257 | 258 | // Identify the shareability. 259 | if (desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED) { 260 | m_isShareable = true; 261 | if (desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE) { 262 | m_useNtHandle = true; 263 | } 264 | } 265 | 266 | TraceLoggingWriteStop(local, 267 | "D3D11Texture_Create", 268 | TLPArg(this, "Texture"), 269 | TLArg(m_isShareable, "Shareable"), 270 | TLArg(m_useNtHandle, "IsNTHandle")); 271 | } 272 | 273 | ~D3D11Texture() override { 274 | TraceLocalActivity(local); 275 | TraceLoggingWriteStart(local, "D3D11Texture_Destroy", TLPArg(this, "Texture")); 276 | TraceLoggingWriteStop(local, "D3D11Texture_Destroy"); 277 | } 278 | 279 | Api getApi() const override { 280 | return Api::D3D11; 281 | } 282 | 283 | void* getNativeTexturePtr() const override { 284 | return m_texture.Get(); 285 | } 286 | 287 | ShareableHandle getTextureHandle() const override { 288 | TraceLocalActivity(local); 289 | TraceLoggingWriteStart(local, "D3D11Texture_Export", TLPArg(this, "Texture")); 290 | 291 | if (!m_isShareable) { 292 | throw std::runtime_error("Texture is not shareable"); 293 | } 294 | 295 | ShareableHandle handle{}; 296 | ComPtr dxgiResource; 297 | CHECK_HRCMD(m_texture->QueryInterface(IID_PPV_ARGS(dxgiResource.ReleaseAndGetAddressOf()))); 298 | if (!m_useNtHandle) { 299 | CHECK_HRCMD(dxgiResource->GetSharedHandle(&handle.handle)); 300 | } else { 301 | CHECK_HRCMD(dxgiResource->CreateSharedHandle(nullptr, GENERIC_ALL, nullptr, handle.ntHandle.put())); 302 | } 303 | handle.isNtHandle = m_useNtHandle; 304 | handle.origin = Api::D3D11; 305 | 306 | TraceLoggingWriteStop( 307 | local, "D3D11Texture_Export", TLPArg(!m_useNtHandle ? handle.handle : handle.ntHandle.get(), "Handle")); 308 | 309 | return handle; 310 | } 311 | 312 | const XrSwapchainCreateInfo& getInfo() const override { 313 | return m_info; 314 | } 315 | 316 | bool isShareable() const override { 317 | return m_isShareable; 318 | } 319 | 320 | const ComPtr m_texture; 321 | 322 | XrSwapchainCreateInfo m_info{}; 323 | bool m_isShareable{false}; 324 | bool m_useNtHandle{false}; 325 | }; 326 | 327 | struct D3D11GraphicsDevice : IGraphicsDevice { 328 | D3D11GraphicsDevice(ID3D11Device* device) : m_device(device) { 329 | TraceLocalActivity(local); 330 | TraceLoggingWriteStart(local, "D3D11GraphicsDevice_Create", TLPArg(device, "D3D11Device")); 331 | 332 | { 333 | ComPtr dxgiDevice; 334 | CHECK_HRCMD(m_device->QueryInterface(IID_PPV_ARGS(dxgiDevice.ReleaseAndGetAddressOf()))); 335 | ComPtr dxgiAdapter; 336 | CHECK_HRCMD(dxgiDevice->GetAdapter(dxgiAdapter.ReleaseAndGetAddressOf())); 337 | DXGI_ADAPTER_DESC desc; 338 | CHECK_HRCMD(dxgiAdapter->GetDesc(&desc)); 339 | m_adapterLuid = desc.AdapterLuid; 340 | 341 | TraceLoggingWriteTagged( 342 | local, 343 | "D3D11GraphicsDevice_Create", 344 | TLArg(desc.Description, "Adapter"), 345 | TLArg(fmt::format("{}:{}", m_adapterLuid.HighPart, m_adapterLuid.LowPart).c_str(), " Luid")); 346 | } 347 | 348 | // Query the necessary flavors of device which will let us use fences. 349 | CHECK_HRCMD(m_device->QueryInterface(m_deviceForFencesAndNtHandles.ReleaseAndGetAddressOf())); 350 | m_device->GetImmediateContext(m_context.ReleaseAndGetAddressOf()); 351 | 352 | TraceLoggingWriteStop(local, "D3D11GraphicsDevice_Create", TLPArg(this, "Device")); 353 | } 354 | 355 | ~D3D11GraphicsDevice() override { 356 | TraceLocalActivity(local); 357 | TraceLoggingWriteStart(local, "D3D11GraphicsDevice_Destroy", TLPArg(this, "Device")); 358 | TraceLoggingWriteStop(local, "D3D11GraphicsDevice_Destroy"); 359 | } 360 | 361 | Api getApi() const override { 362 | return Api::D3D11; 363 | } 364 | 365 | void* getNativeDevicePtr() const override { 366 | return m_device.Get(); 367 | } 368 | 369 | void* getNativeContextPtr() const override { 370 | return m_context.Get(); 371 | } 372 | 373 | std::shared_ptr createTimer() override { 374 | return std::make_shared(m_device.Get()); 375 | } 376 | 377 | std::shared_ptr createFence(bool shareable) override { 378 | ComPtr fence; 379 | CHECK_HRCMD( 380 | m_deviceForFencesAndNtHandles->CreateFence(0, 381 | shareable ? D3D11_FENCE_FLAG_SHARED : D3D11_FENCE_FLAG_NONE, 382 | IID_PPV_ARGS(fence.ReleaseAndGetAddressOf()))); 383 | return std::make_shared(fence.Get(), shareable); 384 | } 385 | 386 | std::shared_ptr openFence(const ShareableHandle& handle) override { 387 | TraceLocalActivity(local); 388 | TraceLoggingWriteStart(local, 389 | "D3D11Fence_Import", 390 | TLArg(!handle.isNtHandle ? handle.handle : handle.ntHandle.get(), "Handle"), 391 | TLArg(handle.isNtHandle, "IsNTHandle")); 392 | 393 | if (!handle.isNtHandle) { 394 | throw std::runtime_error("Must be NTHANDLE"); 395 | } 396 | 397 | ComPtr fence; 398 | CHECK_HRCMD(m_deviceForFencesAndNtHandles->OpenSharedFence(handle.isNtHandle ? handle.ntHandle.get() 399 | : handle.handle, 400 | IID_PPV_ARGS(fence.ReleaseAndGetAddressOf()))); 401 | std::shared_ptr result = std::make_shared(fence.Get(), false /* shareable */); 402 | 403 | TraceLoggingWriteStop(local, "D3D11Fence_Import", TLPArg(result.get(), "Fence")); 404 | 405 | return result; 406 | } 407 | 408 | std::shared_ptr createTexture(const XrSwapchainCreateInfo& info, bool shareable) override { 409 | D3D11_TEXTURE2D_DESC desc{}; 410 | desc.Format = (DXGI_FORMAT)info.format; 411 | desc.Width = info.width; 412 | desc.Height = info.height; 413 | desc.ArraySize = info.arraySize; 414 | desc.MipLevels = info.mipCount; 415 | desc.SampleDesc.Count = info.sampleCount; 416 | desc.Usage = D3D11_USAGE_DEFAULT; 417 | if (info.usageFlags & XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT) { 418 | desc.BindFlags |= D3D11_BIND_RENDER_TARGET; 419 | } 420 | if (info.usageFlags & XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { 421 | desc.BindFlags |= D3D11_BIND_DEPTH_STENCIL; 422 | } 423 | if (info.usageFlags & XR_SWAPCHAIN_USAGE_SAMPLED_BIT) { 424 | desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; 425 | } 426 | if (info.usageFlags & XR_SWAPCHAIN_USAGE_UNORDERED_ACCESS_BIT) { 427 | desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; 428 | } 429 | // Mark as shareable if needed. 430 | desc.MiscFlags = 431 | shareable ? (D3D11_RESOURCE_MISC_SHARED | (PreferNtHandle ? D3D11_RESOURCE_MISC_SHARED_NTHANDLE : 0)) 432 | : 0; 433 | 434 | ComPtr texture; 435 | CHECK_HRCMD(m_device->CreateTexture2D(&desc, nullptr, texture.ReleaseAndGetAddressOf())); 436 | return std::make_shared(texture.Get()); 437 | } 438 | 439 | std::shared_ptr openTexture(const ShareableHandle& handle, 440 | const XrSwapchainCreateInfo& info) override { 441 | TraceLocalActivity(local); 442 | TraceLoggingWriteStart(local, 443 | "D3D11Texture_Import", 444 | TLArg(!handle.isNtHandle ? handle.handle : handle.ntHandle.get(), "Handle"), 445 | TLArg(handle.isNtHandle, "IsNTHandle")); 446 | 447 | ComPtr texture; 448 | if (!handle.isNtHandle) { 449 | CHECK_HRCMD( 450 | m_device->OpenSharedResource(handle.handle, IID_PPV_ARGS(texture.ReleaseAndGetAddressOf()))); 451 | } else { 452 | CHECK_HRCMD(m_deviceForFencesAndNtHandles->OpenSharedResource1( 453 | handle.ntHandle.get(), IID_PPV_ARGS(texture.ReleaseAndGetAddressOf()))); 454 | } 455 | 456 | std::shared_ptr result = std::make_shared(texture.Get()); 457 | 458 | TraceLoggingWriteStop(local, "D3D11Texture_Import", TLPArg(result.get(), "Texture")); 459 | 460 | return result; 461 | } 462 | 463 | std::shared_ptr openTexturePtr(void* nativeTexturePtr, 464 | const XrSwapchainCreateInfo& info) override { 465 | TraceLocalActivity(local); 466 | TraceLoggingWriteStart(local, "D3D11Texture_Import", TLPArg(nativeTexturePtr, "D3D11Texture")); 467 | 468 | ID3D11Texture2D* texture = reinterpret_cast(nativeTexturePtr); 469 | 470 | std::shared_ptr result = std::make_shared(texture); 471 | 472 | TraceLoggingWriteStop(local, "D3D11Texture_Import", TLPArg(result.get(), "Texture")); 473 | 474 | return result; 475 | } 476 | 477 | void copyTexture(IGraphicsTexture* from, IGraphicsTexture* to) override { 478 | TraceLocalActivity(local); 479 | TraceLoggingWriteStart(local, "D3D11Texture_Copy", TLPArg(from, "Source"), TLPArg(to, "Destination")); 480 | 481 | m_context->CopyResource(to->getNativeTexture(), from->getNativeTexture()); 482 | 483 | TraceLoggingWriteStop(local, "D3D11Texture_Copy"); 484 | } 485 | 486 | GenericFormat translateToGenericFormat(int64_t format) const override { 487 | return (DXGI_FORMAT)format; 488 | } 489 | 490 | int64_t translateFromGenericFormat(GenericFormat format) const override { 491 | return (int64_t)format; 492 | } 493 | 494 | LUID getAdapterLuid() const override { 495 | return m_adapterLuid; 496 | } 497 | 498 | const ComPtr m_device; 499 | LUID m_adapterLuid{}; 500 | 501 | ComPtr m_deviceForFencesAndNtHandles; 502 | ComPtr m_context; 503 | }; 504 | 505 | } // namespace 506 | 507 | namespace openxr_api_layer::utils::graphics::internal { 508 | 509 | std::shared_ptr createD3D11CompositionDevice(LUID adapterLuid) { 510 | // Find the adapter. 511 | ComPtr dxgiFactory; 512 | CHECK_HRCMD(CreateDXGIFactory1(IID_PPV_ARGS(dxgiFactory.ReleaseAndGetAddressOf()))); 513 | ComPtr dxgiAdapter; 514 | for (UINT adapterIndex = 0;; adapterIndex++) { 515 | // EnumAdapters1 will fail with DXGI_ERROR_NOT_FOUND when there are no more adapters to 516 | // enumerate. 517 | CHECK_HRCMD(dxgiFactory->EnumAdapters1(adapterIndex, dxgiAdapter.ReleaseAndGetAddressOf())); 518 | 519 | DXGI_ADAPTER_DESC1 desc; 520 | CHECK_HRCMD(dxgiAdapter->GetDesc1(&desc)); 521 | if (!memcmp(&desc.AdapterLuid, &adapterLuid, sizeof(LUID))) { 522 | break; 523 | } 524 | } 525 | 526 | // Create our own device on the same adapter. 527 | ComPtr device; 528 | D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0; 529 | UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; 530 | #ifdef _DEBUG 531 | flags |= D3D11_CREATE_DEVICE_DEBUG; 532 | #endif 533 | CHECK_HRCMD(D3D11CreateDevice(dxgiAdapter.Get(), 534 | D3D_DRIVER_TYPE_UNKNOWN, 535 | 0, 536 | flags, 537 | &featureLevel, 538 | 1, 539 | D3D11_SDK_VERSION, 540 | device.ReleaseAndGetAddressOf(), 541 | nullptr, 542 | nullptr)); 543 | 544 | return std::make_shared(device.Get()); 545 | } 546 | 547 | std::shared_ptr wrapApplicationDevice(const XrGraphicsBindingD3D11KHR& bindings) { 548 | return std::make_shared(bindings.device); 549 | } 550 | 551 | } // namespace openxr_api_layer::utils::graphics::internal 552 | 553 | #endif 554 | -------------------------------------------------------------------------------- /openxr-api-layer/utils/d3d12.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022-2023 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "pch.h" 24 | 25 | #ifdef XR_USE_GRAPHICS_API_D3D12 26 | 27 | #include "log.h" 28 | #include "graphics.h" 29 | 30 | #pragma comment(lib, "dxgi.lib") 31 | #pragma comment(lib, "d3d12.lib") 32 | 33 | namespace { 34 | 35 | using namespace openxr_api_layer::log; 36 | using namespace openxr_api_layer::utils::graphics; 37 | 38 | struct D3D12Timer : IGraphicsTimer { 39 | D3D12Timer(ID3D12Device* device, ID3D12CommandQueue* queue) : m_queue(queue) { 40 | TraceLocalActivity(local); 41 | TraceLoggingWriteStart(local, "D3D12Timer_Create"); 42 | 43 | // Create the command context. 44 | for (uint32_t i = 0; i < 2; i++) { 45 | CHECK_HRCMD(device->CreateCommandAllocator( 46 | D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(m_commandAllocator[i].ReleaseAndGetAddressOf()))); 47 | m_commandAllocator[i]->SetName(L"Timer Command Allocator"); 48 | CHECK_HRCMD(device->CreateCommandList(0, 49 | D3D12_COMMAND_LIST_TYPE_DIRECT, 50 | m_commandAllocator[i].Get(), 51 | nullptr, 52 | IID_PPV_ARGS(m_commandList[i].ReleaseAndGetAddressOf()))); 53 | m_commandList[i]->SetName(L"Timer Command List"); 54 | CHECK_HRCMD(m_commandList[i]->Close()); 55 | } 56 | CHECK_HRCMD(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_fence.ReleaseAndGetAddressOf()))); 57 | m_fence->SetName(L"Timer Readback Fence"); 58 | 59 | // Create the query heap and readback resources. 60 | D3D12_QUERY_HEAP_DESC heapDesc{}; 61 | heapDesc.Count = 2; 62 | heapDesc.NodeMask = 0; 63 | heapDesc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; 64 | CHECK_HRCMD(device->CreateQueryHeap(&heapDesc, IID_PPV_ARGS(m_queryHeap.ReleaseAndGetAddressOf()))); 65 | m_queryHeap->SetName(L"Timestamp Query Heap"); 66 | 67 | D3D12_HEAP_PROPERTIES heapType{}; 68 | heapType.Type = D3D12_HEAP_TYPE_READBACK; 69 | heapType.CreationNodeMask = heapType.VisibleNodeMask = 1; 70 | D3D12_RESOURCE_DESC readbackDesc{}; 71 | readbackDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; 72 | readbackDesc.Width = heapDesc.Count * sizeof(uint64_t); 73 | readbackDesc.Height = readbackDesc.DepthOrArraySize = readbackDesc.MipLevels = 74 | readbackDesc.SampleDesc.Count = 1; 75 | readbackDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; 76 | CHECK_HRCMD(device->CreateCommittedResource(&heapType, 77 | D3D12_HEAP_FLAG_NONE, 78 | &readbackDesc, 79 | D3D12_RESOURCE_STATE_COPY_DEST, 80 | nullptr, 81 | IID_PPV_ARGS(m_queryReadbackBuffer.ReleaseAndGetAddressOf()))); 82 | m_queryReadbackBuffer->SetName(L"Query Readback Buffer"); 83 | 84 | TraceLoggingWriteStop(local, "D3D12Timer_Create", TLPArg(this, "Timer")); 85 | } 86 | 87 | ~D3D12Timer() override { 88 | TraceLocalActivity(local); 89 | TraceLoggingWriteStart(local, "D3D12Timer_Destroy", TLPArg(this, "Timer")); 90 | TraceLoggingWriteStop(local, "D3D12Timer_Destroy"); 91 | } 92 | 93 | Api getApi() const override { 94 | return Api::D3D12; 95 | } 96 | 97 | void start() override { 98 | TraceLocalActivity(local); 99 | TraceLoggingWriteStart(local, "D3D12Timer_Start", TLPArg(this, "Timer")); 100 | 101 | CHECK_HRCMD(m_commandAllocator[0]->Reset()); 102 | CHECK_HRCMD(m_commandList[0]->Reset(m_commandAllocator[0].Get(), nullptr)); 103 | m_commandList[0]->EndQuery(m_queryHeap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, 0); 104 | CHECK_HRCMD(m_commandList[0]->Close()); 105 | ID3D12CommandList* const lists[] = {m_commandList[0].Get()}; 106 | m_queue->ExecuteCommandLists(1, lists); 107 | 108 | TraceLoggingWriteStop(local, "D3D12Timer_Start"); 109 | } 110 | 111 | void stop() override { 112 | TraceLocalActivity(local); 113 | TraceLoggingWriteStart(local, "D3D12Timer_Stop", TLPArg(this, "Timer")); 114 | 115 | CHECK_HRCMD(m_commandAllocator[1]->Reset()); 116 | CHECK_HRCMD(m_commandList[1]->Reset(m_commandAllocator[1].Get(), nullptr)); 117 | m_commandList[1]->EndQuery(m_queryHeap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, 1); 118 | m_commandList[1]->ResolveQueryData( 119 | m_queryHeap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, 0, 2, m_queryReadbackBuffer.Get(), 0); 120 | CHECK_HRCMD(m_commandList[1]->Close()); 121 | ID3D12CommandList* const lists[] = {m_commandList[1].Get()}; 122 | m_queue->ExecuteCommandLists(1, lists); 123 | 124 | // Signal a fence for completion. 125 | m_queue->Signal(m_fence.Get(), ++m_fenceValue); 126 | m_valid = true; 127 | 128 | TraceLoggingWriteStop(local, "D3D12Timer_Stop"); 129 | } 130 | 131 | uint64_t query() const override { 132 | TraceLocalActivity(local); 133 | TraceLoggingWriteStart(local, "D3D12Timer_Query", TLPArg(this, "Timer"), TLArg(m_valid, "Valid")); 134 | 135 | uint64_t duration = 0; 136 | if (m_valid) { 137 | uint64_t gpuTickFrequency; 138 | if (m_fence->GetCompletedValue() >= m_fenceValue && 139 | SUCCEEDED(m_queue->GetTimestampFrequency(&gpuTickFrequency))) { 140 | uint64_t* mappedBuffer; 141 | D3D12_RANGE range{0, 2 * sizeof(uint64_t)}; 142 | CHECK_HRCMD(m_queryReadbackBuffer->Map(0, &range, reinterpret_cast(&mappedBuffer))); 143 | duration = ((mappedBuffer[1] - mappedBuffer[0]) * 1000000) / gpuTickFrequency; 144 | m_queryReadbackBuffer->Unmap(0, nullptr); 145 | } 146 | m_valid = false; 147 | } 148 | 149 | TraceLoggingWriteStop(local, "D3D12Timer_Query", TLArg(duration, "Duration")); 150 | 151 | return duration; 152 | } 153 | 154 | ComPtr m_queue; 155 | ComPtr m_commandAllocator[2]; 156 | ComPtr m_commandList[2]; 157 | ComPtr m_fence; 158 | uint64_t m_fenceValue{0}; 159 | ComPtr m_queryHeap; 160 | ComPtr m_queryReadbackBuffer; 161 | 162 | // Can the timer be queried (it might still only read 0). 163 | mutable bool m_valid{false}; 164 | }; 165 | 166 | struct D3D12Fence : IGraphicsFence { 167 | D3D12Fence(ID3D12Fence* fence, ID3D12CommandQueue* commandQueue, bool shareable) 168 | : m_fence(fence), m_commandQueue(commandQueue), m_isShareable(shareable) { 169 | TraceLocalActivity(local); 170 | TraceLoggingWriteStart( 171 | local, "D3D12Fence_Create", TLPArg(fence, "D3D12Fence"), TLArg(shareable, "Shareable")); 172 | 173 | m_fence->GetDevice(IID_PPV_ARGS(m_device.ReleaseAndGetAddressOf())); 174 | 175 | TraceLoggingWriteStop(local, "D3D12Fence_Create", TLPArg(this, "Fence")); 176 | } 177 | 178 | ~D3D12Fence() override { 179 | TraceLocalActivity(local); 180 | TraceLoggingWriteStart(local, "D3D12Fence_Destroy", TLPArg(this, "Fence")); 181 | TraceLoggingWriteStop(local, "D3D12Fence_Destroy"); 182 | } 183 | 184 | Api getApi() const override { 185 | return Api::D3D12; 186 | } 187 | 188 | void* getNativeFencePtr() const override { 189 | return m_fence.Get(); 190 | } 191 | 192 | ShareableHandle getFenceHandle() const override { 193 | TraceLocalActivity(local); 194 | TraceLoggingWriteStart(local, "D3D12Fence_Export", TLPArg(this, "Fence")); 195 | 196 | if (!m_isShareable) { 197 | throw std::runtime_error("Fence is not shareable"); 198 | } 199 | 200 | ShareableHandle handle{}; 201 | CHECK_HRCMD( 202 | m_device->CreateSharedHandle(m_fence.Get(), nullptr, GENERIC_ALL, nullptr, handle.ntHandle.put())); 203 | handle.isNtHandle = true; 204 | handle.origin = Api::D3D12; 205 | 206 | TraceLoggingWriteStop(local, "D3D12Fence_Export", TLPArg(handle.ntHandle.get(), "Handle")); 207 | 208 | return handle; 209 | } 210 | 211 | void signal(uint64_t value) override { 212 | TraceLocalActivity(local); 213 | TraceLoggingWriteStart(local, "D3D12Fence_Signal", TLPArg(this, "Fence"), TLArg(value, "Value")); 214 | 215 | CHECK_HRCMD(m_commandQueue->Signal(m_fence.Get(), value)); 216 | 217 | TraceLoggingWriteStop(local, "D3D12Fence_Signal"); 218 | } 219 | 220 | void waitOnDevice(uint64_t value) override { 221 | TraceLocalActivity(local); 222 | TraceLoggingWriteStart( 223 | local, "D3D12Fence_Wait", TLPArg(this, "Fence"), TLArg("Device", "WaitType"), TLArg(value, "Value")); 224 | 225 | CHECK_HRCMD(m_commandQueue->Wait(m_fence.Get(), value)); 226 | 227 | TraceLoggingWriteStop(local, "D3D12Fence_Wait"); 228 | } 229 | 230 | void waitOnCpu(uint64_t value) override { 231 | TraceLocalActivity(local); 232 | TraceLoggingWriteStart( 233 | local, "D3D12Fence_Wait", TLPArg(this, "Fence"), TLArg("Host", "WaitType"), TLArg(value, "Value")); 234 | 235 | wil::unique_handle eventHandle; 236 | CHECK_HRCMD(m_commandQueue->Signal(m_fence.Get(), value)); 237 | *eventHandle.put() = CreateEventEx(nullptr, L"D3D Fence", 0, EVENT_ALL_ACCESS); 238 | CHECK_HRCMD(m_fence->SetEventOnCompletion(value, eventHandle.get())); 239 | WaitForSingleObject(eventHandle.get(), INFINITE); 240 | ResetEvent(eventHandle.get()); 241 | 242 | TraceLoggingWriteStop(local, "D3D12Fence_Wait"); 243 | } 244 | 245 | bool isShareable() const override { 246 | return m_isShareable; 247 | } 248 | 249 | const ComPtr m_fence; 250 | const ComPtr m_commandQueue; 251 | const bool m_isShareable; 252 | 253 | ComPtr m_device; 254 | }; 255 | 256 | struct D3D12Texture : IGraphicsTexture { 257 | D3D12Texture(ID3D12Resource* texture) : m_texture(texture) { 258 | TraceLocalActivity(local); 259 | TraceLoggingWriteStart(local, "D3D12Texture_Create", TLPArg(texture, "D3D12Texture")); 260 | 261 | m_texture->GetDevice(IID_PPV_ARGS(m_device.ReleaseAndGetAddressOf())); 262 | 263 | D3D12_RESOURCE_DESC desc = m_texture->GetDesc(); 264 | TraceLoggingWriteTagged(local, 265 | "D3D12Texture_Create", 266 | TLArg(desc.Width, "Width"), 267 | TLArg(desc.Height, "Height"), 268 | TLArg(desc.DepthOrArraySize, "ArraySize"), 269 | TLArg(desc.MipLevels, "MipCount"), 270 | TLArg(desc.SampleDesc.Count, "SampleCount"), 271 | TLArg((int)desc.Format, "Format"), 272 | TLArg((int)desc.Flags, "Flags")); 273 | 274 | // Construct the API-agnostic info descriptor. 275 | m_info.format = (int64_t)desc.Format; 276 | m_info.width = (uint32_t)desc.Width; 277 | m_info.height = desc.Height; 278 | m_info.arraySize = desc.DepthOrArraySize; 279 | m_info.mipCount = desc.MipLevels; 280 | m_info.sampleCount = desc.SampleDesc.Count; 281 | m_info.faceCount = 1; 282 | m_info.usageFlags = 0; 283 | if (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) { 284 | m_info.usageFlags |= XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; 285 | } 286 | if (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) { 287 | m_info.usageFlags |= XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; 288 | } 289 | if (!(desc.Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE)) { 290 | m_info.usageFlags |= XR_SWAPCHAIN_USAGE_SAMPLED_BIT; 291 | } 292 | if (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) { 293 | m_info.usageFlags |= XR_SWAPCHAIN_USAGE_UNORDERED_ACCESS_BIT; 294 | } 295 | 296 | // Identify the shareability. 297 | D3D12_HEAP_FLAGS heapFlags; 298 | CHECK_HRCMD(m_texture->GetHeapProperties(nullptr, &heapFlags)); 299 | m_isShareable = heapFlags & D3D12_HEAP_FLAG_SHARED; 300 | 301 | TraceLoggingWriteStop( 302 | local, "D3D12Texture_Create", TLPArg(this, "Texture"), TLArg(m_isShareable, "Shareable")); 303 | } 304 | 305 | ~D3D12Texture() override { 306 | TraceLocalActivity(local); 307 | TraceLoggingWriteStart(local, "D3D12Texture_Destroy", TLPArg(this, "Texture")); 308 | TraceLoggingWriteStop(local, "D3D12Texture_Destroy"); 309 | } 310 | 311 | Api getApi() const override { 312 | return Api::D3D12; 313 | } 314 | 315 | void* getNativeTexturePtr() const override { 316 | return m_texture.Get(); 317 | } 318 | 319 | ShareableHandle getTextureHandle() const override { 320 | TraceLocalActivity(local); 321 | TraceLoggingWriteStart(local, "D3D12Texture_Export", TLPArg(this, "Texture")); 322 | 323 | if (!m_isShareable) { 324 | throw std::runtime_error("Texture is not shareable"); 325 | } 326 | 327 | ShareableHandle handle{}; 328 | CHECK_HRCMD( 329 | m_device->CreateSharedHandle(m_texture.Get(), nullptr, GENERIC_ALL, nullptr, handle.ntHandle.put())); 330 | handle.isNtHandle = true; 331 | handle.origin = Api::D3D12; 332 | 333 | TraceLoggingWriteStop(local, "D3D12Texture_Export", TLPArg(handle.ntHandle.get(), "Handle")); 334 | 335 | return handle; 336 | } 337 | 338 | const XrSwapchainCreateInfo& getInfo() const override { 339 | return m_info; 340 | } 341 | 342 | bool isShareable() const override { 343 | return m_isShareable; 344 | } 345 | 346 | const ComPtr m_texture; 347 | ComPtr m_device; 348 | 349 | XrSwapchainCreateInfo m_info{}; 350 | bool m_isShareable{false}; 351 | }; 352 | 353 | struct D3D12ReusableCommandList { 354 | ComPtr allocator; 355 | ComPtr commandList; 356 | uint32_t completedFenceValue{0}; 357 | }; 358 | 359 | struct D3D12GraphicsDevice : IGraphicsDevice { 360 | D3D12GraphicsDevice(ID3D12Device* device, ID3D12CommandQueue* commandQueue) 361 | : m_device(device), m_commandQueue(commandQueue) { 362 | TraceLocalActivity(local); 363 | TraceLoggingWriteStart( 364 | local, "D3D12GraphicsDevice_Create", TLPArg(device, "D3D12Device"), TLPArg(commandQueue, "Queue")); 365 | 366 | { 367 | const LUID adapterLuid = m_device->GetAdapterLuid(); 368 | 369 | ComPtr dxgiFactory; 370 | CHECK_HRCMD(CreateDXGIFactory1(IID_PPV_ARGS(dxgiFactory.ReleaseAndGetAddressOf()))); 371 | ComPtr dxgiAdapter; 372 | for (UINT adapterIndex = 0;; adapterIndex++) { 373 | // EnumAdapters1 will fail with DXGI_ERROR_NOT_FOUND when there are no more adapters to 374 | // enumerate. 375 | CHECK_HRCMD(dxgiFactory->EnumAdapters1(adapterIndex, dxgiAdapter.ReleaseAndGetAddressOf())); 376 | 377 | DXGI_ADAPTER_DESC1 desc; 378 | CHECK_HRCMD(dxgiAdapter->GetDesc1(&desc)); 379 | if (!memcmp(&desc.AdapterLuid, &adapterLuid, sizeof(LUID))) { 380 | TraceLoggingWriteTagged( 381 | local, 382 | "D3D12GraphicsDevice_Create", 383 | TLArg(desc.Description, "Adapter"), 384 | TLArg(fmt::format("{}:{}", adapterLuid.HighPart, adapterLuid.LowPart).c_str(), " Luid")); 385 | break; 386 | } 387 | } 388 | } 389 | 390 | CHECK_HRCMD(m_device->CreateFence( 391 | 0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_commandListPoolFence.ReleaseAndGetAddressOf()))); 392 | 393 | TraceLoggingWriteStop(local, "D3D12GraphicsDevice_Create", TLPArg(this, "Device")); 394 | } 395 | 396 | ~D3D12GraphicsDevice() override { 397 | TraceLocalActivity(local); 398 | TraceLoggingWriteStart(local, "D3D12GraphicsDevice_Destroy", TLPArg(this, "Device")); 399 | TraceLoggingWriteStop(local, "D3D12GraphicsDevice_Destroy"); 400 | } 401 | 402 | Api getApi() const override { 403 | return Api::D3D12; 404 | } 405 | 406 | void* getNativeDevicePtr() const override { 407 | return m_device.Get(); 408 | } 409 | 410 | void* getNativeContextPtr() const override { 411 | return m_commandQueue.Get(); 412 | } 413 | 414 | std::shared_ptr createTimer() override { 415 | return std::make_shared(m_device.Get(), m_commandQueue.Get()); 416 | } 417 | 418 | std::shared_ptr createFence(bool shareable) override { 419 | ComPtr fence; 420 | CHECK_HRCMD(m_device->CreateFence(0, 421 | shareable ? D3D12_FENCE_FLAG_SHARED : D3D12_FENCE_FLAG_NONE, 422 | IID_PPV_ARGS(fence.ReleaseAndGetAddressOf()))); 423 | return std::make_shared(fence.Get(), m_commandQueue.Get(), shareable); 424 | } 425 | 426 | std::shared_ptr openFence(const ShareableHandle& handle) override { 427 | TraceLocalActivity(local); 428 | TraceLoggingWriteStart(local, 429 | "D3D12Fence_Import", 430 | TLArg(!handle.isNtHandle ? handle.handle : handle.ntHandle.get(), "Handle"), 431 | TLArg(handle.isNtHandle, "IsNTHandle")); 432 | 433 | if (!handle.isNtHandle) { 434 | throw std::runtime_error("Must be NTHANDLE"); 435 | } 436 | 437 | ComPtr fence; 438 | CHECK_HRCMD(m_device->OpenSharedHandle(handle.isNtHandle ? handle.ntHandle.get() : handle.handle, 439 | IID_PPV_ARGS(fence.ReleaseAndGetAddressOf()))); 440 | 441 | std::shared_ptr result = 442 | std::make_shared(fence.Get(), m_commandQueue.Get(), false /* shareable */); 443 | 444 | TraceLoggingWriteStop(local, "D3D12Fence_Import", TLPArg(result.get(), "Fence")); 445 | 446 | return result; 447 | } 448 | 449 | std::shared_ptr createTexture(const XrSwapchainCreateInfo& info, bool shareable) override { 450 | D3D12_RESOURCE_DESC desc{}; 451 | desc.Format = (DXGI_FORMAT)info.format; 452 | desc.Width = info.width; 453 | desc.Height = info.height; 454 | desc.DepthOrArraySize = info.arraySize; 455 | desc.MipLevels = info.mipCount; 456 | desc.SampleDesc.Count = info.sampleCount; 457 | desc.Flags = D3D12_RESOURCE_FLAG_NONE; 458 | D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON; 459 | if (info.usageFlags & XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT) { 460 | desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; 461 | initialState = D3D12_RESOURCE_STATE_RENDER_TARGET; 462 | } 463 | if (info.usageFlags & XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { 464 | desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; 465 | initialState = D3D12_RESOURCE_STATE_DEPTH_WRITE; 466 | } 467 | if (!(info.usageFlags & XR_SWAPCHAIN_USAGE_SAMPLED_BIT)) { 468 | desc.Flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; 469 | } 470 | if (info.usageFlags & XR_SWAPCHAIN_USAGE_UNORDERED_ACCESS_BIT) { 471 | desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; 472 | } 473 | 474 | ComPtr texture; 475 | D3D12_HEAP_PROPERTIES heapType{}; 476 | heapType.Type = D3D12_HEAP_TYPE_DEFAULT; 477 | heapType.CreationNodeMask = heapType.VisibleNodeMask = 1; 478 | CHECK_HRCMD(m_device->CreateCommittedResource(&heapType, 479 | shareable ? D3D12_HEAP_FLAG_SHARED : D3D12_HEAP_FLAG_NONE, 480 | &desc, 481 | initialState, 482 | nullptr, 483 | IID_PPV_ARGS(texture.ReleaseAndGetAddressOf()))); 484 | return std::make_shared(texture.Get()); 485 | } 486 | 487 | std::shared_ptr openTexture(const ShareableHandle& handle, 488 | const XrSwapchainCreateInfo& info) override { 489 | TraceLocalActivity(local); 490 | TraceLoggingWriteStart(local, 491 | "D3D12Texture_Import", 492 | TLArg(!handle.isNtHandle ? handle.handle : handle.ntHandle.get(), "Handle"), 493 | TLArg(handle.isNtHandle, "IsNTHandle")); 494 | 495 | ComPtr texture; 496 | CHECK_HRCMD(m_device->OpenSharedHandle(handle.isNtHandle ? handle.ntHandle.get() : handle.handle, 497 | IID_PPV_ARGS(texture.ReleaseAndGetAddressOf()))); 498 | 499 | std::shared_ptr result = std::make_shared(texture.Get()); 500 | 501 | TraceLoggingWriteStop(local, "D3D12Texture_Import", TLPArg(result.get(), "Texture")); 502 | 503 | return result; 504 | } 505 | 506 | std::shared_ptr openTexturePtr(void* nativeTexturePtr, 507 | const XrSwapchainCreateInfo& info) override { 508 | TraceLocalActivity(local); 509 | TraceLoggingWriteStart(local, "D3D12Texture_Import", TLPArg(nativeTexturePtr, "D3D12Texture")); 510 | 511 | ID3D12Resource* texture = reinterpret_cast(nativeTexturePtr); 512 | 513 | std::shared_ptr result = std::make_shared(texture); 514 | 515 | TraceLoggingWriteStop(local, "D3D12Texture_Import", TLPArg(result.get(), "Texture")); 516 | 517 | return result; 518 | } 519 | 520 | void copyTexture(IGraphicsTexture* from, IGraphicsTexture* to) override { 521 | TraceLocalActivity(local); 522 | TraceLoggingWriteStart(local, "D3D12Texture_Copy", TLPArg(from, "Source"), TLPArg(to, "Destination")); 523 | 524 | D3D12ReusableCommandList commandList = getCommandList(); 525 | commandList.commandList->CopyResource(to->getNativeTexture(), from->getNativeTexture()); 526 | submitCommandList(std::move(commandList)); 527 | 528 | TraceLoggingWriteStop(local, "D3D12Texture_Copy"); 529 | } 530 | 531 | GenericFormat translateToGenericFormat(int64_t format) const override { 532 | return (DXGI_FORMAT)format; 533 | } 534 | 535 | int64_t translateFromGenericFormat(GenericFormat format) const override { 536 | return (int64_t)format; 537 | } 538 | 539 | LUID getAdapterLuid() const override { 540 | return m_device->GetAdapterLuid(); 541 | } 542 | 543 | D3D12ReusableCommandList getCommandList() { 544 | std::unique_lock lock(m_commandListPoolMutex); 545 | 546 | if (m_availableCommandList.empty()) { 547 | // Recycle completed command lists. 548 | while (!m_pendingCommandList.empty() && m_commandListPoolFence->GetCompletedValue() >= 549 | m_pendingCommandList.front().completedFenceValue) { 550 | m_availableCommandList.push_back(std::move(m_pendingCommandList.front())); 551 | m_pendingCommandList.pop_front(); 552 | } 553 | } 554 | 555 | D3D12ReusableCommandList commandList; 556 | if (m_availableCommandList.empty()) { 557 | // Allocate a new command list if needed. 558 | CHECK_HRCMD(m_device->CreateCommandAllocator( 559 | D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(commandList.allocator.ReleaseAndGetAddressOf()))); 560 | CHECK_HRCMD( 561 | m_device->CreateCommandList(0, 562 | D3D12_COMMAND_LIST_TYPE_DIRECT, 563 | commandList.allocator.Get(), 564 | nullptr, 565 | IID_PPV_ARGS(commandList.commandList.ReleaseAndGetAddressOf()))); 566 | } else { 567 | commandList = m_availableCommandList.front(); 568 | m_availableCommandList.pop_front(); 569 | 570 | // Reset the command list before reuse. 571 | CHECK_HRCMD(commandList.commandList->Reset(commandList.allocator.Get(), nullptr)); 572 | } 573 | return commandList; 574 | } 575 | 576 | void submitCommandList(D3D12ReusableCommandList commandList) { 577 | std::unique_lock lock(m_commandListPoolMutex); 578 | 579 | CHECK_HRCMD(commandList.commandList->Close()); 580 | m_commandQueue->ExecuteCommandLists( 581 | 1, reinterpret_cast(commandList.commandList.GetAddressOf())); 582 | commandList.completedFenceValue = m_commandListPoolFenceValue + 1; 583 | m_commandQueue->Signal(m_commandListPoolFence.Get(), commandList.completedFenceValue); 584 | m_pendingCommandList.push_back(std::move(commandList)); 585 | } 586 | 587 | const ComPtr m_device; 588 | const ComPtr m_commandQueue; 589 | 590 | std::mutex m_commandListPoolMutex; 591 | std::deque m_availableCommandList; 592 | std::deque m_pendingCommandList; 593 | ComPtr m_commandListPoolFence; 594 | uint32_t m_commandListPoolFenceValue{0}; 595 | }; 596 | 597 | } // namespace 598 | 599 | namespace openxr_api_layer::utils::graphics::internal { 600 | 601 | std::shared_ptr wrapApplicationDevice(const XrGraphicsBindingD3D12KHR& bindings) { 602 | return std::make_shared(bindings.device, bindings.queue); 603 | } 604 | 605 | } // namespace openxr_api_layer::utils::graphics::internal 606 | 607 | #endif 608 | --------------------------------------------------------------------------------