├── .clang-format ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── question.md └── workflows │ └── msbuild.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── Pimax-OpenXR.sln ├── README.md ├── THIRD_PARTY ├── companion ├── App.config ├── ExperimentalSettings.Designer.cs ├── ExperimentalSettings.cs ├── ExperimentalSettings.resx ├── MainForm.Designer.cs ├── MainForm.cs ├── MainForm.resx ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── companion.csproj ├── icon.ico └── logo.bmp ├── external ├── OpenGL │ ├── GL │ │ ├── glext.h │ │ └── wglext.h │ └── KHR │ │ └── khrplatform.h └── aSeeVRClient │ ├── bin │ └── aSeeVRClient.dll │ ├── include │ ├── aSeeVRClient.h │ ├── aSeeVRTypes.h │ └── aSeeVRUtility.h │ └── lib │ └── aSeeVRClient.lib ├── installer ├── .gitignore ├── EULA.rtf ├── README.rtf ├── banner.bmp ├── comodo.pfx ├── installer.vdproj ├── selfsigncert.pfx └── signtool.exe ├── pimax-openxr ├── AlphaBlending.hlsli ├── AlphaBlendingCS.hlsl ├── AlphaBlendingTexArrayCS.hlsl ├── FullScreenQuadVS.hlsl ├── PassthroughPS.hlsl ├── action.cpp ├── companion.cpp ├── d3d11_native.cpp ├── d3d12_interop.cpp ├── display_refresh_rate.cpp ├── eye_tracking.cpp ├── frame.cpp ├── framework │ ├── __init__.py │ ├── dispatch.cpp │ ├── dispatch.gen.cpp │ ├── dispatch.gen.h │ ├── dispatch.h │ ├── dispatch_generator.py │ └── entry.cpp ├── gpu_timers.h ├── guardian.png ├── guardian.xcf ├── hand_tracking.cpp ├── instance.cpp ├── log.cpp ├── log.h ├── mappings.cpp ├── mirror_window.cpp ├── opengl_interop.cpp ├── overlay.cpp ├── overlay.png ├── packages.config ├── pch.cpp ├── pch.h ├── perf_counter.cpp ├── pimax-openxr-32.json ├── pimax-openxr.def ├── pimax-openxr.json ├── pimax-openxr.vcxproj ├── pimax-openxr.vcxproj.filters ├── resource.h ├── resource.rc ├── runtime.h ├── session.cpp ├── space.cpp ├── store.cpp ├── store.h ├── swapchain.cpp ├── system.cpp ├── utils.h ├── version.h ├── visibility_mask.cpp └── vulkan_interop.cpp ├── pimax_cli ├── pimax_cli.cpp ├── pimax_cli.vcxproj ├── pimax_cli.vcxproj.filters ├── resource.h └── resource.rc ├── pvr-logger ├── dllmain.cpp ├── packages.config ├── pch.cpp ├── pch.h ├── pvr-logger.vcxproj └── pvr-logger.vcxproj.filters ├── scripts ├── Build-CTS.ps1 ├── Install-Runtime.ps1 ├── PimaxOpenXR.wprp ├── Run-Perf.ps1 ├── msys-2.0.dll ├── msys-iconv-2.dll ├── msys-intl-8.dll └── sed.exe └── version.info /.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 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.gen.* text eol=lf 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: mbucchia 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **ANY ISSUE FILED WITHOUT THE INFORMATION REQUIRED BELOW WILL BE CLOSED WITHOUT BEING LOOKED AT. NO EXCEPTIONS.** 11 | 12 | **Describe the bug** 13 | 14 | _A clear and concise description of what the bug is._ 15 | _Always attach (upload, do not copy/paste) the log file that can be found at `%LocalAppData%\pimax-openxr\logs` or opened from the PimaxXR Control Center app._ 16 | 17 | **Environment** 18 | 19 | - _Application/Game and version_: 20 | - _Version of Pitool or Pimax client_: 21 | 22 | **To Reproduce** 23 | 24 | _Steps to reproduce the behavior:_ 25 | 26 | 1. Run '....' game 27 | 2. Enable option '....' 28 | 3. See error 29 | 30 | **Expected behavior** 31 | 32 | _A clear and concise description of what you expected to happen._ 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: PimaxXR Homepage 4 | url: https://github.com/mbucchia/Pimax-OpenXR/wiki 5 | about: The homepage has a lot of useful information, including Compatibility list. 6 | - name: OpenComposite Homepage 7 | url: https://gitlab.com/znixian/OpenOVR/-/tree/openxr 8 | about: If your questions/issues are with OpenComposite and not PimaxXR, head to their homepage for information. 9 | - name: OpenComposite Discord 10 | url: https://discord.gg/sQ2jwSb62J 11 | about: If your questions/issues are with OpenComposite and not PimaxXR, use their Discord. 12 | - name: OpenXR Toolkit Homepage 13 | url: https://mbucchia.github.io/OpenXR-Toolkit/ 14 | about: The homepage has A LOT OF INFORMATION if you are using OpenXR Toolkit. Check there first for the answer to your questions/issues. There is a Search bar at the top. 15 | - name: OpenXR Toolkit/PimaxXR Discord 16 | url: https://discord.gg/NxvH3x66js 17 | about: Finally, if you have checked all the information above, but still cannot address your questions/issues, ask and answer questions on our Discord. 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: 'Ask about this project. ' 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | _Give us as much details as possible_ 11 | -------------------------------------------------------------------------------- /.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: Pimax-OpenXR.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: "comodo" 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}}|Win32" 59 | devenv.com ${{env.SOLUTION_FILE_PATH}} /Build "${{env.BUILD_CONFIGURATION}}|x64" 60 | 61 | - name: Signing 62 | env: 63 | PFX_PASSWORD: ${{ secrets.PFX_PASSWORD }} 64 | PFX_NAME: "comodo" 65 | working-directory: ${{env.GITHUB_WORKSPACE}} 66 | run: | 67 | $pfxName = if ($env:PFX_NAME) { $env:PFX_NAME } else { "selfsigncert" }; 68 | installer/signtool.exe sign /d "PimaxXR" /du "https://github.com/mbucchia/Pimax-OpenXR" /f installer/$pfxName.pfx /p "$env:PFX_PASSWORD" /v installer/output/PimaxXR.msi 69 | 70 | - name: Publish 71 | uses: actions/upload-artifact@v2 72 | with: 73 | name: Setup 74 | path: | 75 | installer/output/PimaxXR.msi 76 | bin/Win32/Release/pimax-openxr-32.pdb 77 | bin/x64/Release/pimax-openxr.pdb 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | packages/ 3 | bin/ 4 | **/x64/ 5 | **/obj/ 6 | **/__pycache__/ 7 | *.pyc 8 | *.vcxproj.user 9 | *.csproj.user 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/OpenXR-SDK"] 2 | path = external/OpenXR-SDK 3 | url = https://github.com/KhronosGroup/OpenXR-SDK.git 4 | [submodule "external/OpenXR-SDK-Source"] 5 | path = external/OpenXR-SDK-Source 6 | url = https://github.com/KhronosGroup/OpenXR-SDK-Source.git 7 | [submodule "external/OpenXR-MixedReality"] 8 | path = external/OpenXR-MixedReality 9 | url = https://github.com/mbucchia/OpenXR-MixedReality.git 10 | [submodule "external/PVR"] 11 | path = external/PVR 12 | url = https://github.com/mbucchia/PVR.git 13 | [submodule "external/OpenXR-CTS"] 14 | path = external/OpenXR-CTS 15 | url = https://github.com/KhronosGroup/OpenXR-CTS.git 16 | [submodule "external/FW1FontWrapper"] 17 | path = external/FW1FontWrapper 18 | url = https://github.com/mbucchia/FW1FontWrapper.git 19 | [submodule "external/Vulkan-SDK"] 20 | path = external/Vulkan-SDK 21 | url = https://github.com/mbucchia/Vulkan-SDK.git 22 | [submodule "external/fmt"] 23 | path = external/fmt 24 | url = https://github.com/fmtlib/fmt.git 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 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 | -------------------------------------------------------------------------------- /Pimax-OpenXR.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}") = "pimax-openxr", "pimax-openxr\pimax-openxr.vcxproj", "{93D573D0-634F-4BA0-8FE0-FB63D7D00A05}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {9F62DB07-EA42-4388-82AB-E6FAA371F353} = {9F62DB07-EA42-4388-82AB-E6FAA371F353} 9 | EndProjectSection 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Files", "Solution Files", "{A53ED6CB-95D3-4833-8A16-C6A588F16F6E}" 12 | ProjectSection(SolutionItems) = preProject 13 | .clang-format = .clang-format 14 | .gitattributes = .gitattributes 15 | .gitignore = .gitignore 16 | LICENSE = LICENSE 17 | README.md = README.md 18 | version.info = version.info 19 | EndProjectSection 20 | EndProject 21 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{BA775DE9-671E-4E3B-92AC-9828C2AAF490}" 22 | ProjectSection(SolutionItems) = preProject 23 | scripts\Install-Runtime.ps1 = scripts\Install-Runtime.ps1 24 | scripts\PimaxOpenXR.wprp = scripts\PimaxOpenXR.wprp 25 | EndProjectSection 26 | EndProject 27 | Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "installer", "installer\installer.vdproj", "{8B253B1D-439A-4A30-AEC6-D1984861AD88}" 28 | EndProject 29 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub", "GitHub", "{8F6FBE40-3313-449F-ABA0-D7AB4578BC51}" 30 | ProjectSection(SolutionItems) = preProject 31 | .github\workflows\msbuild.yml = .github\workflows\msbuild.yml 32 | EndProjectSection 33 | EndProject 34 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "companion", "companion\companion.csproj", "{795212E0-1E96-44AB-8D87-9DA1751C0EB2}" 35 | ProjectSection(ProjectDependencies) = postProject 36 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05} = {93D573D0-634F-4BA0-8FE0-FB63D7D00A05} 37 | EndProjectSection 38 | EndProject 39 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pimax_cli", "pimax_cli\pimax_cli.vcxproj", "{C3EF2FE7-770A-448E-A3AC-226276092ABF}" 40 | EndProject 41 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestApps", "TestApps", "{18290AA7-D4EC-42C7-B417-D2CC3422A207}" 42 | EndProject 43 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BasicXrApp_win32", "external\OpenXR-MixedReality\samples\BasicXrApp\BasicXrApp_win32.vcxproj", "{A75A907B-8952-4ED2-BC2D-A68F09CEBD83}" 44 | ProjectSection(ProjectDependencies) = postProject 45 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05} = {93D573D0-634F-4BA0-8FE0-FB63D7D00A05} 46 | EndProjectSection 47 | EndProject 48 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleSceneWin32", "external\OpenXR-MixedReality\samples\SampleSceneWin32\SampleSceneWin32.vcxproj", "{4D888630-45DB-4CAA-8AAA-3C4CE33F90AB}" 49 | ProjectSection(ProjectDependencies) = postProject 50 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05} = {93D573D0-634F-4BA0-8FE0-FB63D7D00A05} 51 | EndProjectSection 52 | EndProject 53 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XrSceneLib_win32", "external\OpenXR-MixedReality\shared\XrSceneLib\XrSceneLib_win32.vcxproj", "{A758AF22-F54F-4C74-BF85-05A377B5892E}" 54 | EndProject 55 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Framework", "Framework", "{62ABB036-B5EF-46D5-8D8B-2C4D06164B4C}" 56 | EndProject 57 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Gltf_win32", "external\OpenXR-MixedReality\shared\gltf\Gltf_win32.vcxproj", "{6A3225A3-0750-47B7-8004-80CA543F8B8B}" 58 | EndProject 59 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pbr_win32", "external\OpenXR-MixedReality\shared\pbr\pbr_win32.vcxproj", "{2B7688F8-9AE6-4A67-809B-1BAC82094F21}" 60 | EndProject 61 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleShared_win32", "external\OpenXR-MixedReality\shared\SampleShared\SampleShared_win32.vcxproj", "{269C12FA-E68D-470B-A734-4701034306BD}" 62 | EndProject 63 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EyeGazeInteractionWin32", "external\OpenXR-MixedReality\samples\EyeGazeInteractionUwp\EyeGazeInteractionWin32.vcxproj", "{A4D2019B-622D-49B9-9510-16877979807A}" 64 | ProjectSection(ProjectDependencies) = postProject 65 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05} = {93D573D0-634F-4BA0-8FE0-FB63D7D00A05} 66 | EndProjectSection 67 | EndProject 68 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dependencies", "Dependencies", "{D5F26FBB-3CA3-40F8-859F-C599B2022DC4}" 69 | EndProject 70 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FW1FontWrapper", "external\FW1FontWrapper\FW1FontWrapper\FW1FontWrapper.vcxproj", "{9F62DB07-EA42-4388-82AB-E6FAA371F353}" 71 | EndProject 72 | Global 73 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 74 | Debug|Win32 = Debug|Win32 75 | Debug|x64 = Debug|x64 76 | Release|Win32 = Release|Win32 77 | Release|x64 = Release|x64 78 | EndGlobalSection 79 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 80 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Debug|Win32.ActiveCfg = Debug|Win32 81 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Debug|Win32.Build.0 = Debug|Win32 82 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Debug|x64.ActiveCfg = Debug|x64 83 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Debug|x64.Build.0 = Debug|x64 84 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Release|Win32.ActiveCfg = Release|Win32 85 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Release|Win32.Build.0 = Release|Win32 86 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Release|x64.ActiveCfg = Release|x64 87 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Release|x64.Build.0 = Release|x64 88 | {8B253B1D-439A-4A30-AEC6-D1984861AD88}.Debug|Win32.ActiveCfg = Release 89 | {8B253B1D-439A-4A30-AEC6-D1984861AD88}.Debug|x64.ActiveCfg = Release 90 | {8B253B1D-439A-4A30-AEC6-D1984861AD88}.Release|Win32.ActiveCfg = Release 91 | {8B253B1D-439A-4A30-AEC6-D1984861AD88}.Release|x64.ActiveCfg = Release 92 | {8B253B1D-439A-4A30-AEC6-D1984861AD88}.Release|x64.Build.0 = Release 93 | {795212E0-1E96-44AB-8D87-9DA1751C0EB2}.Debug|Win32.ActiveCfg = Debug|Any CPU 94 | {795212E0-1E96-44AB-8D87-9DA1751C0EB2}.Debug|Win32.Build.0 = Debug|Any CPU 95 | {795212E0-1E96-44AB-8D87-9DA1751C0EB2}.Debug|x64.ActiveCfg = Debug|Any CPU 96 | {795212E0-1E96-44AB-8D87-9DA1751C0EB2}.Debug|x64.Build.0 = Debug|Any CPU 97 | {795212E0-1E96-44AB-8D87-9DA1751C0EB2}.Release|Win32.ActiveCfg = Release|Any CPU 98 | {795212E0-1E96-44AB-8D87-9DA1751C0EB2}.Release|x64.ActiveCfg = Release|Any CPU 99 | {795212E0-1E96-44AB-8D87-9DA1751C0EB2}.Release|x64.Build.0 = Release|Any CPU 100 | {C3EF2FE7-770A-448E-A3AC-226276092ABF}.Debug|Win32.ActiveCfg = Debug|Win32 101 | {C3EF2FE7-770A-448E-A3AC-226276092ABF}.Debug|Win32.Build.0 = Debug|Win32 102 | {C3EF2FE7-770A-448E-A3AC-226276092ABF}.Debug|x64.ActiveCfg = Debug|x64 103 | {C3EF2FE7-770A-448E-A3AC-226276092ABF}.Debug|x64.Build.0 = Debug|x64 104 | {C3EF2FE7-770A-448E-A3AC-226276092ABF}.Release|Win32.ActiveCfg = Release|Win32 105 | {C3EF2FE7-770A-448E-A3AC-226276092ABF}.Release|x64.ActiveCfg = Release|x64 106 | {C3EF2FE7-770A-448E-A3AC-226276092ABF}.Release|x64.Build.0 = Release|x64 107 | {A75A907B-8952-4ED2-BC2D-A68F09CEBD83}.Debug|Win32.ActiveCfg = Debug|Win32 108 | {A75A907B-8952-4ED2-BC2D-A68F09CEBD83}.Debug|Win32.Build.0 = Debug|Win32 109 | {A75A907B-8952-4ED2-BC2D-A68F09CEBD83}.Debug|x64.ActiveCfg = Debug|x64 110 | {A75A907B-8952-4ED2-BC2D-A68F09CEBD83}.Debug|x64.Build.0 = Debug|x64 111 | {A75A907B-8952-4ED2-BC2D-A68F09CEBD83}.Release|Win32.ActiveCfg = Release|Win32 112 | {A75A907B-8952-4ED2-BC2D-A68F09CEBD83}.Release|x64.ActiveCfg = Release|x64 113 | {4D888630-45DB-4CAA-8AAA-3C4CE33F90AB}.Debug|Win32.ActiveCfg = Debug|Win32 114 | {4D888630-45DB-4CAA-8AAA-3C4CE33F90AB}.Debug|Win32.Build.0 = Debug|Win32 115 | {4D888630-45DB-4CAA-8AAA-3C4CE33F90AB}.Debug|x64.ActiveCfg = Debug|x64 116 | {4D888630-45DB-4CAA-8AAA-3C4CE33F90AB}.Debug|x64.Build.0 = Debug|x64 117 | {4D888630-45DB-4CAA-8AAA-3C4CE33F90AB}.Release|Win32.ActiveCfg = Release|Win32 118 | {4D888630-45DB-4CAA-8AAA-3C4CE33F90AB}.Release|x64.ActiveCfg = Release|x64 119 | {A758AF22-F54F-4C74-BF85-05A377B5892E}.Debug|Win32.ActiveCfg = Debug|Win32 120 | {A758AF22-F54F-4C74-BF85-05A377B5892E}.Debug|Win32.Build.0 = Debug|Win32 121 | {A758AF22-F54F-4C74-BF85-05A377B5892E}.Debug|x64.ActiveCfg = Debug|x64 122 | {A758AF22-F54F-4C74-BF85-05A377B5892E}.Debug|x64.Build.0 = Debug|x64 123 | {A758AF22-F54F-4C74-BF85-05A377B5892E}.Release|Win32.ActiveCfg = Release|Win32 124 | {A758AF22-F54F-4C74-BF85-05A377B5892E}.Release|x64.ActiveCfg = Release|x64 125 | {6A3225A3-0750-47B7-8004-80CA543F8B8B}.Debug|Win32.ActiveCfg = Debug|Win32 126 | {6A3225A3-0750-47B7-8004-80CA543F8B8B}.Debug|Win32.Build.0 = Debug|Win32 127 | {6A3225A3-0750-47B7-8004-80CA543F8B8B}.Debug|x64.ActiveCfg = Debug|x64 128 | {6A3225A3-0750-47B7-8004-80CA543F8B8B}.Debug|x64.Build.0 = Debug|x64 129 | {6A3225A3-0750-47B7-8004-80CA543F8B8B}.Release|Win32.ActiveCfg = Release|Win32 130 | {6A3225A3-0750-47B7-8004-80CA543F8B8B}.Release|x64.ActiveCfg = Release|x64 131 | {2B7688F8-9AE6-4A67-809B-1BAC82094F21}.Debug|Win32.ActiveCfg = Debug|Win32 132 | {2B7688F8-9AE6-4A67-809B-1BAC82094F21}.Debug|Win32.Build.0 = Debug|Win32 133 | {2B7688F8-9AE6-4A67-809B-1BAC82094F21}.Debug|x64.ActiveCfg = Debug|x64 134 | {2B7688F8-9AE6-4A67-809B-1BAC82094F21}.Debug|x64.Build.0 = Debug|x64 135 | {2B7688F8-9AE6-4A67-809B-1BAC82094F21}.Release|Win32.ActiveCfg = Release|Win32 136 | {2B7688F8-9AE6-4A67-809B-1BAC82094F21}.Release|x64.ActiveCfg = Release|x64 137 | {269C12FA-E68D-470B-A734-4701034306BD}.Debug|Win32.ActiveCfg = Debug|Win32 138 | {269C12FA-E68D-470B-A734-4701034306BD}.Debug|Win32.Build.0 = Debug|Win32 139 | {269C12FA-E68D-470B-A734-4701034306BD}.Debug|x64.ActiveCfg = Debug|x64 140 | {269C12FA-E68D-470B-A734-4701034306BD}.Debug|x64.Build.0 = Debug|x64 141 | {269C12FA-E68D-470B-A734-4701034306BD}.Release|Win32.ActiveCfg = Release|Win32 142 | {269C12FA-E68D-470B-A734-4701034306BD}.Release|x64.ActiveCfg = Release|x64 143 | {A4D2019B-622D-49B9-9510-16877979807A}.Debug|Win32.ActiveCfg = Debug|Win32 144 | {A4D2019B-622D-49B9-9510-16877979807A}.Debug|Win32.Build.0 = Debug|Win32 145 | {A4D2019B-622D-49B9-9510-16877979807A}.Debug|x64.ActiveCfg = Debug|x64 146 | {A4D2019B-622D-49B9-9510-16877979807A}.Debug|x64.Build.0 = Debug|x64 147 | {A4D2019B-622D-49B9-9510-16877979807A}.Release|Win32.ActiveCfg = Release|Win32 148 | {A4D2019B-622D-49B9-9510-16877979807A}.Release|x64.ActiveCfg = Release|x64 149 | {9F62DB07-EA42-4388-82AB-E6FAA371F353}.Debug|Win32.ActiveCfg = Debug|Win32 150 | {9F62DB07-EA42-4388-82AB-E6FAA371F353}.Debug|Win32.Build.0 = Debug|Win32 151 | {9F62DB07-EA42-4388-82AB-E6FAA371F353}.Debug|x64.ActiveCfg = Debug|x64 152 | {9F62DB07-EA42-4388-82AB-E6FAA371F353}.Debug|x64.Build.0 = Debug|x64 153 | {9F62DB07-EA42-4388-82AB-E6FAA371F353}.Release|Win32.ActiveCfg = Release|Win32 154 | {9F62DB07-EA42-4388-82AB-E6FAA371F353}.Release|Win32.Build.0 = Release|Win32 155 | {9F62DB07-EA42-4388-82AB-E6FAA371F353}.Release|x64.ActiveCfg = Release|x64 156 | {9F62DB07-EA42-4388-82AB-E6FAA371F353}.Release|x64.Build.0 = Release|x64 157 | EndGlobalSection 158 | GlobalSection(SolutionProperties) = preSolution 159 | HideSolutionNode = FALSE 160 | EndGlobalSection 161 | GlobalSection(NestedProjects) = preSolution 162 | {BA775DE9-671E-4E3B-92AC-9828C2AAF490} = {A53ED6CB-95D3-4833-8A16-C6A588F16F6E} 163 | {8F6FBE40-3313-449F-ABA0-D7AB4578BC51} = {A53ED6CB-95D3-4833-8A16-C6A588F16F6E} 164 | {A75A907B-8952-4ED2-BC2D-A68F09CEBD83} = {18290AA7-D4EC-42C7-B417-D2CC3422A207} 165 | {4D888630-45DB-4CAA-8AAA-3C4CE33F90AB} = {18290AA7-D4EC-42C7-B417-D2CC3422A207} 166 | {A758AF22-F54F-4C74-BF85-05A377B5892E} = {62ABB036-B5EF-46D5-8D8B-2C4D06164B4C} 167 | {62ABB036-B5EF-46D5-8D8B-2C4D06164B4C} = {18290AA7-D4EC-42C7-B417-D2CC3422A207} 168 | {6A3225A3-0750-47B7-8004-80CA543F8B8B} = {62ABB036-B5EF-46D5-8D8B-2C4D06164B4C} 169 | {2B7688F8-9AE6-4A67-809B-1BAC82094F21} = {62ABB036-B5EF-46D5-8D8B-2C4D06164B4C} 170 | {269C12FA-E68D-470B-A734-4701034306BD} = {62ABB036-B5EF-46D5-8D8B-2C4D06164B4C} 171 | {A4D2019B-622D-49B9-9510-16877979807A} = {18290AA7-D4EC-42C7-B417-D2CC3422A207} 172 | {9F62DB07-EA42-4388-82AB-E6FAA371F353} = {D5F26FBB-3CA3-40F8-859F-C599B2022DC4} 173 | EndGlobalSection 174 | GlobalSection(ExtensibilityGlobals) = postSolution 175 | SolutionGuid = {07E77829-9766-4585-AC6C-0A28BA014E77} 176 | EndGlobalSection 177 | EndGlobal 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PimaxXR: an implementation of the OpenXR standard for Pimax headsets 2 | 3 | This program is an implementation of the OpenXR standard for Pimax headsets on Windows. It allows you to run OpenXR applications without SteamVR. It is not produced by Pimax. 4 | 5 | It is built on top of the official Pimax PVR native SDK and aims to provide a very fast and efficient implementation of the OpenXR standard for Pimax headsets. 6 | 7 | DISCLAIMER: This runtime is not officially conformant per Khronos standards: it cannot be called "conformant" nor use the OpenXR trademark and logo. However, the runtime passes the majority of OpenXR conformance tests and the reason for not seeking official conformance is the required $30,000 adopter fee. 8 | 9 | DISCLAIMER: This software is distributed as-is, without any warranties or conditions of any kind. Use at your own risks. 10 | 11 | # Details and instructions on the Wiki: https://github.com/mbucchia/Pimax-OpenXR/wiki 12 | 13 | ## Donate 14 | 15 | Donations are welcome and totally optional. Please use [my GitHub sponsorship page](https://github.com/sponsors/mbucchia) to make one-time or recurring donations! 16 | 17 | Thank you! 18 | 19 | ## Contributors 20 | 21 | Logo and icons designed by Smitty. 22 | 23 | Special thanks to the following individuals for their contributions: 24 | 25 | - Vladimir Dranyonkov 26 | - Pavel Skakov 27 | - DJSlane 28 | - TommyVR 29 | - [Omniwhatever](https://www.youtube.com/@Omniwhatever) 30 | 31 | ## Very special thanks 32 | 33 | Very special thanks to Pimax for providing me with a Pimax 8KX and a Pimax Crystal for development. 34 | -------------------------------------------------------------------------------- /companion/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /companion/ExperimentalSettings.cs: -------------------------------------------------------------------------------- 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 | using System; 24 | using System.Collections.Generic; 25 | using System.ComponentModel; 26 | using System.Data; 27 | using System.Drawing; 28 | using System.Linq; 29 | using System.Text; 30 | using System.Threading.Tasks; 31 | using System.Windows.Forms; 32 | 33 | namespace companion 34 | { 35 | public partial class ExperimentalSettings : Form 36 | { 37 | private bool loading = true; 38 | 39 | public ExperimentalSettings() 40 | { 41 | InitializeComponent(); 42 | LoadSettings(); 43 | } 44 | 45 | private void ExperimentalSettings_FormClosing(object sender, FormClosingEventArgs e) 46 | { 47 | if (e.CloseReason == CloseReason.UserClosing) 48 | { 49 | e.Cancel = true; 50 | Hide(); 51 | } 52 | } 53 | 54 | public void LoadSettings() 55 | { 56 | loading = true; 57 | SuspendLayout(); 58 | 59 | Microsoft.Win32.RegistryKey key = null; 60 | 61 | // Read the PimaxXR configuration. 62 | try 63 | { 64 | key = Microsoft.Win32.Registry.LocalMachine.CreateSubKey(MainForm.RegPrefix); 65 | 66 | // Must match the defaults in the runtime! 67 | enableFrameTiming.Checked = (int)key.GetValue("quirk_frame_timing_override", 0) == 1 ? true : false; 68 | filterLength.Value = (int)key.GetValue("frame_time_filter_length", 5); 69 | var multiplier = (int)key.GetValue("frame_time_override_multiplier", 0); 70 | if (multiplier == 100) 71 | { 72 | forceHalf.Checked = true; 73 | } 74 | else if (multiplier == 200) 75 | { 76 | forceThird.Checked = true; 77 | } 78 | else 79 | { 80 | forceHalf.Checked = forceThird.Checked = false; 81 | } 82 | // Convert value from microseconds to tenth of milliseconds. 83 | timingBias.Value = multiplier == 0 ? ((int)key.GetValue("frame_time_override_offset", 0) / 100) : 0; 84 | waitForGpuWorkInEndFrame.Checked = (int)key.GetValue("quirk_sync_gpu_work_in_end_frame", 0) == 1 ? true : false; 85 | forceDisableParallelProjection.Checked = (int)key.GetValue("force_parallel_projection_state", 1) == 0 ? true : false; 86 | droolonProjectionDistance.Value = (int)key.GetValue("droolon_projection_distance", 35); 87 | 88 | // DO NOT FORGET TO ADD TO restoreDefaults_Click()! 89 | } 90 | catch (Exception) 91 | { 92 | MessageBox.Show(this, "Failed to write to registry. Please make sure the app is running elevated.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 93 | } 94 | finally 95 | { 96 | if (key != null) 97 | { 98 | key.Close(); 99 | } 100 | } 101 | 102 | RefreshEnabledState(); 103 | filterLength_Scroll(null, null); 104 | timingBias_Scroll(null, null); 105 | droolonProjectionDistance_Scroll(null, null); 106 | 107 | ResumeLayout(); 108 | loading = false; 109 | } 110 | 111 | private void RefreshEnabledState() 112 | { 113 | forceRateLabel.Enabled = forceHalf.Enabled = forceThird.Enabled = enableFrameTiming.Checked; 114 | filterLength.Enabled = filterLengthLabel.Enabled = filterLengthValue.Enabled = 115 | timingBias.Enabled = timingBiasLabel.Enabled = timingBiasValue.Enabled = enableFrameTiming.Checked && !(forceHalf.Checked || forceThird.Checked); 116 | } 117 | 118 | private void enableFrameTiming_CheckedChanged(object sender, EventArgs e) 119 | { 120 | RefreshEnabledState(); 121 | 122 | if (loading) 123 | { 124 | return; 125 | } 126 | 127 | MainForm.WriteSetting("quirk_frame_timing_override", enableFrameTiming.Checked ? 1 : 0); 128 | } 129 | 130 | private void filterLength_Scroll(object sender, EventArgs e) 131 | { 132 | filterLengthValue.Text = filterLength.Value.ToString(); 133 | 134 | if (loading) 135 | { 136 | return; 137 | } 138 | 139 | MainForm.WriteSetting("frame_time_filter_length", filterLength.Value); 140 | } 141 | 142 | private void timingBias_Scroll(object sender, EventArgs e) 143 | { 144 | // Use the input in tenth of milliseconds to allow one decimal. 145 | timingBiasValue.Text = timingBias.Value != 0 ? (timingBias.Value / 10.0f).ToString("#.##") : "0"; 146 | 147 | if (loading) 148 | { 149 | return; 150 | } 151 | 152 | // Store in microseconds. 153 | MainForm.WriteSetting("frame_time_override_offset", timingBias.Value * 100); 154 | } 155 | 156 | private void forceHalf_CheckedChanged(object sender, EventArgs e) 157 | { 158 | forceThird.Checked = false; 159 | RefreshEnabledState(); 160 | 161 | if (loading) 162 | { 163 | return; 164 | } 165 | 166 | if (forceHalf.Checked) 167 | { 168 | // Force 100% frame duration + 1ms. 169 | MainForm.WriteSetting("frame_time_override_multiplier", 100); 170 | MainForm.WriteSetting("frame_time_override_offset", 1000); 171 | } 172 | else 173 | { 174 | MainForm.WriteSetting("frame_time_override_multiplier", 0); 175 | timingBias_Scroll(null, null); 176 | } 177 | } 178 | 179 | private void forceThird_CheckedChanged(object sender, EventArgs e) 180 | { 181 | forceHalf.Checked = false; 182 | RefreshEnabledState(); 183 | 184 | if (loading) 185 | { 186 | return; 187 | } 188 | 189 | if (forceThird.Checked) 190 | { 191 | // Force 200% frame duration + 1ms. 192 | MainForm.WriteSetting("frame_time_override_multiplier", 200); 193 | MainForm.WriteSetting("frame_time_override_offset", 1000); 194 | } 195 | else 196 | { 197 | MainForm.WriteSetting("frame_time_override_multiplier", 0); 198 | timingBias_Scroll(null, null); 199 | } 200 | } 201 | 202 | private void waitForGpuWorkInEndFrame_CheckedChanged(object sender, EventArgs e) 203 | { 204 | if (loading) 205 | { 206 | return; 207 | } 208 | 209 | MainForm.WriteSetting("quirk_sync_gpu_work_in_end_frame", waitForGpuWorkInEndFrame.Checked ? 1 : 0); 210 | } 211 | 212 | private void forceDisableParallelProjection_CheckedChanged(object sender, EventArgs e) 213 | { 214 | if (loading) 215 | { 216 | return; 217 | } 218 | 219 | if (forceDisableParallelProjection.Checked) 220 | { 221 | MainForm.WriteSetting("force_parallel_projection_state", 0); 222 | } 223 | else 224 | { 225 | Microsoft.Win32.RegistryKey key = null; 226 | try 227 | { 228 | key = Microsoft.Win32.Registry.LocalMachine.CreateSubKey(MainForm.RegPrefix); 229 | key.DeleteValue("force_parallel_projection_state", false); 230 | } 231 | catch (Exception) 232 | { 233 | } 234 | finally 235 | { 236 | if (key != null) 237 | { 238 | key.Close(); 239 | } 240 | } 241 | } 242 | } 243 | 244 | private void droolonProjectionDistance_Scroll(object sender, EventArgs e) 245 | { 246 | droolonProjectionDistanceValue.Text = droolonProjectionDistance.Value != 0 ? (droolonProjectionDistance.Value / 100.0f).ToString("#.##") : "0"; 247 | 248 | if (loading) 249 | { 250 | return; 251 | } 252 | 253 | MainForm.WriteSetting("droolon_projection_distance", droolonProjectionDistance.Value); 254 | } 255 | 256 | private void restoreDefaults_Click(object sender, EventArgs e) 257 | { 258 | Microsoft.Win32.RegistryKey key = null; 259 | 260 | try 261 | { 262 | key = Microsoft.Win32.Registry.LocalMachine.CreateSubKey(MainForm.RegPrefix); 263 | 264 | key.DeleteValue("quirk_frame_timing_override", false); 265 | key.DeleteValue("frame_time_filter_length", false); 266 | key.DeleteValue("frame_time_override_multiplier", false); 267 | key.DeleteValue("frame_time_override_offset", false); 268 | key.DeleteValue("quirk_sync_gpu_work_in_end_frame", false); 269 | key.DeleteValue("force_parallel_projection_state", false); 270 | key.DeleteValue("droolon_projection_distance", false); 271 | } 272 | catch (Exception) 273 | { 274 | } 275 | finally 276 | { 277 | if (key != null) 278 | { 279 | key.Close(); 280 | } 281 | } 282 | 283 | LoadSettings(); 284 | } 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /companion/Program.cs: -------------------------------------------------------------------------------- 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 | using System; 24 | using System.Collections.Generic; 25 | using System.ComponentModel; 26 | using System.Diagnostics; 27 | using System.Linq; 28 | using System.Reflection; 29 | using System.Security.Principal; 30 | using System.Threading.Tasks; 31 | using System.Windows.Forms; 32 | 33 | namespace companion 34 | { 35 | static class Program 36 | { 37 | /// 38 | /// The main entry point for the application. 39 | /// 40 | [STAThread] 41 | static void Main() 42 | { 43 | #if !DEBUG 44 | var principal = new WindowsPrincipal(WindowsIdentity.GetCurrent()); 45 | if (!principal.IsInRole(WindowsBuiltInRole.Administrator)) 46 | { 47 | var processInfo = new System.Diagnostics.ProcessStartInfo(); 48 | processInfo.Verb = "RunAs"; 49 | processInfo.FileName = Assembly.GetEntryAssembly().Location; 50 | try 51 | { 52 | Process.Start(processInfo).WaitForExit(); 53 | } 54 | catch (Win32Exception) 55 | { 56 | MessageBox.Show("This application must be run as Administrator.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 57 | } 58 | Application.Exit(); 59 | return; 60 | } 61 | #endif 62 | Application.EnableVisualStyles(); 63 | Application.SetCompatibleTextRenderingDefault(false); 64 | Application.Run(new MainForm()); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /companion/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("PimaxXR - OpenXR Control Center")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("companion")] 13 | [assembly: AssemblyCopyright("Copyright 2022 Matthieu Bucchianeri")] 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("795212e0-1e96-44ab-8d87-9da1751c0eb2")] 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("0.4.5.0")] 36 | [assembly: AssemblyFileVersion("0.4.5.0")] 37 | -------------------------------------------------------------------------------- /companion/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | 12 | namespace companion.Properties 13 | { 14 | /// 15 | /// A strongly-typed resource class, for looking up localized strings, etc. 16 | /// 17 | // This class was auto-generated by the StronglyTypedResourceBuilder 18 | // class via a tool like ResGen or Visual Studio. 19 | // To add or remove a member, edit your .ResX file then rerun ResGen 20 | // with the /str option, or rebuild your VS project. 21 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 22 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 23 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 24 | internal class Resources 25 | { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() 33 | { 34 | } 35 | 36 | /// 37 | /// Returns the cached ResourceManager instance used by this class. 38 | /// 39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 40 | internal static global::System.Resources.ResourceManager ResourceManager 41 | { 42 | get 43 | { 44 | if ((resourceMan == null)) 45 | { 46 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("companion.Properties.Resources", typeof(Resources).Assembly); 47 | resourceMan = temp; 48 | } 49 | return resourceMan; 50 | } 51 | } 52 | 53 | /// 54 | /// Overrides the current thread's CurrentUICulture property for all 55 | /// resource lookups using this strongly typed resource class. 56 | /// 57 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 58 | internal static global::System.Globalization.CultureInfo Culture 59 | { 60 | get 61 | { 62 | return resourceCulture; 63 | } 64 | set 65 | { 66 | resourceCulture = value; 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /companion/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /companion/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | 12 | namespace companion.Properties 13 | { 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 17 | { 18 | 19 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 20 | 21 | public static Settings Default 22 | { 23 | get 24 | { 25 | return defaultInstance; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /companion/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /companion/companion.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {795212E0-1E96-44AB-8D87-9DA1751C0EB2} 8 | WinExe 9 | companion 10 | companion 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | ..\bin\x64\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | true 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | ..\bin\x64\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | false 37 | true 38 | 39 | 40 | icon.ico 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Form 60 | 61 | 62 | ExperimentalSettings.cs 63 | 64 | 65 | Form 66 | 67 | 68 | MainForm.cs 69 | 70 | 71 | 72 | 73 | ExperimentalSettings.cs 74 | 75 | 76 | MainForm.cs 77 | 78 | 79 | ResXFileCodeGenerator 80 | Resources.Designer.cs 81 | Designer 82 | 83 | 84 | True 85 | Resources.resx 86 | 87 | 88 | SettingsSingleFileGenerator 89 | Settings.Designer.cs 90 | 91 | 92 | True 93 | Settings.settings 94 | True 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | if not defined PFX_PASSWORD goto skip_signing 106 | if not defined PFX_NAME set PFX_NAME=selfsigncert 107 | $(SolutionDir)\installer\signtool.exe sign /d "PimaxXR" /du "https://github.com/mbucchia/Pimax-OpenXR" /f $(SolutionDir)\installer\%25PFX_NAME%25.pfx /p "%25PFX_PASSWORD%25" /v $(TargetPath) 108 | REM The installer picks up the file from obj... Sign this one too. 109 | $(SolutionDir)\installer\signtool.exe sign /d "PimaxXR" /du "https://github.com/mbucchia/Pimax-OpenXR" /f $(SolutionDir)\installer\%25PFX_NAME%25.pfx /p "%25PFX_PASSWORD%25" /v $(ProjectDir)\obj\$(ConfigurationName)\$(TargetFilename) 110 | :skip_signing 111 | 112 | 113 | if not exist $(SolutionDir)\version.info goto :skip_version 114 | for /f "delims== tokens=1,2" %25%25G in ($(SolutionDir)\version.info) do set %25%25G=%25%25H 115 | $(SolutionDir)\scripts\sed.exe -i "s/^\[assembly: AssemblyVersion(\".*\")\]$/\[assembly: AssemblyVersion(\"%25major%25.%25minor%25.%25patch%25.0\")\]/g" $(ProjectDir)\Properties\AssemblyInfo.cs 116 | $(SolutionDir)\scripts\sed.exe -i "s/^\[assembly: AssemblyFileVersion(\".*\")\]$/\[assembly: AssemblyFileVersion(\"%25major%25.%25minor%25.%25patch%25.0\")\]/g" $(ProjectDir)\Properties\AssemblyInfo.cs 117 | :skip_version 118 | 119 | -------------------------------------------------------------------------------- /companion/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Pimax-OpenXR/2f54824af1ac05451d61c1ccf3a281e74e67df0c/companion/icon.ico -------------------------------------------------------------------------------- /companion/logo.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Pimax-OpenXR/2f54824af1ac05451d61c1ccf3a281e74e67df0c/companion/logo.bmp -------------------------------------------------------------------------------- /external/aSeeVRClient/bin/aSeeVRClient.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Pimax-OpenXR/2f54824af1ac05451d61c1ccf3a281e74e67df0c/external/aSeeVRClient/bin/aSeeVRClient.dll -------------------------------------------------------------------------------- /external/aSeeVRClient/include/aSeeVRClient.h: -------------------------------------------------------------------------------- 1 | /*******************************************************************************/ 2 | /* 3 | Copyright (C) 2016 - 2020, Beijing 7invensun Technology Co.Ltd.All rights reserved. 4 | Permission is hereby granted, free of charge, to any person or organization obtaining 5 | a copy of the software and accompanying documentation covered by this license(the "Software") 6 | to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare 7 | derivative works of the Software, and to permit third - parties to whom the Software 8 | is furnished to do so, all subject to the following : 9 | The copyright notices in the Software and this entire statement, including the above 10 | license grant, this restriction and the following disclaimer, must be included in all 11 | copies of the Software, in whole or in part, and all derivative works of the Software, 12 | unless such copies or derivative works are solely in the form of machine - executable 13 | object code generated by a source language processor. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 17 | PURPOSE, TITLE AND NON - INFRINGEMENT.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE 18 | DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 | OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | @author 7invensun 23 | */ 24 | #ifndef _INCLUDE_7INVENSUN_ASEEVR_API_H_ 25 | #define _INCLUDE_7INVENSUN_ASEEVR_API_H_ 26 | 27 | #include "aSeeVRTypes.h" 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | /** A callback function to receive asynchronous state from other APIs, registrates though aSeeVR_register_callback function. 34 | * @param state A state(Refer to the definition of the aSeeVRState structure). 35 | * @param context Used to pass the context of the caller, can be empty. 36 | * @return void 37 | */ 38 | typedef void (_7INVENSUN_CALL *aSeeVR_state_callback)(const aSeeVRState* state, void* context); 39 | 40 | /** A callback function to receive an eye image��registrates though aSeeVR_register_callback function. 41 | * @param image Eye image data (Refer to the definition of the aSeeVRImage structure). 42 | * @param context Used to pass the context of the caller, can be empty. 43 | * @return void 44 | */ 45 | typedef void (_7INVENSUN_CALL *aSeeVR_eye_image_callback)(const aSeeVRImage* image, void* context); 46 | 47 | /** A callback function to receive eye featrues and gaze points, registrates though aSeeVR_register_callback function. 48 | 49 | * @param state Eye data(Refer to the definition of the aSeeVREyeData structure). 50 | * @param context Used to pass the context of the caller, can be empty. 51 | * @return void 52 | */ 53 | typedef void (_7INVENSUN_CALL *aSeeVR_eye_data_callback)(const aSeeVREyeData* eye_data, void* context); 54 | 55 | /** A callback function for receiving the calibration coefficient, registrates though aSeeVR_register_callback function. 56 | * @param data Calibration coefficient( Refer to the definition of the aSeeVRCoefficient structure). 57 | * @param context Used to pass the context of the caller, can be empty. 58 | * @return void 59 | */ 60 | typedef void (_7INVENSUN_CALL *aSeeVR_coefficient_callback)(const aSeeVRCoefficient* data, void* context); 61 | 62 | /** Registering Callback Function 63 | * @param type Function type. 64 | * @param cb Callback function pointer. 65 | * @param context Used to pass the context of the caller, can be empty. 66 | * @return Refer to the definition of the aSeeVRReturnCode structure. 67 | */ 68 | _7INVENSUN_API aSeeVRReturnCode _7INVENSUN_CALL aSeeVR_register_callback(aSeeVRCallbackType type, void* cb, void* context); 69 | 70 | /** Connect to the Server. 71 | * @param param Initialization parameter(Refer to the definition of the aSeeVRImage structure). 72 | * @return Refer to the definition of the aSeeVRReturnCode structure. 73 | */ 74 | _7INVENSUN_API aSeeVRReturnCode _7INVENSUN_CALL aSeeVR_connect_server(const aSeeVRInitParam* param); 75 | 76 | /** Disconnect from the Server. 77 | * @param void 78 | * @return Refer to the definition of the aSeeVRReturnCode structure. 79 | */ 80 | _7INVENSUN_API aSeeVRReturnCode _7INVENSUN_CALL aSeeVR_disconnect_server(void); 81 | 82 | /** Start the device and start receiving data. 83 | * @param state A state(param descriptive parameter) 84 | * @param context Used to pass the context of the caller, can be empty. 85 | * @return Refer to the definition in aSeeVRReturnCode. 86 | * @note This function sends an asynchronous request to the server, and the return value only indicates whether the asynchronous request was successfully sent, whether the device is successfully stopped is returned asynchronously through the aSeeVR_state_callback function. 87 | * @pre Function aSeeVR_connect_server has been called successfully, has successfully registered aSeeVR_state_callback through aSeeVR_register_callback. 88 | */ 89 | _7INVENSUN_API aSeeVRReturnCode _7INVENSUN_CALL aSeeVR_start(const aSeeVRCoefficient* coe, const aSeeVRLanuchParam* others = 0); 90 | 91 | /** Halt Device. 92 | * @param void 93 | * @return Refer to the definition of the aSeeVRReturnCode structure. 94 | * @note This function sends an asynchronous request to the server, and the return value only indicates whether the asynchronous request was successfully sent, whether the device is successfully stopped is returned asynchronously through the aSeeVR_state_callback function. 95 | * @pre Function aSeeVR_connect_server has been called successfully,has successfully registered aSeeVR_state_callback through aSeeVR_register_callback. 96 | */ 97 | _7INVENSUN_API aSeeVRReturnCode _7INVENSUN_CALL aSeeVR_stop(void); 98 | 99 | /** Obtain Calibration Coefficient. 100 | * @param void 101 | * @return Refer to the definition of the aSeeVRReturnCode structure. 102 | * @note This function sends an asynchronous request to the server, and the return value only indicates whether the asynchronous request was successfully sent,calibration coefficient is returned by aSeeVR_coefficient_callback function asynchronously. 103 | * @pre Function aSeeVR_connect_server has been called successfully, has successfully registered aSeeVR_coefficient_callback through aSeeVR_register_callback. 104 | */ 105 | _7INVENSUN_API aSeeVRReturnCode _7INVENSUN_CALL aSeeVR_get_coefficient(void); 106 | 107 | #ifdef __cplusplus 108 | } 109 | #endif 110 | 111 | #endif//_INCLUDE_7INVENSUN_ASEEVR_API_H_ -------------------------------------------------------------------------------- /external/aSeeVRClient/include/aSeeVRTypes.h: -------------------------------------------------------------------------------- 1 | /*******************************************************************************/ 2 | /* 3 | Copyright (C) 2016 - 2020, Beijing 7invensun Technology Co.Ltd.All rights reserved. 4 | Permission is hereby granted, free of charge, to any person or organization obtaining 5 | a copy of the software and accompanying documentation covered by this license(the "Software") 6 | to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare 7 | derivative works of the Software, and to permit third - parties to whom the Software 8 | is furnished to do so, all subject to the following : 9 | The copyright notices in the Software and this entire statement, including the above 10 | license grant, this restriction and the following disclaimer, must be included in all 11 | copies of the Software, in whole or in part, and all derivative works of the Software, 12 | unless such copies or derivative works are solely in the form of machine - executable 13 | object code generated by a source language processor. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 17 | PURPOSE, TITLE AND NON - INFRINGEMENT.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE 18 | DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 | OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | @author 7invensun 23 | */ 24 | #ifndef _INCLUDE_7INVENSUN_ASEEVR_TYPES_H_ 25 | #define _INCLUDE_7INVENSUN_ASEEVR_TYPES_H_ 26 | 27 | #ifdef _MSC_VER 28 | # ifdef _7INVENSUN_ASEEVRCLIENT_DLL_EXPORTS 29 | # define _7INVENSUN_API __declspec(dllexport) 30 | # else 31 | # define _7INVENSUN_API __declspec(dllimport) 32 | # endif//_7INVENSUN_ASEEVRCLIENT_DLL_EXPORTS 33 | # ifndef _7INVENSUN_CALL 34 | # define _7INVENSUN_CALL __stdcall 35 | # endif//_7INVENSUN_CALL 36 | #else 37 | # define _7INVENSUN_API 38 | # define _7INVENSUN_CALL 39 | #endif//_MSC_VER 40 | 41 | #ifndef uint8_t 42 | typedef unsigned char uint8_t; 43 | #endif 44 | 45 | #ifndef int16_t 46 | typedef short int16_t; 47 | #endif 48 | 49 | #ifndef int32_t 50 | typedef int int32_t; 51 | #endif 52 | 53 | #ifndef uint32_t 54 | typedef unsigned int uint32_t; 55 | #endif 56 | 57 | #ifndef int64_t 58 | typedef long long int64_t; 59 | #endif 60 | 61 | #ifndef uint64_t 62 | typedef unsigned long long uint64_t; 63 | #endif 64 | 65 | /** 66 | * @enum aSeeVRCallbackType 67 | * Enumerate all the callback function types used in the API. 68 | */ 69 | typedef enum ASEEVR_CALLBACK_TYPE { 70 | state, /*! out_texture : register(u0); 12 | 13 | [numthreads(32, 32, 1)] 14 | void main(uint2 pos : SV_DispatchThreadID) { 15 | uint width, height; 16 | in_texture.GetDimensions(width, height); 17 | out_texture[pos] = 18 | processAlpha(in_texture[pos], pos, uint2(width, height), ignoreAlpha, isUnpremultipliedAlpha); 19 | } 20 | -------------------------------------------------------------------------------- /pimax-openxr/AlphaBlendingTexArrayCS.hlsl: -------------------------------------------------------------------------------- 1 | // Clear or set the alpha channel and/or premultiply each component. 2 | 3 | #include "AlphaBlending.hlsli" 4 | 5 | cbuffer config : register(b0) { 6 | bool ignoreAlpha; 7 | bool isUnpremultipliedAlpha; 8 | }; 9 | 10 | Texture2DArray in_texture : register(t0); 11 | RWTexture2D out_texture : register(u0); 12 | 13 | [numthreads(32, 32, 1)] 14 | void main(uint2 pos : SV_DispatchThreadID) { 15 | uint width, height, size; 16 | in_texture.GetDimensions(width, height, size); 17 | out_texture[pos] = processAlpha( 18 | in_texture[float3(pos, 0)], pos, uint2(width, height), ignoreAlpha, isUnpremultipliedAlpha); 19 | } 20 | -------------------------------------------------------------------------------- /pimax-openxr/FullScreenQuadVS.hlsl: -------------------------------------------------------------------------------- 1 | // A simple full screen quad. 2 | void main(in uint id : SV_VertexID, out float4 position : SV_POSITION, out float2 texcoord : TEXCOORD0) { 3 | texcoord = float2((id == 1) ? 2.0 : 0.0, (id == 2) ? 2.0 : 0.0); 4 | position = float4(texcoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); 5 | } 6 | -------------------------------------------------------------------------------- /pimax-openxr/PassthroughPS.hlsl: -------------------------------------------------------------------------------- 1 | // A simple passthrough shader (for SRGB color conversion). 2 | SamplerState sourceSampler : register(s0); 3 | Texture2D sourceTexture : register(t0); 4 | 5 | float4 main(in float4 position : SV_POSITION, in float2 texcoord : TEXCOORD0) : SV_TARGET { 6 | return sourceTexture.Sample(sourceSampler, texcoord); 7 | } 8 | -------------------------------------------------------------------------------- /pimax-openxr/companion.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 | #include "log.h" 26 | #include "runtime.h" 27 | #include "utils.h" 28 | 29 | using namespace pimax_openxr::utils; 30 | 31 | struct RuntimeStatus { 32 | bool valid; 33 | 34 | float refreshRate; 35 | uint32_t resolutionWidth; 36 | uint32_t resolutionHeight; 37 | uint8_t fovLevel; 38 | float fov; 39 | float floorHeight; 40 | bool useParallelProjection; 41 | bool useSmartSmoothing; 42 | float fps; 43 | }; 44 | 45 | extern "C" __declspec(dllexport) void WINAPI getRuntimeStatus(RuntimeStatus* status) { 46 | pimax_openxr::log::Log("Hello\n"); 47 | 48 | pvrEnvHandle pvr; 49 | CHECK_PVRCMD(pvr_initialise(&pvr)); 50 | 51 | pvrSessionHandle pvrSession; 52 | CHECK_PVRCMD(pvr_createSession(pvr, &pvrSession)); 53 | 54 | // Ensure there is no stale parallel projection settings. 55 | CHECK_PVRCMD(pvr_setIntConfig(pvrSession, "view_rotation_fix", 0)); 56 | 57 | pvrDisplayInfo displayInfo{}; 58 | CHECK_PVRCMD(pvr_getEyeDisplayInfo(pvrSession, pvrEye_Left, &displayInfo)); 59 | 60 | pvrEyeRenderInfo eyeInfo[xr::StereoView::Count]; 61 | CHECK_PVRCMD(pvr_getEyeRenderInfo(pvrSession, pvrEye_Left, &eyeInfo[xr::StereoView::Left])); 62 | CHECK_PVRCMD(pvr_getEyeRenderInfo(pvrSession, pvrEye_Right, &eyeInfo[xr::StereoView::Right])); 63 | const auto cantingAngle = PVR::Quatf{eyeInfo[xr::StereoView::Left].HmdToEyePose.Orientation}.Angle( 64 | eyeInfo[xr::StereoView::Right].HmdToEyePose.Orientation) / 65 | 2.f; 66 | const auto fov = PVR::RadToDegree(atan(eyeInfo[xr::StereoView::Left].Fov.LeftTan) + 67 | atan(eyeInfo[xr::StereoView::Right].Fov.RightTan) + cantingAngle * 2.f); 68 | const auto useParallelProjection = 69 | cantingAngle > 0.0001f && 70 | RegGetDword(HKEY_LOCAL_MACHINE, "SOFTWARE\\PimaxXR", "force_parallel_projection_state") 71 | .value_or(!pvr_getIntConfig(pvrSession, "steamvr_use_native_fov", 0)); 72 | 73 | if (useParallelProjection) { 74 | // Per Pimax, we must set this value for parallel projection to work properly. 75 | CHECK_PVRCMD(pvr_setIntConfig(pvrSession, "view_rotation_fix", 1)); 76 | 77 | // Update eye info to account for parallel projection. 78 | CHECK_PVRCMD(pvr_getEyeRenderInfo(pvrSession, pvrEye_Left, &eyeInfo[xr::StereoView::Left])); 79 | CHECK_PVRCMD(pvr_getEyeRenderInfo(pvrSession, pvrEye_Right, &eyeInfo[xr::StereoView::Right])); 80 | } 81 | 82 | const pvrFovPort fovForResolution = eyeInfo[xr::StereoView::Left].Fov; 83 | 84 | pvrSizei viewportSize; 85 | CHECK_PVRCMD(pvr_getFovTextureSize(pvrSession, pvrEye_Left, fovForResolution, 1.f, &viewportSize)); 86 | 87 | status->refreshRate = displayInfo.refresh_rate; 88 | status->resolutionWidth = viewportSize.w; 89 | status->resolutionHeight = viewportSize.h; 90 | status->fovLevel = pvr_getIntConfig(pvrSession, "fov_level", 1); 91 | status->fov = fov; 92 | status->floorHeight = pvr_getFloatConfig(pvrSession, CONFIG_KEY_EYE_HEIGHT, 0.f); 93 | status->useParallelProjection = useParallelProjection; 94 | status->useSmartSmoothing = pvr_getIntConfig(pvrSession, "dbg_asw_enable", 0); 95 | status->fps = pvr_getFloatConfig(pvrSession, "client_fps", 0); 96 | 97 | status->valid = true; 98 | 99 | pvr_destroySession(pvrSession); 100 | pvr_shutdown(pvr); 101 | } 102 | -------------------------------------------------------------------------------- /pimax-openxr/display_refresh_rate.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 | #include "log.h" 26 | #include "runtime.h" 27 | #include "utils.h" 28 | 29 | // Implements the mock support for the XR_FB_display_refresh_rate extension: 30 | // https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_FB_display_refresh_rate 31 | 32 | namespace pimax_openxr { 33 | 34 | using namespace pimax_openxr::log; 35 | using namespace pimax_openxr::utils; 36 | 37 | XrResult OpenXrRuntime::xrEnumerateDisplayRefreshRatesFB(XrSession session, 38 | uint32_t displayRefreshRateCapacityInput, 39 | uint32_t* displayRefreshRateCountOutput, 40 | float* displayRefreshRates) { 41 | TraceLoggingWrite(g_traceProvider, 42 | "xrEnumerateDisplayRefreshRatesFB", 43 | TLXArg(session, "Session"), 44 | TLArg(displayRefreshRateCapacityInput, "displayRefreshRateCapacityInput")); 45 | 46 | if (!has_XR_FB_display_refresh_rate) { 47 | return XR_ERROR_FUNCTION_UNSUPPORTED; 48 | } 49 | 50 | if (!m_sessionCreated || session != (XrSession)1) { 51 | return XR_ERROR_HANDLE_INVALID; 52 | } 53 | 54 | if (displayRefreshRateCapacityInput && displayRefreshRateCapacityInput < 1) { 55 | return XR_ERROR_SIZE_INSUFFICIENT; 56 | } 57 | 58 | *displayRefreshRateCountOutput = 1; 59 | TraceLoggingWrite(g_traceProvider, 60 | "xrEnumerateDisplayRefreshRatesFB", 61 | TLArg(*displayRefreshRateCountOutput, "DisplayRefreshRateCountOutput")); 62 | 63 | if (displayRefreshRateCapacityInput && displayRefreshRates) { 64 | displayRefreshRates[0] = m_displayRefreshRate; 65 | TraceLoggingWrite(g_traceProvider, 66 | "xrEnumerateDisplayRefreshRatesFB", 67 | TLArg(displayRefreshRates[0], "DisplayRefreshRate")); 68 | } 69 | 70 | return XR_SUCCESS; 71 | } 72 | 73 | XrResult OpenXrRuntime::xrGetDisplayRefreshRateFB(XrSession session, float* displayRefreshRate) { 74 | TraceLoggingWrite(g_traceProvider, "xrGetDisplayRefreshRateFB", TLXArg(session, "Session")); 75 | 76 | if (!has_XR_FB_display_refresh_rate) { 77 | return XR_ERROR_FUNCTION_UNSUPPORTED; 78 | } 79 | 80 | if (!m_sessionCreated || session != (XrSession)1) { 81 | return XR_ERROR_HANDLE_INVALID; 82 | } 83 | 84 | *displayRefreshRate = m_displayRefreshRate; 85 | 86 | TraceLoggingWrite( 87 | g_traceProvider, "xrGetDisplayRefreshRateFB", TLArg(*displayRefreshRate, "DisplayRefreshRate")); 88 | 89 | return XR_SUCCESS; 90 | } 91 | 92 | XrResult OpenXrRuntime::xrRequestDisplayRefreshRateFB(XrSession session, float displayRefreshRate) { 93 | TraceLoggingWrite(g_traceProvider, 94 | "xrRequestDisplayRefreshRateFB", 95 | TLXArg(session, "Session"), 96 | TLArg(displayRefreshRate, "DisplayRefreshRate")); 97 | 98 | if (!has_XR_FB_display_refresh_rate) { 99 | return XR_ERROR_FUNCTION_UNSUPPORTED; 100 | } 101 | 102 | if (!m_sessionCreated || session != (XrSession)1) { 103 | return XR_ERROR_HANDLE_INVALID; 104 | } 105 | 106 | if (std::abs(displayRefreshRate - m_displayRefreshRate) > FLT_EPSILON) { 107 | return XR_ERROR_DISPLAY_REFRESH_RATE_UNSUPPORTED_FB; 108 | } 109 | 110 | return XR_SUCCESS; 111 | } 112 | 113 | } // namespace pimax_openxr 114 | -------------------------------------------------------------------------------- /pimax-openxr/eye_tracking.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 | #include "log.h" 26 | #include "runtime.h" 27 | #include "utils.h" 28 | 29 | // Implements the foundations of eye tracking needed for various extensions. 30 | 31 | namespace pimax_openxr { 32 | 33 | using namespace pimax_openxr::log; 34 | using namespace pimax_openxr::utils; 35 | using namespace xr::math; 36 | 37 | bool OpenXrRuntime::getEyeGaze(XrTime time, bool getStateOnly, XrVector3f& unitVector, double& sampleTime) const { 38 | if (!m_isEyeTrackingAvailable) { 39 | return false; 40 | } 41 | 42 | if (m_eyeTrackingType == EyeTracking::PVR) { 43 | pvrEyeTrackingInfo state{}; 44 | CHECK_PVRCMD(pvr_getEyeTrackingInfo(m_pvrSession, xrTimeToPvrTime(time), &state)); 45 | TraceLoggingWrite(g_traceProvider, 46 | "PVR_EyeTrackerPoseState", 47 | TLArg(xr::ToString(state.GazeTan[xr::StereoView::Left]).c_str(), "LeftGaze"), 48 | TLArg(xr::ToString(state.GazeTan[xr::StereoView::Right]).c_str(), "RightGaze"), 49 | TLArg(state.TimeInSeconds, "TimeInSeconds")); 50 | 51 | // According to Pimax, this is how we detect gaze not valid. 52 | if (state.TimeInSeconds == 0) { 53 | return false; 54 | } 55 | 56 | // Compute the gaze pitch/yaw angles by averaging both eyes. 57 | // TODO: Find the convergence point instead. 58 | const float angleHorizontal = 59 | atan((state.GazeTan[xr::StereoView::Left].x + state.GazeTan[xr::StereoView::Right].x) / 2.f); 60 | const float angleVertical = 61 | atan((state.GazeTan[xr::StereoView::Left].y + state.GazeTan[xr::StereoView::Right].y) / 2.f); 62 | 63 | // Use polar coordinates to create a unit vector. 64 | unitVector = { 65 | sin(angleHorizontal) * cos(angleVertical), 66 | -sin(angleVertical), 67 | -cos(angleHorizontal) * cos(angleVertical), 68 | }; 69 | 70 | sampleTime = state.TimeInSeconds; 71 | 72 | } else if (m_eyeTrackingType == EyeTracking::aSeeVR || m_eyeTrackingType == EyeTracking::Simulated) { 73 | XrVector2f point{0.5f, 0.5f}; 74 | #ifndef NOASEEVRCLIENT 75 | if (m_eyeTrackingType == EyeTracking::aSeeVR) { 76 | std::unique_lock lock(m_droolonMutex); 77 | 78 | TraceLoggingWrite(g_traceProvider, 79 | "aSeeVR_EyeTrackerState", 80 | TLArg(m_isDroolonReady, "Ready"), 81 | TLArg(xr::ToString(m_droolonGaze).c_str(), "Gaze"), 82 | TLArg(m_droolonTimestamp, "Timestamp")); 83 | 84 | if (!m_isDroolonReady) { 85 | return false; 86 | } 87 | 88 | point = m_droolonGaze; 89 | sampleTime = m_droolonTimestamp; 90 | } else 91 | #endif 92 | { 93 | // Use the mouse to simulate eye tracking. 94 | RECT rect; 95 | rect.left = 1; 96 | rect.right = 999; 97 | rect.top = 1; 98 | rect.bottom = 999; 99 | ClipCursor(&rect); 100 | 101 | POINT pt{}; 102 | GetCursorPos(&pt); 103 | 104 | point = {(float)pt.x / 1000.f, (1000.f - pt.y) / 1000.f}; 105 | sampleTime = pvr_getTimeSeconds(m_pvr); 106 | } 107 | 108 | // Experimentally determined that Z should be 0.35m in front for Droolon. 109 | unitVector = Normalize({point.x - 0.5f, 0.5f - point.y, -m_droolonProjectionDistance}); 110 | 111 | } else { 112 | return false; 113 | } 114 | 115 | return true; 116 | } 117 | 118 | #ifndef NOASEEVRCLIENT 119 | bool OpenXrRuntime::initializeDroolon() { 120 | m_isDroolonReady = false; 121 | aSeeVRInitParam param; 122 | param.ports[0] = getSetting("droolon_port").value_or(5347); 123 | { 124 | TraceLocalActivity(connect); 125 | TraceLoggingWriteStart(connect, "aSeeVRClient", TLArg("Connect", "Operation")); 126 | const aSeeVRReturnCode status = aSeeVR_connect_server(¶m); 127 | TraceLoggingWriteStop( 128 | connect, "aSeeVRClient", TLArg("Connect", "Operation"), TLArg(xr::ToString(status).c_str(), "Status")); 129 | if (status != aSeeVRReturnCode::success) { 130 | Log(fmt::format("Failed to connect to Droolon service: {}\n", xr::ToString(status).c_str()).c_str()); 131 | return false; 132 | } 133 | } 134 | 135 | aSeeVR_register_callback(aSeeVRCallbackType::coefficient, aSeeVRgetCoefficientCallback, this); 136 | aSeeVR_register_callback(aSeeVRCallbackType::state, aSeeVRstateCallback, this); 137 | aSeeVR_register_callback(aSeeVRCallbackType::eye_data, aSeeVReyeDataCallback, this); 138 | return true; 139 | } 140 | 141 | void OpenXrRuntime::startDroolonTracking() { 142 | TraceLoggingWrite(g_traceProvider, "aSeeVRClient", TLArg("GetCoefficient", "Operation")); 143 | const aSeeVRReturnCode status = aSeeVR_get_coefficient(); 144 | if (status != aSeeVRReturnCode::success) { 145 | TraceLoggingWrite(g_traceProvider, 146 | "aSeeVRClient", 147 | TLArg("GetCoefficient", "Operation"), 148 | TLArg(xr::ToString(status).c_str(), "Status")); 149 | } 150 | } 151 | 152 | void OpenXrRuntime::stopDroolonTracking() { 153 | TraceLoggingWrite(g_traceProvider, "aSeeVRClient", TLArg("Stop", "Operation")); 154 | const aSeeVRReturnCode status = aSeeVR_stop(); 155 | if (status != aSeeVRReturnCode::success) { 156 | TraceLoggingWrite(g_traceProvider, 157 | "aSeeVRClient", 158 | TLArg("Stop", "Operation"), 159 | TLArg(xr::ToString(status).c_str(), "Status")); 160 | } 161 | } 162 | 163 | void OpenXrRuntime::setDroolonCoefficients(const aSeeVRCoefficient& coefficients) { 164 | m_droolonCoefficients = coefficients; 165 | TraceLoggingWrite(g_traceProvider, "aSeeVRClient", TLArg("Start", "Operation")); 166 | const aSeeVRReturnCode status = aSeeVR_start(&m_droolonCoefficients); 167 | if (status != aSeeVRReturnCode::success) { 168 | TraceLoggingWrite(g_traceProvider, 169 | "aSeeVRClient", 170 | TLArg("Start", "Operation"), 171 | TLArg(xr::ToString(status).c_str(), "Status")); 172 | Log(fmt::format("Failed to start Droolon eye tracking: {}", xr::ToString(status).c_str()).c_str()); 173 | } 174 | } 175 | 176 | void OpenXrRuntime::setDroolonReady(bool ready) { 177 | std::unique_lock lock(m_droolonMutex); 178 | 179 | m_isDroolonReady = ready; 180 | } 181 | 182 | void OpenXrRuntime::setDroolonData(int64_t timestamp, const XrVector2f& gaze) { 183 | std::unique_lock lock(m_droolonMutex); 184 | 185 | // There is no direct translation between the timestamp from the eye tracking service and the rest of the 186 | // system. We capture the "time of arrival" as a best effort. 187 | m_droolonTimestamp = pvr_getTimeSeconds(m_pvr); 188 | m_droolonGaze = gaze; 189 | } 190 | 191 | void OpenXrRuntime::aSeeVRgetCoefficientCallback(const aSeeVRCoefficient* data, void* context) { 192 | TraceLocalActivity(local); 193 | TraceLoggingWriteStart(local, "aSeeVRgetCoefficientCallback", TLPArg(data), TLPArg(context)); 194 | 195 | OpenXrRuntime* const runtime = reinterpret_cast(context); 196 | 197 | TraceLoggingWrite(g_traceProvider, 198 | "aSeeVRClient", 199 | TLArg("GetCoefficient", "Operation"), 200 | TLArg(xr::ToString(aSeeVRReturnCode::success).c_str(), "Status")); 201 | runtime->setDroolonCoefficients(*data); 202 | 203 | TraceLoggingWriteStop(local, "aSeeVRgetCoefficientCallback"); 204 | } 205 | 206 | void OpenXrRuntime::aSeeVRstateCallback(const aSeeVRState* state, void* context) { 207 | TraceLocalActivity(local); 208 | TraceLoggingWriteStart(local, "aSeeVRstateCallback", TLPArg(state), TLPArg(context)); 209 | 210 | OpenXrRuntime* const runtime = reinterpret_cast(context); 211 | 212 | switch (state->code) { 213 | case aSeeVRStateCode::api_start: 214 | TraceLoggingWrite(g_traceProvider, 215 | "aSeeVRClient", 216 | TLArg("Start", "Operation"), 217 | TLArg(xr::ToString((aSeeVRReturnCode)state->error).c_str(), "Status")); 218 | if (state->error == aSeeVRReturnCode::success) { 219 | runtime->setDroolonReady(true); 220 | } else { 221 | Log(fmt::format("Failed to start Droolon eye tracking: {}", xr::ToString(state->error).c_str()) 222 | .c_str()); 223 | runtime->setDroolonReady(false); 224 | } 225 | break; 226 | 227 | case aSeeVRStateCode::api_stop: 228 | TraceLoggingWrite(g_traceProvider, 229 | "aSeeVRClient", 230 | TLArg("Stop", "Operation"), 231 | TLArg(xr::ToString((aSeeVRReturnCode)state->error).c_str(), "Status")); 232 | runtime->setDroolonReady(false); 233 | break; 234 | 235 | default: 236 | break; 237 | } 238 | 239 | TraceLoggingWriteStop(local, "aSeeVRstateCallback"); 240 | } 241 | 242 | void OpenXrRuntime::aSeeVReyeDataCallback(const aSeeVREyeData* eyeData, void* context) { 243 | TraceLocalActivity(local); 244 | TraceLoggingWriteStart(local, "aSeeVReyeDataCallback", TLPArg(eyeData), TLPArg(context)); 245 | 246 | OpenXrRuntime* const runtime = reinterpret_cast(context); 247 | 248 | if (!eye_data) { 249 | return; 250 | } 251 | 252 | int64_t timestamp = 0; 253 | aSeeVR_get_int64(eyeData, aSeeVREye::undefine_eye, aSeeVREyeDataItemType::timestamp, ×tamp); 254 | 255 | aSeeVRPoint2D point2D = {0}; 256 | aSeeVR_get_point2d(eyeData, aSeeVREye::undefine_eye, aSeeVREyeDataItemType::gaze, &point2D); 257 | 258 | runtime->setDroolonData(timestamp, XrVector2f{point2D.x, point2D.y}); 259 | 260 | TraceLoggingWriteStop(local, "aSeeVReyeDataCallback"); 261 | } 262 | #endif 263 | 264 | } // namespace pimax_openxr 265 | -------------------------------------------------------------------------------- /pimax-openxr/framework/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Pimax-OpenXR/2f54824af1ac05451d61c1ccf3a281e74e67df0c/pimax-openxr/framework/__init__.py -------------------------------------------------------------------------------- /pimax-openxr/framework/dispatch.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 | #include 26 | 27 | #include "dispatch.h" 28 | #include "log.h" 29 | 30 | #ifndef RUNTIME_NAMESPACE 31 | #error Must define RUNTIME_NAMESPACE 32 | #endif 33 | 34 | using namespace RUNTIME_NAMESPACE::log; 35 | 36 | namespace RUNTIME_NAMESPACE { 37 | 38 | // Handle cleanup of the layer's singleton. 39 | XrResult XRAPI_CALL xrDestroyInstance(XrInstance instance) { 40 | TraceLocalActivity(local); 41 | TraceLoggingWriteStart(local, "xrDestroyInstance"); 42 | 43 | XrResult result; 44 | try { 45 | result = RUNTIME_NAMESPACE::GetInstance()->xrDestroyInstance(instance); 46 | if (XR_SUCCEEDED(result)) { 47 | RUNTIME_NAMESPACE::ResetInstance(); 48 | } 49 | } catch (std::exception& exc) { 50 | TraceLoggingWriteTagged(local, "xrDestroyInstance_Error", TLArg(exc.what(), "Error")); 51 | ErrorLog("xrDestroyInstance: %s\n", exc.what()); 52 | result = XR_ERROR_RUNTIME_FAILURE; 53 | } 54 | 55 | TraceLoggingWriteStop(local, "xrDestroyInstance", TLArg(xr::ToCString(result), "Result")); 56 | if (XR_FAILED(result)) { 57 | ErrorLog("xrDestroyInstance failed with %s\n", xr::ToCString(result)); 58 | } 59 | 60 | return result; 61 | } 62 | 63 | // Forward the xrGetInstanceProcAddr() call to the dispatcher. 64 | XrResult XRAPI_CALL xrGetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) { 65 | TraceLocalActivity(local); 66 | TraceLoggingWriteStart(local, "xrGetInstanceProcAddr"); 67 | 68 | XrResult result; 69 | try { 70 | result = RUNTIME_NAMESPACE::GetInstance()->xrGetInstanceProcAddr(instance, name, function); 71 | } catch (std::exception& exc) { 72 | TraceLoggingWriteTagged(local, "xrGetInstanceProcAddr_Error", TLArg(exc.what(), "Error")); 73 | ErrorLog("xrGetInstanceProcAddr: %s\n", exc.what()); 74 | result = XR_ERROR_RUNTIME_FAILURE; 75 | } 76 | 77 | TraceLoggingWriteStop(local, "xrGetInstanceProcAddr", TLArg(xr::ToCString(result), "Result")); 78 | if (XR_FAILED(result) && result != XR_ERROR_FUNCTION_UNSUPPORTED) { 79 | ErrorLog("xrGetInstanceProcAddr failed with %s\n", xr::ToCString(result)); 80 | } 81 | 82 | return result; 83 | } 84 | 85 | } // namespace RUNTIME_NAMESPACE 86 | -------------------------------------------------------------------------------- /pimax-openxr/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 | #ifndef RUNTIME_NAMESPACE 26 | #error Must define RUNTIME_NAMESPACE 27 | #endif 28 | 29 | namespace RUNTIME_NAMESPACE { 30 | 31 | XrResult XRAPI_CALL xrDestroyInstance(XrInstance instance); 32 | XrResult XRAPI_CALL xrGetInstanceProcAddr(XrInstance instance, 33 | const char* name, 34 | PFN_xrVoidFunction* function); 35 | 36 | } // namespace RUNTIME_NAMESPACE 37 | -------------------------------------------------------------------------------- /pimax-openxr/framework/entry.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 | #include 26 | 27 | #include "dispatch.h" 28 | #include "log.h" 29 | 30 | #ifndef RUNTIME_NAMESPACE 31 | #error Must define RUNTIME_NAMESPACE 32 | #endif 33 | 34 | namespace RUNTIME_NAMESPACE { 35 | std::filesystem::path dllHome; 36 | 37 | // The path to store logs & others. 38 | std::filesystem::path localAppData; 39 | 40 | namespace log { 41 | // The file logger. 42 | std::ofstream logStream; 43 | } // namespace log 44 | } // namespace RUNTIME_NAMESPACE 45 | 46 | using namespace RUNTIME_NAMESPACE; 47 | using namespace RUNTIME_NAMESPACE::log; 48 | 49 | extern "C" { 50 | 51 | // Entry point for the loader. 52 | XrResult __declspec(dllexport) XRAPI_CALL xrNegotiateLoaderRuntimeInterface(const XrNegotiateLoaderInfo* loaderInfo, 53 | XrNegotiateRuntimeRequest* runtimeRequest) { 54 | // Retrieve the path of the DLL. 55 | if (dllHome.empty()) { 56 | HMODULE module; 57 | if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 58 | (LPCWSTR)&dllHome, 59 | &module)) { 60 | wchar_t path[_MAX_PATH]; 61 | GetModuleFileNameW(module, path, sizeof(path)); 62 | dllHome = std::filesystem::path(path).parent_path(); 63 | } else { 64 | // Falling back to loading config/writing logs to the current working directory. 65 | DebugLog("Failed to locate DLL\n"); 66 | } 67 | } 68 | 69 | localAppData = std::filesystem::path(getenv("LOCALAPPDATA")) / RuntimeName; 70 | CreateDirectoryA(localAppData.string().c_str(), nullptr); 71 | 72 | // Start logging to file. 73 | if (!logStream.is_open()) { 74 | std::string logFile = (localAppData / (RuntimeName + ".log")).string(); 75 | logStream.open(logFile, std::ios_base::ate); 76 | } 77 | 78 | Log("%s\n", RuntimePrettyName.c_str()); 79 | 80 | if (!loaderInfo || !runtimeRequest || loaderInfo->structType != XR_LOADER_INTERFACE_STRUCT_LOADER_INFO || 81 | loaderInfo->structVersion != XR_LOADER_INFO_STRUCT_VERSION || 82 | loaderInfo->structSize != sizeof(XrNegotiateLoaderInfo) || 83 | runtimeRequest->structType != XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST || 84 | runtimeRequest->structVersion != XR_RUNTIME_INFO_STRUCT_VERSION || 85 | runtimeRequest->structSize != sizeof(XrNegotiateRuntimeRequest) || 86 | loaderInfo->minInterfaceVersion > XR_CURRENT_LOADER_API_LAYER_VERSION || 87 | loaderInfo->maxInterfaceVersion < XR_CURRENT_LOADER_API_LAYER_VERSION || 88 | loaderInfo->maxInterfaceVersion > XR_CURRENT_LOADER_API_LAYER_VERSION || 89 | loaderInfo->maxApiVersion < XR_CURRENT_API_VERSION || loaderInfo->minApiVersion > XR_CURRENT_API_VERSION) { 90 | Log("xrNegotiateLoaderRuntimeInterface validation failed\n"); 91 | return XR_ERROR_INITIALIZATION_FAILED; 92 | } 93 | 94 | // This is it! Tell the loader to use our API implementation. 95 | runtimeRequest->getInstanceProcAddr = xrGetInstanceProcAddr; 96 | runtimeRequest->runtimeInterfaceVersion = XR_CURRENT_LOADER_API_LAYER_VERSION; 97 | runtimeRequest->runtimeApiVersion = XR_CURRENT_API_VERSION; 98 | 99 | return XR_SUCCESS; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /pimax-openxr/guardian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Pimax-OpenXR/2f54824af1ac05451d61c1ccf3a281e74e67df0c/pimax-openxr/guardian.png -------------------------------------------------------------------------------- /pimax-openxr/guardian.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Pimax-OpenXR/2f54824af1ac05451d61c1ccf3a281e74e67df0c/pimax-openxr/guardian.xcf -------------------------------------------------------------------------------- /pimax-openxr/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 | #include "runtime.h" 26 | 27 | namespace { 28 | constexpr uint32_t k_maxLoggedErrors = 100; 29 | uint32_t g_globalErrorCount = 0; 30 | } // namespace 31 | 32 | namespace pimax_openxr::log { 33 | extern std::ofstream logStream; 34 | 35 | // {cbf3adcd-42b1-4c38-830b-91980af201f6} 36 | TRACELOGGING_DEFINE_PROVIDER(g_traceProvider, 37 | "PimaxOpenXR", 38 | (0xcbf3adcd, 0x42b1, 0x4c38, 0x83, 0x0b, 0x91, 0x98, 0x0a, 0xf2, 0x01, 0xf6)); 39 | 40 | TraceLoggingActivity g_traceActivity; 41 | 42 | namespace { 43 | 44 | // Utility logging function. 45 | void InternalLog(const char* fmt, va_list va) { 46 | const std::time_t now = std::time(nullptr); 47 | 48 | char buf[1024]; 49 | size_t offset = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z: ", std::localtime(&now)); 50 | vsnprintf_s(buf + offset, sizeof(buf) - offset, _TRUNCATE, fmt, va); 51 | OutputDebugStringA(buf); 52 | if (logStream.is_open()) { 53 | logStream << buf; 54 | logStream.flush(); 55 | } 56 | } 57 | } // namespace 58 | 59 | void Log(const char* fmt, ...) { 60 | va_list va; 61 | va_start(va, fmt); 62 | InternalLog(fmt, va); 63 | va_end(va); 64 | } 65 | 66 | void ErrorLog(const char* fmt, ...) { 67 | if (g_globalErrorCount++ < k_maxLoggedErrors) { 68 | va_list va; 69 | va_start(va, fmt); 70 | InternalLog(fmt, va); 71 | va_end(va); 72 | if (g_globalErrorCount == k_maxLoggedErrors) { 73 | Log("Maximum number of errors logged. Going silent.\n"); 74 | } 75 | } 76 | } 77 | 78 | void DebugLog(const char* fmt, ...) { 79 | #ifdef _DEBUG 80 | va_list va; 81 | va_start(va, fmt); 82 | InternalLog(fmt, va); 83 | va_end(va); 84 | #endif 85 | } 86 | 87 | } // namespace pimax_openxr::log 88 | -------------------------------------------------------------------------------- /pimax-openxr/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 pimax_openxr::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 | #define TLPArray(var, count, ...) TraceLoggingCodePointerArray((void**)var, (UINT16)count, ##__VA_ARGS__) 45 | 46 | // General logging function. 47 | void Log(const char* fmt, ...); 48 | 49 | // Debug logging function. Can make things very slow (only enabled on Debug builds). 50 | void DebugLog(const char* fmt, ...); 51 | 52 | // Error logging function. Goes silent after too many errors. 53 | void ErrorLog(const char* fmt, ...); 54 | 55 | } // namespace pimax_openxr::log 56 | -------------------------------------------------------------------------------- /pimax-openxr/mirror_window.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 | #include "log.h" 26 | #include "runtime.h" 27 | #include "utils.h" 28 | 29 | namespace pimax_openxr { 30 | 31 | using namespace pimax_openxr::log; 32 | using namespace pimax_openxr::utils; 33 | 34 | LRESULT CALLBACK wndProcWrapper(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { 35 | return static_cast(GetInstance())->mirrorWindowProc(hwnd, msg, wParam, lParam); 36 | } 37 | 38 | void OpenXrRuntime::createMirrorWindow() { 39 | m_mirrorWindowReady = false; 40 | m_mirrorWindowThread = std::thread([&]() { 41 | // Create the window. 42 | WNDCLASSEX wndClassEx = {sizeof(wndClassEx)}; 43 | wndClassEx.lpfnWndProc = wndProcWrapper; 44 | wndClassEx.style = CS_HREDRAW | CS_VREDRAW; 45 | wndClassEx.lpszClassName = L"PimaxXRMirrorWindow"; 46 | CHECK_MSG(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | 47 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 48 | (LPCWSTR)&wndProcWrapper, 49 | &wndClassEx.hInstance), 50 | "Failed to get DLL handle"); 51 | // We may let this fail if the class was already registered by a previous start up. 52 | RegisterClassExW(&wndClassEx); 53 | 54 | const std::string title = "PimaxXR Mirror Window - " + m_applicationName; 55 | const auto defaultWidth = m_cachedEyeInfo[xr::StereoView::Left].DistortedViewport.Size.w / 2; 56 | const auto defaultHeight = m_cachedEyeInfo[xr::StereoView::Left].DistortedViewport.Size.h / 2; 57 | m_mirrorWindowHwnd = CreateWindowW(wndClassEx.lpszClassName, 58 | xr::utf8_to_wide(title).c_str(), 59 | WS_OVERLAPPEDWINDOW, 60 | CW_USEDEFAULT, 61 | CW_USEDEFAULT, 62 | defaultWidth, 63 | defaultHeight, 64 | nullptr, 65 | nullptr, 66 | nullptr, 67 | nullptr); 68 | CHECK_MSG(m_mirrorWindowHwnd, "Failed to CreateWindowW()"); 69 | m_mirrorWindowReady = true; 70 | 71 | // Show but don't steal focus. 72 | ShowWindow(m_mirrorWindowHwnd, SW_SHOWNOACTIVATE); 73 | UpdateWindow(m_mirrorWindowHwnd); 74 | 75 | // Service the window. 76 | MSG msg; 77 | while (GetMessage(&msg, m_mirrorWindowHwnd, 0, 0) > 0) { 78 | TranslateMessage(&msg); 79 | DispatchMessage(&msg); 80 | } 81 | 82 | // Free resources ASAP. 83 | { 84 | std::unique_lock lock(m_mirrorWindowMutex); 85 | m_mirrorWindowSwapchain.Reset(); 86 | m_mirrorTexture.Reset(); 87 | pvr_destroyMirrorTexture(m_pvrSession, m_pvrMirrorSwapChain); 88 | m_pvrMirrorSwapChain = nullptr; 89 | m_mirrorWindowHwnd = nullptr; 90 | } 91 | }); 92 | } 93 | 94 | void OpenXrRuntime::updateMirrorWindow(bool preferSRGB) { 95 | std::unique_lock lock(m_mirrorWindowMutex); 96 | 97 | if (!m_mirrorWindowReady || !IsWindowVisible(m_mirrorWindowHwnd)) { 98 | return; 99 | } 100 | 101 | RECT rect{}; 102 | GetClientRect(m_mirrorWindowHwnd, &rect); 103 | AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, false); 104 | const auto width = rect.right - rect.left; 105 | const auto height = rect.bottom - rect.top; 106 | 107 | // Check if visible. 108 | if (!width || !height) { 109 | return; 110 | } 111 | 112 | // Workaround: PVR does not seem to correctly write to non-SRGB, so for now we always prefer non-SRGB. 113 | preferSRGB = false; 114 | 115 | bool isSRGB = preferSRGB; 116 | D3D11_TEXTURE2D_DESC mirrorDesc; 117 | if (m_mirrorTexture) { 118 | m_mirrorTexture->GetDesc(&mirrorDesc); 119 | isSRGB = isSRGBFormat(mirrorDesc.Format); 120 | } 121 | 122 | // Create the DXGI swapchain for the window. 123 | if (!m_mirrorWindowSwapchain || preferSRGB != isSRGB) { 124 | ComPtr dxgiFactory; 125 | ComPtr dxgiDevice; 126 | CHECK_HRCMD(m_pvrSubmissionDevice->QueryInterface(IID_PPV_ARGS(dxgiDevice.ReleaseAndGetAddressOf()))); 127 | 128 | ComPtr dxgiAdapter; 129 | CHECK_HRCMD(dxgiDevice->GetAdapter(&dxgiAdapter)); 130 | CHECK_HRCMD(dxgiAdapter->GetParent(IID_PPV_ARGS(dxgiFactory.ReleaseAndGetAddressOf()))); 131 | 132 | DXGI_SWAP_CHAIN_DESC1 swapchainDesc{}; 133 | swapchainDesc.Width = width; 134 | swapchainDesc.Height = height; 135 | swapchainDesc.Format = preferSRGB ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM; 136 | swapchainDesc.SampleDesc.Count = 1; 137 | swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 138 | swapchainDesc.BufferCount = 2; 139 | swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; 140 | CHECK_HRCMD(dxgiFactory->CreateSwapChainForHwnd(m_pvrSubmissionDevice.Get(), 141 | m_mirrorWindowHwnd, 142 | &swapchainDesc, 143 | nullptr, 144 | nullptr, 145 | m_mirrorWindowSwapchain.ReleaseAndGetAddressOf())); 146 | } 147 | 148 | // Check for resizing or initial creation. 149 | if (!m_mirrorTexture || mirrorDesc.Width != width || mirrorDesc.Height != height || preferSRGB != isSRGB) { 150 | TraceLoggingWrite(g_traceProvider, "MirrorWindow", TLArg(width, "Width"), TLArg(height, "Height")); 151 | 152 | CHECK_HRCMD(m_mirrorWindowSwapchain->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0)); 153 | 154 | // Recreate a new PVR swapchain with the correct size. 155 | if (m_pvrMirrorSwapChain) { 156 | m_mirrorTexture.Reset(); 157 | pvr_destroyMirrorTexture(m_pvrSession, m_pvrMirrorSwapChain); 158 | } 159 | 160 | pvrMirrorTextureDesc mirrorDesc; 161 | mirrorDesc.Format = preferSRGB ? pvrTextureFormat::PVR_FORMAT_R8G8B8A8_UNORM_SRGB 162 | : pvrTextureFormat::PVR_FORMAT_R8G8B8A8_UNORM; 163 | mirrorDesc.Width = width; 164 | mirrorDesc.Height = height; 165 | mirrorDesc.SampleCount = 1; 166 | CHECK_PVRCMD(pvr_createMirrorTextureDX( 167 | m_pvrSession, m_pvrSubmissionDevice.Get(), &mirrorDesc, &m_pvrMirrorSwapChain)); 168 | CHECK_PVRCMD(pvr_getMirrorTextureBufferDX( 169 | m_pvrSession, m_pvrMirrorSwapChain, IID_PPV_ARGS(m_mirrorTexture.ReleaseAndGetAddressOf()))); 170 | } 171 | 172 | TraceLocalActivity(presentMirrorWindow); 173 | TraceLoggingWriteStart(presentMirrorWindow, "PresentMirrorWindow"); 174 | 175 | // Let those fail silently below so we do not crash the application. 176 | ComPtr frameBuffer; 177 | m_mirrorWindowSwapchain->GetBuffer(0, IID_PPV_ARGS(frameBuffer.ReleaseAndGetAddressOf())); 178 | m_pvrSubmissionContext->CopyResource(frameBuffer.Get(), m_mirrorTexture.Get()); 179 | m_mirrorWindowSwapchain->Present(0, 0); 180 | TraceLoggingWriteStop(presentMirrorWindow, "PresentMirrorWindow"); 181 | } 182 | 183 | LRESULT CALLBACK OpenXrRuntime::mirrorWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { 184 | switch (msg) { 185 | case WM_CLOSE: 186 | DestroyWindow(hwnd); 187 | return 0; 188 | 189 | case WM_DESTROY: 190 | PostQuitMessage(0); 191 | return 0; 192 | } 193 | 194 | return DefWindowProc(hwnd, msg, wParam, lParam); 195 | } 196 | 197 | } // namespace pimax_openxr 198 | -------------------------------------------------------------------------------- /pimax-openxr/overlay.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 | #include "log.h" 26 | #include "runtime.h" 27 | #include "utils.h" 28 | 29 | // Implements the in-VR overlay. 30 | 31 | namespace pimax_openxr { 32 | 33 | using namespace pimax_openxr::log; 34 | using namespace pimax_openxr::utils; 35 | 36 | // Create overlay resources. 37 | void OpenXrRuntime::initializeOverlayResources() { 38 | HRESULT hr; 39 | 40 | // Load the background texture. 41 | auto image = std::make_unique(); 42 | CoInitializeEx(nullptr, COINIT_MULTITHREADED); 43 | hr = DirectX::LoadFromWICFile((dllHome / L"overlay.png").c_str(), DirectX::WIC_FLAGS_NONE, nullptr, *image); 44 | 45 | if (SUCCEEDED(hr)) { 46 | hr = DirectX::CreateTexture(m_pvrSubmissionDevice.Get(), 47 | image->GetImages(), 48 | 1, 49 | image->GetMetadata(), 50 | m_overlayBackground.ReleaseAndGetAddressOf()); 51 | 52 | if (SUCCEEDED(hr)) { 53 | // Create a PVR swapchain for the overlay. 54 | pvrTextureSwapChainDesc desc{}; 55 | desc.Type = pvrTexture_2D; 56 | desc.ArraySize = 1; 57 | desc.Width = m_overlayExtent.width = (int)image->GetMetadata().width; 58 | desc.Height = m_overlayExtent.height = (int)image->GetMetadata().height; 59 | desc.MipLevels = 1; 60 | desc.SampleCount = 1; 61 | m_overlaySwapchainFormat = image->GetMetadata().format; 62 | desc.Format = dxgiToPvrTextureFormat(m_overlaySwapchainFormat); 63 | desc.BindFlags = pvrTextureBind_DX_RenderTarget; 64 | 65 | CHECK_PVRCMD(pvr_createTextureSwapChainDX( 66 | m_pvrSession, m_pvrSubmissionDevice.Get(), &desc, &m_overlaySwapchain)); 67 | } else { 68 | ErrorLog("Failed to create texture from overlay.png: %X\n"); 69 | } 70 | } else { 71 | ErrorLog("Failed to load overlay.png: %X\n"); 72 | } 73 | } 74 | 75 | void OpenXrRuntime::refreshOverlay() { 76 | const std::time_t now = std::time(nullptr); 77 | if (now - m_lastOverlayRefresh < 1) { 78 | return; 79 | } 80 | m_lastOverlayRefresh = now; 81 | 82 | // Acquire the next image. 83 | int imageIndex = -1; 84 | CHECK_PVRCMD(pvr_getTextureSwapChainCurrentIndex(m_pvrSession, m_overlaySwapchain, &imageIndex)); 85 | ComPtr swapchainTexture; 86 | CHECK_PVRCMD(pvr_getTextureSwapChainBufferDX( 87 | m_pvrSession, m_overlaySwapchain, imageIndex, IID_PPV_ARGS(swapchainTexture.ReleaseAndGetAddressOf()))); 88 | 89 | // We are about to do something destructive to the application context. Save the context. It will be 90 | // restored at the end of xrEndFrame(). 91 | if (m_d3d11Device == m_pvrSubmissionDevice && !m_d3d11ContextState) { 92 | m_pvrSubmissionContext->SwapDeviceContextState(m_pvrSubmissionContextState.Get(), 93 | m_d3d11ContextState.ReleaseAndGetAddressOf()); 94 | } 95 | 96 | // Copy the background. 97 | m_pvrSubmissionContext->CopyResource(swapchainTexture.Get(), m_overlayBackground.Get()); 98 | m_pvrSubmissionContext->Flush(); 99 | 100 | // Draw the text. 101 | const auto getBatteryLevel = [&](pvrTrackedDeviceType device) -> std::wstring { 102 | const int batteryPercent = 103 | pvr_getTrackedDeviceIntProperty(m_pvrSession, device, pvrTrackedDeviceProp_BatteryPercent_int, -1); 104 | if (batteryPercent >= 0) { 105 | return xr::utf8_to_wide(fmt::format("{}%", batteryPercent)) + (batteryPercent > 20 ? L"" : L" \x26A0"); 106 | } else { 107 | const int batteryLevel = 108 | pvr_getTrackedDeviceIntProperty(m_pvrSession, device, pvrTrackedDeviceProp_BatteryLevel_int, -1); 109 | if (batteryLevel != pvrTrackedDeviceBateryLevel_NotSupport) { 110 | switch (batteryLevel) { 111 | case pvrTrackedDeviceBateryLevel_Low: 112 | return L"Low \x26A0"; 113 | case pvrTrackedDeviceBateryLevel_Middle: 114 | return L"Medium"; 115 | case pvrTrackedDeviceBateryLevel_High: 116 | return L"High"; 117 | } 118 | } 119 | } 120 | return L"???"; 121 | }; 122 | 123 | m_pvrSubmissionContext->ClearState(); 124 | 125 | ComPtr rtv; 126 | { 127 | D3D11_RENDER_TARGET_VIEW_DESC desc{}; 128 | desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; 129 | desc.Format = m_overlaySwapchainFormat; 130 | CHECK_HRCMD(m_pvrSubmissionDevice->CreateRenderTargetView( 131 | swapchainTexture.Get(), &desc, rtv.ReleaseAndGetAddressOf())); 132 | } 133 | m_pvrSubmissionContext->OMSetRenderTargets(1, rtv.GetAddressOf(), nullptr); 134 | { 135 | D3D11_VIEWPORT viewport{}; 136 | viewport.Width = (float)m_overlayExtent.width; 137 | viewport.Height = (float)m_overlayExtent.height; 138 | viewport.MaxDepth = 1; 139 | m_pvrSubmissionContext->RSSetViewports(1, &viewport); 140 | } 141 | 142 | const uint32_t color = 0xffffffff; 143 | 144 | { 145 | char buf[8]{}; 146 | std::strftime(buf, sizeof(buf), "%H:%M", std::localtime(&now)); 147 | 148 | m_fontNormal->DrawString(m_pvrSubmissionContext.Get(), 149 | xr::utf8_to_wide(buf).c_str(), 150 | 200.f, 151 | 600.f, 152 | 12.f, 153 | color, 154 | FW1_LEFT | FW1_NOFLUSH); 155 | } 156 | 157 | m_fontNormal->DrawString(m_pvrSubmissionContext.Get(), 158 | getBatteryLevel(pvrTrackedDevice_HMD).c_str(), 159 | 150.f, 160 | 726.f, 161 | 744.f, 162 | color, 163 | FW1_CENTER | FW1_NOFLUSH); 164 | 165 | for (uint32_t side = 0; side < 2; side++) { 166 | m_fontNormal->DrawString( 167 | m_pvrSubmissionContext.Get(), 168 | m_isControllerActive[side] 169 | ? getBatteryLevel(side == 0 ? pvrTrackedDevice_LeftController : pvrTrackedDevice_RightController) 170 | .c_str() 171 | : L"-", 172 | 150.f, 173 | side == 0 ? 204.f : 1278.f, 174 | 744.f, 175 | color, 176 | FW1_CENTER | FW1_NOFLUSH); 177 | } 178 | 179 | const auto fps = m_frameTimes.size(); 180 | m_fontNormal->DrawString(m_pvrSubmissionContext.Get(), 181 | xr::utf8_to_wide(fmt::format("{}", fps)).c_str(), 182 | 150.f, 183 | 1400.f, 184 | 1098.f, 185 | color, 186 | FW1_RIGHT | FW1_NOFLUSH); 187 | 188 | m_fontNormal->DrawString(m_pvrSubmissionContext.Get(), 189 | m_isSmartSmoothingEnabled ? (m_isSmartSmoothingActive ? L"Active" : L"Standby") 190 | : L"Off", 191 | 150.f, 192 | 1400.f, 193 | 1402.f, 194 | color, 195 | FW1_RIGHT | FW1_NOFLUSH); 196 | 197 | m_fontNormal->DrawString( 198 | m_pvrSubmissionContext.Get(), 199 | xr::utf8_to_wide(fmt::format("{}x{}", m_proj0Extent.width, m_proj0Extent.height)).c_str(), 200 | 150.f, 201 | 1400.f, 202 | 1754.f, 203 | color, 204 | FW1_RIGHT | FW1_NOFLUSH); 205 | 206 | m_fontNormal->Flush(m_pvrSubmissionContext.Get()); 207 | 208 | CHECK_PVRCMD(pvr_commitTextureSwapChain(m_pvrSession, m_overlaySwapchain)); 209 | } 210 | 211 | } // namespace pimax_openxr 212 | -------------------------------------------------------------------------------- /pimax-openxr/overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Pimax-OpenXR/2f54824af1ac05451d61c1ccf3a281e74e67df0c/pimax-openxr/overlay.png -------------------------------------------------------------------------------- /pimax-openxr/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /pimax-openxr/pch.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 | -------------------------------------------------------------------------------- /pimax-openxr/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 | // Standard library. 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 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 | 47 | #pragma intrinsic(_ReturnAddress) 48 | 49 | using namespace std::chrono_literals; 50 | 51 | // Windows header files. 52 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 53 | #define NOMINMAX 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | 62 | using Microsoft::WRL::ComPtr; 63 | 64 | // Graphics APIs. 65 | #include 66 | #include 67 | #include 68 | #define VK_USE_PLATFORM_WIN32_KHR 69 | #include 70 | #include 71 | #include 72 | #include 73 | 74 | // Pimax SDK 75 | #include 76 | #include 77 | #include 78 | #include 79 | 80 | // OpenXR + Windows-specific definitions. 81 | #define XR_NO_PROTOTYPES 82 | #define XR_USE_PLATFORM_WIN32 83 | #define XR_USE_GRAPHICS_API_D3D11 84 | #define XR_USE_GRAPHICS_API_D3D12 85 | #define XR_USE_GRAPHICS_API_VULKAN 86 | #define XR_USE_GRAPHICS_API_OPENGL 87 | #include 88 | #include 89 | #include 90 | 91 | // OpenXR loader interfaces. 92 | #include 93 | 94 | // OpenXR utilities. 95 | #include 96 | #include 97 | #include 98 | #include 99 | #include 100 | #include 101 | 102 | // This header is not compatible with XR_NO_PROTOTYPES... We make a couple of symbols for now. 103 | #define xrStringToPath(...) XR_ERROR_RUNTIME_FAILURE 104 | #define xrPathToString(...) XR_ERROR_RUNTIME_FAILURE 105 | #include 106 | #undef xrStringToPath 107 | #undef xrPathToString 108 | 109 | // Detours 110 | #include 111 | 112 | // FMT formatter. 113 | #define FMT_HEADER_ONLY 114 | #include 115 | 116 | #ifndef NOASEEVRCLIENT 117 | // aSeeVRClient for Droolon eye tracking. 118 | #include 119 | #include 120 | #endif 121 | 122 | // DirectXTex 123 | #include 124 | 125 | // Text drawing. 126 | #include 127 | -------------------------------------------------------------------------------- /pimax-openxr/perf_counter.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 | #include "log.h" 26 | #include "runtime.h" 27 | #include "utils.h" 28 | 29 | // Implements the necessary support for the XR_KHR_win32_convert_performance_counter_time extension: 30 | // https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_KHR_win32_convert_performance_counter_time 31 | 32 | namespace pimax_openxr { 33 | 34 | using namespace pimax_openxr::log; 35 | using namespace pimax_openxr::utils; 36 | 37 | // https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#xrConvertWin32PerformanceCounterToTimeKHR 38 | XrResult OpenXrRuntime::xrConvertWin32PerformanceCounterToTimeKHR(XrInstance instance, 39 | const LARGE_INTEGER* performanceCounter, 40 | XrTime* time) { 41 | TraceLoggingWrite(g_traceProvider, 42 | "xrConvertWin32PerformanceCounterToTimeKHR", 43 | TLXArg(instance, "Instance"), 44 | TLArg(performanceCounter->QuadPart, "PerformanceCounter")); 45 | 46 | if (!has_XR_KHR_win32_convert_performance_counter_time) { 47 | return XR_ERROR_FUNCTION_UNSUPPORTED; 48 | } 49 | 50 | if (!m_instanceCreated || instance != (XrInstance)1) { 51 | return XR_ERROR_HANDLE_INVALID; 52 | } 53 | 54 | if (performanceCounter->QuadPart <= 0) { 55 | return XR_ERROR_TIME_INVALID; 56 | } 57 | 58 | double pvrTime = (double)performanceCounter->QuadPart / m_qpcFrequency.QuadPart; 59 | pvrTime += m_pvrTimeFromQpcTimeOffset; 60 | 61 | *time = pvrTimeToXrTime(pvrTime); 62 | 63 | TraceLoggingWrite(g_traceProvider, "xrConvertWin32PerformanceCounterToTimeKHR", TLArg(*time, "Time")); 64 | 65 | return XR_SUCCESS; 66 | } 67 | 68 | // https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#xrConvertTimeToWin32PerformanceCounterKHR 69 | XrResult OpenXrRuntime::xrConvertTimeToWin32PerformanceCounterKHR(XrInstance instance, 70 | XrTime time, 71 | LARGE_INTEGER* performanceCounter) { 72 | TraceLoggingWrite(g_traceProvider, 73 | "xrConvertTimeToWin32PerformanceCounterKHR", 74 | TLXArg(instance, "Instance"), 75 | TLArg(time, "Time")); 76 | 77 | if (!has_XR_KHR_win32_convert_performance_counter_time) { 78 | return XR_ERROR_FUNCTION_UNSUPPORTED; 79 | } 80 | 81 | if (!m_instanceCreated || instance != (XrInstance)1) { 82 | return XR_ERROR_HANDLE_INVALID; 83 | } 84 | 85 | if (time <= 0) { 86 | return XR_ERROR_TIME_INVALID; 87 | } 88 | 89 | double pvrTime = xrTimeToPvrTime(time); 90 | pvrTime -= m_pvrTimeFromQpcTimeOffset; 91 | 92 | performanceCounter->QuadPart = (LONGLONG)(pvrTime * m_qpcFrequency.QuadPart); 93 | 94 | TraceLoggingWrite(g_traceProvider, 95 | "xrConvertTimeToWin32PerformanceCounterKHR", 96 | TLArg(performanceCounter->QuadPart, "PerformanceCounter")); 97 | 98 | return XR_SUCCESS; 99 | } 100 | 101 | } // namespace pimax_openxr 102 | -------------------------------------------------------------------------------- /pimax-openxr/pimax-openxr-32.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_format_version": "1.0.0", 3 | "runtime": { 4 | "library_path": ".\\pimax-openxr-32.dll" 5 | } 6 | } -------------------------------------------------------------------------------- /pimax-openxr/pimax-openxr.def: -------------------------------------------------------------------------------- 1 | LIBRARY 2 | EXPORTS 3 | xrNegotiateLoaderRuntimeInterface 4 | getVersionString 5 | -------------------------------------------------------------------------------- /pimax-openxr/pimax-openxr.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_format_version": "1.0.0", 3 | "runtime": { 4 | "library_path": ".\\pimax-openxr.dll", 5 | "name": "PimaxXR" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /pimax-openxr/pimax-openxr.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 | {73ee9eb9-ee06-4f4a-8e05-87a550165032} 17 | 18 | 19 | 20 | 21 | Header Files 22 | 23 | 24 | Header Files 25 | 26 | 27 | Framework 28 | 29 | 30 | Framework 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | 49 | 50 | Source Files 51 | 52 | 53 | Source Files 54 | 55 | 56 | Framework 57 | 58 | 59 | Framework 60 | 61 | 62 | Framework 63 | 64 | 65 | Source Files 66 | 67 | 68 | Source Files 69 | 70 | 71 | Source Files 72 | 73 | 74 | Source Files 75 | 76 | 77 | Source Files 78 | 79 | 80 | Source Files 81 | 82 | 83 | Source Files 84 | 85 | 86 | Source Files 87 | 88 | 89 | Source Files 90 | 91 | 92 | Source Files 93 | 94 | 95 | Source Files 96 | 97 | 98 | Source Files 99 | 100 | 101 | Source Files 102 | 103 | 104 | Source Files 105 | 106 | 107 | Source Files 108 | 109 | 110 | Source Files 111 | 112 | 113 | Source Files 114 | 115 | 116 | Source Files 117 | 118 | 119 | Source Files 120 | 121 | 122 | Source Files 123 | 124 | 125 | Source Files 126 | 127 | 128 | 129 | 130 | 131 | Framework 132 | 133 | 134 | 135 | 136 | Shaders 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | Shaders 145 | 146 | 147 | Shaders 148 | 149 | 150 | Shaders 151 | 152 | 153 | Shaders 154 | 155 | 156 | -------------------------------------------------------------------------------- /pimax-openxr/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 | -------------------------------------------------------------------------------- /pimax-openxr/resource.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Pimax-OpenXR/2f54824af1ac05451d61c1ccf3a281e74e67df0c/pimax-openxr/resource.rc -------------------------------------------------------------------------------- /pimax-openxr/store.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 | #include "store.h" 26 | #include "log.h" 27 | 28 | namespace pimax_openxr::store { 29 | 30 | using namespace pimax_openxr::log; 31 | 32 | void storeAsyncInit() { 33 | const auto result = pvr_PlatformInit(10116220724823ull); 34 | if (result != pvrPlatformResult::pvrPlatformResult_Success) { 35 | TraceLoggingWrite(g_traceProvider, "PVR_Platform", TLArg((int)result, "Error")); 36 | // We just make this optional, this is only useful for users who downloaded PimaxXR from the Pimax Client. 37 | } else { 38 | TraceLoggingWrite(g_traceProvider, "PVR_Platform", TLArg("Login", "Action")); 39 | 40 | // Kick-off an entitlement check for compliance. 41 | pvr_CheckEntitlement(); 42 | } 43 | 44 | bool running = true; 45 | while (running) { 46 | pvrMessageHandle message; 47 | while (running && (message = pvr_PollMessage())) { 48 | const auto messageType = pvr_Message_GetType(message); 49 | TraceLoggingWrite(g_traceProvider, "PVR_Platform", TLXArg((void*)messageType, "Message")); 50 | 51 | // Trace errors for good measure. 52 | if (pvr_Message_IsError(message)) { 53 | TraceLoggingWrite(g_traceProvider, 54 | "PVR_Platform", 55 | TLArg(pvr_Message_GetErrorInfo(pvr_Message_GetError(message)), "Error")); 56 | } 57 | 58 | // Shutdown PVR platform on successful entitlement check or on any error. 59 | switch (messageType) { 60 | case pvrMessageType::pvrMessage_CheckEntitlement: 61 | if (!pvr_Message_IsError(message)) { 62 | TraceLoggingWrite(g_traceProvider, 63 | "PVR_Platform", 64 | TLArg((int)pvr_CheckEntitlement_GetResult(message), "Entitlement")); 65 | } 66 | running = false; 67 | break; 68 | 69 | case pvrMessage_Notify_RuntimeError: 70 | // The platform SDK does not seem to export this on 32-bit. It is misnamed "RunningError" instead. 71 | if (!pvr_Message_IsError(message)) { 72 | #ifdef _WIN64 73 | TraceLoggingWrite(g_traceProvider, 74 | "PVR_Platform", 75 | TLArg((int)pvr_RuntimeError_GetError(message), "RuntimeError")); 76 | #endif 77 | } 78 | running = false; 79 | break; 80 | 81 | case pvrMessage_Notify_Logout: 82 | if (!pvr_Message_IsError(message)) { 83 | TraceLoggingWrite(g_traceProvider, "PVR_Platform", TLArg("Logout", "Action")); 84 | } 85 | running = false; 86 | break; 87 | 88 | default: 89 | break; 90 | } 91 | } 92 | 93 | // Yield the rest of the time. 94 | std::this_thread::sleep_for(100ms); 95 | } 96 | 97 | pvr_PlatformShutdown(); 98 | } 99 | 100 | } // namespace pimax_openxr::store 101 | -------------------------------------------------------------------------------- /pimax-openxr/store.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 pimax_openxr::store { 28 | 29 | void storeAsyncInit(); 30 | 31 | } // namespace pimax_openxr::store 32 | -------------------------------------------------------------------------------- /pimax-openxr/version.h: -------------------------------------------------------------------------------- 1 | const unsigned int RuntimeVersionMajor = 0; 2 | const unsigned int RuntimeVersionMinor = 4; 3 | const unsigned int RuntimeVersionPatch = 5; 4 | -------------------------------------------------------------------------------- /pimax-openxr/visibility_mask.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 | #include "log.h" 26 | #include "runtime.h" 27 | #include "utils.h" 28 | 29 | // Implements the necessary support for the XR_KHR_visibility_mask extension: 30 | // https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_KHR_visibility_mask 31 | 32 | namespace pimax_openxr { 33 | 34 | using namespace pimax_openxr::log; 35 | using namespace pimax_openxr::utils; 36 | using namespace DirectX; 37 | 38 | // https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#xrGetVisibilityMaskKHR 39 | XrResult OpenXrRuntime::xrGetVisibilityMaskKHR(XrSession session, 40 | XrViewConfigurationType viewConfigurationType, 41 | uint32_t viewIndex, 42 | XrVisibilityMaskTypeKHR visibilityMaskType, 43 | XrVisibilityMaskKHR* visibilityMask) { 44 | if (visibilityMask->type != XR_TYPE_VISIBILITY_MASK_KHR) { 45 | return XR_ERROR_VALIDATION_FAILURE; 46 | } 47 | 48 | TraceLoggingWrite(g_traceProvider, 49 | "xrGetVisibilityMaskKHR", 50 | TLXArg(session, "Session"), 51 | TLArg(xr::ToCString(viewConfigurationType), "ViewConfigurationType"), 52 | TLArg(viewIndex, "ViewIndex"), 53 | TLArg(xr::ToCString(visibilityMaskType), "VisibilityMaskType"), 54 | TLArg(visibilityMask->vertexCapacityInput, "VertexCapacityInput"), 55 | TLArg(visibilityMask->indexCapacityInput, "IndexCapacityInput")); 56 | 57 | if (!has_XR_KHR_visibility_mask) { 58 | return XR_ERROR_FUNCTION_UNSUPPORTED; 59 | } 60 | 61 | if (!m_sessionCreated || session != (XrSession)1) { 62 | return XR_ERROR_HANDLE_INVALID; 63 | } 64 | 65 | if (viewConfigurationType != XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO) { 66 | return XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED; 67 | } 68 | 69 | if (viewIndex >= xr::StereoView::Count) { 70 | return XR_ERROR_VALIDATION_FAILURE; 71 | } 72 | 73 | // We only support the hidden area mesh and we don't return a mask with parallel projection. 74 | if (visibilityMaskType != XR_VISIBILITY_MASK_TYPE_HIDDEN_TRIANGLE_MESH_KHR || m_useParallelProjection) { 75 | visibilityMask->vertexCountOutput = 0; 76 | visibilityMask->indexCountOutput = 0; 77 | return XR_SUCCESS; 78 | } 79 | 80 | const auto verticesCount = 81 | pvr_getEyeHiddenAreaMesh(m_pvrSession, !viewIndex ? pvrEye_Left : pvrEye_Right, nullptr, 0); 82 | TraceLoggingWrite(g_traceProvider, "PVR_EyeHiddenAreaMesh", TLArg(verticesCount, "VerticesCount")); 83 | 84 | // The hidden area mesh is disabled by the platform. 85 | if (!verticesCount) { 86 | visibilityMask->vertexCountOutput = 0; 87 | visibilityMask->indexCountOutput = 0; 88 | return XR_SUCCESS; 89 | } 90 | 91 | if (visibilityMask->vertexCapacityInput == 0) { 92 | visibilityMask->vertexCountOutput = verticesCount; 93 | visibilityMask->indexCountOutput = verticesCount; 94 | } else if (visibilityMask->vertices && visibilityMask->indices) { 95 | if (visibilityMask->vertexCapacityInput < verticesCount || 96 | visibilityMask->indexCapacityInput < verticesCount) { 97 | return XR_ERROR_SIZE_INSUFFICIENT; 98 | } 99 | 100 | static_assert(sizeof(XrVector2f) == sizeof(pvrVector2f)); 101 | pvr_getEyeHiddenAreaMesh(m_pvrSession, 102 | !viewIndex ? pvrEye_Left : pvrEye_Right, 103 | (pvrVector2f*)visibilityMask->vertices, 104 | verticesCount); 105 | 106 | convertSteamVRToOpenXRHiddenMesh( 107 | m_cachedEyeInfo[viewIndex].Fov, visibilityMask->vertices, visibilityMask->indices, verticesCount); 108 | } 109 | 110 | return XR_SUCCESS; 111 | } 112 | 113 | void OpenXrRuntime::convertSteamVRToOpenXRHiddenMesh(const pvrFovPort& fov, 114 | XrVector2f* vertices, 115 | uint32_t* indices, 116 | uint32_t count) const { 117 | const float b = -fov.DownTan; 118 | const float t = fov.UpTan; 119 | const float l = -fov.LeftTan; 120 | const float r = fov.RightTan; 121 | 122 | // z = -1, n = 1 123 | // pndcx = (2n/(r-l) * pvx - (r+l)/(r-l)) / -z => pvx = (pndcx + (r+l)/(r-l))/(2n/(r-l)) 124 | // pndcy = (2n/(t-b) * pvy - (t+b)/(t-b)) / -z => pvy = (pndcy + (t+b)/(t-b))/(2n/(t-b)) 125 | const float hSpanRcp = 1.0f / (r - l); 126 | const float vSpanRcp = 1.0f / (t - b); 127 | 128 | // (r+l)/(r-l) 129 | const float rplOverHSpan = (r + l) * hSpanRcp; 130 | const float tpbOverVSpan = (t + b) * vSpanRcp; 131 | 132 | const float halfHSpan = (r - l) * 0.5f; 133 | const float halfVSpan = (t - b) * 0.5f; 134 | 135 | // constTerm = (r+l)/(r-l)/(2n(r-l)) 136 | const float hConstTerm = rplOverHSpan * halfHSpan; 137 | const float vConstTerm = tpbOverVSpan * halfVSpan; 138 | 139 | for (uint32_t i = 0; i < count; i++) { 140 | // Screen to NDC. 141 | XrVector2f ndc{(vertices[i].x - 0.5f) * 2.f, (vertices[i].y - 0.5f) * 2.f}; 142 | 143 | // Project the vertex. 144 | XMStoreFloat2(reinterpret_cast(&vertices[i]), 145 | XMVectorMultiplyAdd(XMVECTORF32{{{ndc.x, ndc.y, 0.f, 0.f}}}, 146 | XMVECTORF32{{{halfHSpan, halfVSpan, 0.f, 0.f}}}, 147 | XMVECTORF32{{{hConstTerm, vConstTerm, 0.f, 0.f}}})); 148 | 149 | // Record the indices. 150 | indices[i] = i; 151 | } 152 | } 153 | 154 | } // namespace pimax_openxr 155 | -------------------------------------------------------------------------------- /pimax_cli/pimax_cli.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 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | int main(int argc, char** argv) { 30 | int retval = 1; 31 | if (argc != 3 && argc != 4) { 32 | std::cerr << "usage: " << argv[0] << "<-int|-decimal|-string> []\n"; 33 | return 1; 34 | } 35 | 36 | pvrResult status; 37 | 38 | pvrEnvHandle handle = nullptr; 39 | pvrSessionHandle session = nullptr; 40 | 41 | status = pvr_initialise(&handle); 42 | if (status != pvr_success) { 43 | std::cerr << "pvr_initialise() failed with: " << status << "\n"; 44 | retval = 1; 45 | goto exit; 46 | } 47 | 48 | status = pvr_createSession(handle, &session); 49 | if (status != pvr_success) { 50 | std::cerr << "pvr_createSession() failed with: " << status << "\n"; 51 | retval = 1; 52 | goto exit; 53 | } 54 | 55 | { 56 | std::string type(argv[1]); 57 | std::string key(argv[2]); 58 | 59 | // Set the value if needed. 60 | if (argc == 4) { 61 | std::string value(argv[3]); 62 | if (type == "-int") { 63 | status = pvr_setIntConfig(session, key.c_str(), std::stoi(value)); 64 | } else if (type == "-decimal") { 65 | status = pvr_setFloatConfig(session, key.c_str(), std::stof(value)); 66 | } else if (type == "-string") { 67 | status = pvr_setStringConfig(session, key.c_str(), value.c_str()); 68 | } else { 69 | std::cerr << "unknown type: " << type << "\n"; 70 | retval = 1; 71 | goto exit; 72 | } 73 | 74 | if (status != pvr_success) { 75 | std::cerr << "pvr_setConfig() failed with: " << status << "\n"; 76 | retval = 1; 77 | goto exit; 78 | } 79 | } 80 | 81 | // Readback through the service. 82 | if (type == "-int") { 83 | std::cout << key << "=" << pvr_getIntConfig(session, key.c_str(), INT_MIN) << "\n"; 84 | } else if (type == "-decimal") { 85 | std::cout << key << "=" << pvr_getFloatConfig(session, key.c_str(), NAN) << "\n"; 86 | } else if (type == "-string") { 87 | char buf[256]{}; 88 | pvr_getStringConfig(session, key.c_str(), buf, sizeof(buf)); 89 | std::cout << key << "=" << buf << "\n"; 90 | } else { 91 | std::cerr << "unknown type: " << type << "\n"; 92 | retval = 1; 93 | goto exit; 94 | } 95 | } 96 | 97 | retval = 0; 98 | exit: 99 | if (session) { 100 | pvr_destroySession(session); 101 | } 102 | if (handle) { 103 | pvr_shutdown(handle); 104 | } 105 | 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /pimax_cli/pimax_cli.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {c3ef2fe7-770a-448e-a3ac-226276092abf} 25 | pimaxcli 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(SolutionDir)\bin\$(Platform)\$(Configuration)\ 76 | $(SolutionDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ 77 | 78 | 79 | false 80 | $(SolutionDir)\bin\$(Platform)\$(Configuration)\ 81 | $(SolutionDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ 82 | 83 | 84 | true 85 | $(SolutionDir)\bin\$(Platform)\$(Configuration)\ 86 | $(SolutionDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ 87 | 88 | 89 | false 90 | $(SolutionDir)\bin\$(Platform)\$(Configuration)\ 91 | $(SolutionDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ 92 | 93 | 94 | 95 | Level3 96 | true 97 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 98 | true 99 | $(SolutionDir)\external\PVR 100 | 101 | 102 | Console 103 | true 104 | 105 | 106 | if not exist $(SolutionDir)\version.info goto :skip_version 107 | for /f "delims== tokens=1,2" %%G in ($(SolutionDir)\version.info) do set %%G=%%H 108 | $(SolutionDir)\scripts\sed.exe -i "s/FILEVERSION .*$/FILEVERSION %major%,%minor%,%patch%,0/g" $(ProjectDir)\resource.rc 109 | $(SolutionDir)\scripts\sed.exe -i "s/PRODUCTVERSION .*$/PRODUCTVERSION %major%,%minor%,%patch%,0/g" $(ProjectDir)\resource.rc 110 | $(SolutionDir)\scripts\sed.exe -i "s/VALUE \"FileVersion\", \".*\"$/VALUE \"FileVersion\", \"%major%.%minor%.%patch%.0\"/g" $(ProjectDir)\resource.rc 111 | $(SolutionDir)\scripts\sed.exe -i "s/VALUE \"ProductVersion\", \".*\"$/VALUE \"ProductVersion\", \"%major%.%minor%.%patch%.0\"/g" $(ProjectDir)\resource.rc 112 | :skip_version 113 | 114 | Generating version info... 115 | 116 | 117 | 118 | 119 | Level3 120 | true 121 | true 122 | true 123 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | true 125 | $(SolutionDir)\external\PVR 126 | 127 | 128 | Console 129 | true 130 | true 131 | true 132 | 133 | 134 | if not exist $(SolutionDir)\version.info goto :skip_version 135 | for /f "delims== tokens=1,2" %%G in ($(SolutionDir)\version.info) do set %%G=%%H 136 | $(SolutionDir)\scripts\sed.exe -i "s/FILEVERSION .*$/FILEVERSION %major%,%minor%,%patch%,0/g" $(ProjectDir)\resource.rc 137 | $(SolutionDir)\scripts\sed.exe -i "s/PRODUCTVERSION .*$/PRODUCTVERSION %major%,%minor%,%patch%,0/g" $(ProjectDir)\resource.rc 138 | $(SolutionDir)\scripts\sed.exe -i "s/VALUE \"FileVersion\", \".*\"$/VALUE \"FileVersion\", \"%major%.%minor%.%patch%.0\"/g" $(ProjectDir)\resource.rc 139 | $(SolutionDir)\scripts\sed.exe -i "s/VALUE \"ProductVersion\", \".*\"$/VALUE \"ProductVersion\", \"%major%.%minor%.%patch%.0\"/g" $(ProjectDir)\resource.rc 140 | :skip_version 141 | 142 | Generating version info... 143 | 144 | 145 | 146 | 147 | Level3 148 | true 149 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 150 | true 151 | $(SolutionDir)\external\PVR 152 | 153 | 154 | Console 155 | true 156 | 157 | 158 | if not exist $(SolutionDir)\version.info goto :skip_version 159 | for /f "delims== tokens=1,2" %%G in ($(SolutionDir)\version.info) do set %%G=%%H 160 | $(SolutionDir)\scripts\sed.exe -i "s/FILEVERSION .*$/FILEVERSION %major%,%minor%,%patch%,0/g" $(ProjectDir)\resource.rc 161 | $(SolutionDir)\scripts\sed.exe -i "s/PRODUCTVERSION .*$/PRODUCTVERSION %major%,%minor%,%patch%,0/g" $(ProjectDir)\resource.rc 162 | $(SolutionDir)\scripts\sed.exe -i "s/VALUE \"FileVersion\", \".*\"$/VALUE \"FileVersion\", \"%major%.%minor%.%patch%.0\"/g" $(ProjectDir)\resource.rc 163 | $(SolutionDir)\scripts\sed.exe -i "s/VALUE \"ProductVersion\", \".*\"$/VALUE \"ProductVersion\", \"%major%.%minor%.%patch%.0\"/g" $(ProjectDir)\resource.rc 164 | :skip_version 165 | 166 | Generating version info... 167 | 168 | 169 | 170 | 171 | Level3 172 | true 173 | true 174 | true 175 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 176 | true 177 | $(SolutionDir)\external\PVR 178 | 179 | 180 | Console 181 | true 182 | true 183 | true 184 | 185 | 186 | if not exist $(SolutionDir)\version.info goto :skip_version 187 | for /f "delims== tokens=1,2" %%G in ($(SolutionDir)\version.info) do set %%G=%%H 188 | $(SolutionDir)\scripts\sed.exe -i "s/FILEVERSION .*$/FILEVERSION %major%,%minor%,%patch%,0/g" $(ProjectDir)\resource.rc 189 | $(SolutionDir)\scripts\sed.exe -i "s/PRODUCTVERSION .*$/PRODUCTVERSION %major%,%minor%,%patch%,0/g" $(ProjectDir)\resource.rc 190 | $(SolutionDir)\scripts\sed.exe -i "s/VALUE \"FileVersion\", \".*\"$/VALUE \"FileVersion\", \"%major%.%minor%.%patch%.0\"/g" $(ProjectDir)\resource.rc 191 | $(SolutionDir)\scripts\sed.exe -i "s/VALUE \"ProductVersion\", \".*\"$/VALUE \"ProductVersion\", \"%major%.%minor%.%patch%.0\"/g" $(ProjectDir)\resource.rc 192 | :skip_version 193 | 194 | Generating version info... 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /pimax_cli/pimax_cli.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 | 14 | 15 | Source Files 16 | 17 | 18 | 19 | 20 | Header Files 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /pimax_cli/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 | -------------------------------------------------------------------------------- /pimax_cli/resource.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Pimax-OpenXR/2f54824af1ac05451d61c1ccf3a281e74e67df0c/pimax_cli/resource.rc -------------------------------------------------------------------------------- /pvr-logger/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /pvr-logger/pch.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022 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 | -------------------------------------------------------------------------------- /pvr-logger/pch.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022 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 | // Standard library. 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | // Windows header files. 32 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | // Graphics APIs. 39 | #include 40 | 41 | // Pimax SDK 42 | #include 43 | #include 44 | #include 45 | 46 | // FMT formatter. 47 | #include 48 | -------------------------------------------------------------------------------- /pvr-logger/pvr-logger.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 16.0 15 | Win32Proj 16 | {12d0a36d-3f0a-4571-b0b8-8ddf5dbce5a0} 17 | pvrlogger 18 | 10.0 19 | 20 | 21 | 22 | DynamicLibrary 23 | true 24 | v142 25 | Unicode 26 | 27 | 28 | DynamicLibrary 29 | false 30 | v142 31 | true 32 | Unicode 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | true 48 | $(SolutionDir)\bin\$(Platform)\$(Configuration)\ 49 | $(SolutionDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ 50 | libPVRClient64 51 | 52 | 53 | false 54 | $(SolutionDir)\bin\$(Platform)\$(Configuration)\ 55 | $(SolutionDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ 56 | libPVRClient64 57 | 58 | 59 | 60 | Level3 61 | true 62 | _DEBUG;PVRLOGGER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 63 | true 64 | Use 65 | pch.h 66 | $(ProjectDir);$(SolutionDir)\external\PVR 67 | stdcpp17 68 | 69 | 70 | Windows 71 | true 72 | false 73 | 74 | 75 | 76 | 77 | Level3 78 | true 79 | true 80 | true 81 | NDEBUG;PVRLOGGER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 82 | true 83 | Use 84 | pch.h 85 | $(ProjectDir);$(SolutionDir)\external\PVR 86 | stdcpp17 87 | 88 | 89 | Windows 90 | true 91 | true 92 | true 93 | false 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | Create 103 | Create 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /pvr-logger/pvr-logger.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 | 14 | 15 | Header Files 16 | 17 | 18 | 19 | 20 | Source Files 21 | 22 | 23 | Source Files 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /scripts/Build-CTS.ps1: -------------------------------------------------------------------------------- 1 | $BuildType = "RelWithDebInfo" 2 | 3 | cmake -G "Visual Studio 16 2019" -A "x64" -B $PSScriptRoot\..\bin\x64\CTS -S $PSScriptRoot\..\external\OpenXR-CTS ` 4 | -DDYNAMIC_LOADER=ON ` 5 | -DBUILD_LOADER=ON ` 6 | -DBUILD_API_LAYERS=OFF ` 7 | -DBUILD_TESTS=OFF ` 8 | -DBUILD_CONFORMANCE_TESTS=ON 9 | if (-Not $?) 10 | { 11 | throw "CMake generate failed: $LastExitCode" 12 | } 13 | 14 | cmake --build $PSScriptRoot\..\bin\x64\CTS --config $BuildType --parallel 8 15 | if (-Not $?) 16 | { 17 | throw "CMake build failed: $LastExitCode" 18 | } 19 | 20 | # Cleanup past prebuilt. 21 | If (Test-Path $PSScriptRoot\..\bin\x64\CTS\output) 22 | { 23 | Remove-Item $PSScriptRoot\..\bin\x64\CTS\output -Recurse -Force 24 | } 25 | 26 | New-Item $PSScriptRoot\..\bin\x64\CTS\output -ItemType Directory | Out-Null 27 | Copy-Item $PSScriptRoot\..\bin\x64\CTS\src\conformance\conformance_cli\$BuildType\conformance_cli.exe $PSScriptRoot\..\bin\x64\CTS\output -Force 28 | Copy-Item $PSScriptRoot\..\bin\x64\CTS\src\conformance\conformance_cli\$BuildType\conformance_cli.pdb $PSScriptRoot\..\bin\x64\CTS\output -Force 29 | Copy-Item $PSScriptRoot\..\bin\x64\CTS\src\conformance\conformance_test\$BuildType\conformance_test.dll $PSScriptRoot\..\bin\x64\CTS\output -Force 30 | Copy-Item $PSScriptRoot\..\bin\x64\CTS\src\conformance\conformance_test\$BuildType\conformance_test.pdb $PSScriptRoot\..\bin\x64\CTS\output -Force 31 | Copy-Item $PSScriptRoot\..\bin\x64\CTS\src\conformance\conformance_cli\*.png $PSScriptRoot\..\bin\x64\CTS\output -Force 32 | Copy-Item $PSScriptRoot\..\bin\x64\CTS\src\conformance\conformance_cli\*.otf $PSScriptRoot\..\bin\x64\CTS\output -Force 33 | Copy-Item $PSScriptRoot\..\bin\x64\CTS\src\conformance\conformance_test\$BuildType\openxr_loader.dll $PSScriptRoot\..\bin\x64\CTS\output -Force 34 | Copy-Item $PSScriptRoot\..\bin\x64\CTS\src\conformance\conformance_layer\$BuildType\XrApiLayer_runtime_conformance.dll $PSScriptRoot\..\bin\x64\CTS\output -Force 35 | Copy-Item $PSScriptRoot\..\bin\x64\CTS\src\conformance\conformance_layer\$BuildType\XrApiLayer_runtime_conformance.pdb $PSScriptRoot\..\bin\x64\CTS\output -Force 36 | Copy-Item $PSScriptRoot\..\bin\x64\CTS\src\conformance\conformance_layer\XrApiLayer_runtime_conformance.json $PSScriptRoot\..\bin\x64\CTS\output -Force 37 | 38 | Write-Host "All output in: $PSScriptRoot\..\bin\x64\CTS\output" 39 | -------------------------------------------------------------------------------- /scripts/Install-Runtime.ps1: -------------------------------------------------------------------------------- 1 | if (Test-Path "pimax-openxr.json") { 2 | $RegistryPath = "HKLM:\Software\Khronos\OpenXR\1" 3 | $JsonPath = Join-Path "$PSScriptRoot" "pimax-openxr.json" 4 | } elseif (Test-Path "pimax-openxr-32.json") { 5 | $RegistryPath = "HKLM:\Software\WOW6432Node\Khronos\OpenXR\1" 6 | $JsonPath = Join-Path "$PSScriptRoot" "pimax-openxr-32.json" 7 | } else { 8 | Exit 9 | } 10 | Start-Process -FilePath powershell.exe -Verb RunAs -Wait -ArgumentList @" 11 | & { 12 | If (-not (Test-Path $RegistryPath)) { 13 | New-Item -Path $RegistryPath -Force | Out-Null 14 | } 15 | New-ItemProperty -Path $RegistryPath -Name ActiveRuntime -PropertyType String -Value '$jsonPath' -Force | Out-Null 16 | } 17 | "@ 18 | -------------------------------------------------------------------------------- /scripts/PimaxOpenXR.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 | 56 | 57 | -------------------------------------------------------------------------------- /scripts/Run-Perf.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [string]$ProjectDir = "$PSScriptRoot/..", 3 | [string[]]$Revisions=@("HEAD"), 4 | [string]$DevEnv = "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\devenv.com", 5 | [string]$PerfTestDir = "D:\XR\XrPerfTest\Output" 6 | ) 7 | 8 | $Solution = Resolve-Path "${ProjectDir}\Pimax-OpenXR.sln" 9 | $RuntimeJson = Resolve-Path "${ProjectDir}\bin\x64\Release\Pimax-openxr.json" 10 | $PerfTest = "${PerfTestDir}\XrPerfTest.exe" 11 | 12 | $ErrorActionPreference = "Stop" 13 | function ThrowOnNativeFailure { 14 | if ($LastExitCode -ne 0) { 15 | throw 'Error executing command' 16 | } 17 | } 18 | 19 | if (-not $(Test-Path -Path $PerfTest -PathType leaf)) { 20 | Write-Host -ForegroundColor Red "Could not find ${PerfTest}. Please check that the project is built." 21 | exit 1 22 | } 23 | 24 | foreach ($revision in $Revisions) { 25 | git checkout $ProjectDir 26 | git checkout $revision 27 | ThrowOnNativeFailure 28 | git submodule update 29 | ThrowOnNativeFailure 30 | 31 | git rev-parse 32 | 33 | & $DevEnv $Solution /Rebuild "Release|x64" /Project "pimax-openxr" 34 | ThrowOnNativeFailure 35 | 36 | Remove-Item "${PerfTestDir}\perf.csv" -ErrorAction SilentlyContinue 37 | Remove-Item "${PerfTestDir}\ScreenCaptureAtEnd.png" -ErrorAction SilentlyContinue 38 | 39 | $env:XR_RUNTIME_JSON=$RuntimeJson 40 | Push-Location $PerfTestDir 41 | & $PerfTest 42 | ThrowOnNativeFailure 43 | Pop-Location 44 | 45 | while (-not $(Test-Path -Path "${PerfTestDir}\ScreenCaptureAtEnd.png" -PathType leaf)) { 46 | Sleep 1 47 | } 48 | 49 | Copy-Item "${PerfTestDir}\perf.csv" "perf-${revision}.csv" 50 | Copy-Item "${PerfTestDir}\ScreenCaptureAtEnd.png" "ScreenCaptureAtEnd-${revision}.png" 51 | } 52 | -------------------------------------------------------------------------------- /scripts/msys-2.0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Pimax-OpenXR/2f54824af1ac05451d61c1ccf3a281e74e67df0c/scripts/msys-2.0.dll -------------------------------------------------------------------------------- /scripts/msys-iconv-2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Pimax-OpenXR/2f54824af1ac05451d61c1ccf3a281e74e67df0c/scripts/msys-iconv-2.dll -------------------------------------------------------------------------------- /scripts/msys-intl-8.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Pimax-OpenXR/2f54824af1ac05451d61c1ccf3a281e74e67df0c/scripts/msys-intl-8.dll -------------------------------------------------------------------------------- /scripts/sed.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbucchia/Pimax-OpenXR/2f54824af1ac05451d61c1ccf3a281e74e67df0c/scripts/sed.exe -------------------------------------------------------------------------------- /version.info: -------------------------------------------------------------------------------- 1 | major=0 2 | minor=4 3 | patch=5 4 | --------------------------------------------------------------------------------