1
1

Compare commits

...

200 Commits

Author SHA1 Message Date
Julian Eisel
7ec3023926 Rename internal VAMR files (mainly removes VAMR_ prefix)
Also rename VAMR_internal.h to utils.h and move OpenXR include out of
it.
2019-08-31 14:39:56 +02:00
Julian Eisel
37fc5bcd8c Use VAMR namespace for internal types 2019-08-31 14:20:57 +02:00
Julian Eisel
88ef625c0b Remove unnecessary WITH_OPENXR definition in BKE 2019-08-31 13:10:52 +02:00
Julian Eisel
34430ac5f5 Remove Ghost-XR code
Also fixes remaining call to it.
2019-08-31 13:10:37 +02:00
Julian Eisel
00842f9efd Call VAMR functions in window-manager, not GHOST_Xr
This gives linking errors currently because of conflicting symbols, but
that is to be expected and will be fixed in a followup.
2019-08-31 13:00:13 +02:00
Julian Eisel
04eada72fa Add missing C-API source file
Also add missing license header.
2019-08-31 12:59:18 +02:00
Julian Eisel
17dda0b3e6 Rename types and functions for VAMR 2019-08-30 17:55:44 +02:00
Julian Eisel
b0a333e755 Move Ghost-XR files into new VAMR-module and rename them 2019-08-30 16:44:02 +02:00
Julian Eisel
bc8186b5d1 Merge branch 'master' into soc-2019-openxr 2019-08-30 14:22:04 +02:00
Julian Eisel
e0bb3f9286 Merge branch 'master' into soc-2019-openxr 2019-08-24 23:21:33 +02:00
Julian Eisel
d0b4ec00f6 Don't drwa relationship lines in the VR view 2019-08-24 22:06:23 +02:00
Julian Eisel
7b6514c222 Merge branch 'master' into soc-2019-openxr 2019-08-24 14:45:41 +02:00
Julian Eisel
c506acf2fc Add Oculus to the list of known runtimes
With this, all currently available runtimes are in the runtime map. So
removing the TODO comment.
2019-08-24 03:35:32 +02:00
Julian Eisel
31b8350b01 Cleanup: Refactor (hacky) swapchain image submission
Code now assumes that the view-draw callback left the OpenGL state for
ongoing use by Ghost-XR. So the state doesn't have to be set by the
swapchain image submission and thus it we don't need to pass the Ghost
OpenGL context to it in a hacky way.
Also, rename drawViewEnd (to submitToSwapchain) and remove
drawViewBegin.
2019-08-24 03:32:11 +02:00
Julian Eisel
d7a216704b Fix warning in release builds 2019-08-24 03:17:14 +02:00
Julian Eisel
4b67477732 Own FBO for VR viewport to get previous changes to work 2019-08-24 03:15:25 +02:00
Julian Eisel
4a039158e5 Avoid OpenGL context deactivation, just to reactivate it immediately 2019-08-24 00:13:36 +02:00
Julian Eisel
61014e1dd9 Remove hack to resize the usable default framebuffer region 2019-08-23 23:46:09 +02:00
Julian Eisel
3441314e40 Remove blitting from default framebuffer 2019-08-23 23:36:04 +02:00
Julian Eisel
f175dcc35f Add Monado to the list of known runtimes 2019-08-23 23:08:21 +02:00
Julian Eisel
756b676076 Merge branch 'master' into soc-2019-openxr 2019-08-23 17:51:28 +02:00
Julian Eisel
7462fca737 Avoid redundant framebuffer resize and offscreen texture drawing 2019-08-23 17:46:43 +02:00
Julian Eisel
7c9e18c193 Cleanup: Add comments 2019-08-23 17:00:30 +02:00
Julian Eisel
6b69b5349b Address and remove some TODOs marked in code 2019-08-22 23:24:05 +02:00
Julian Eisel
daba8140c2 Cleanup: Unused functions, add comments, sync to master 2019-08-22 23:02:01 +02:00
Julian Eisel
599d0611b0 Improve batch file text for starting Blender with Oculus support 2019-08-22 13:33:06 +02:00
Julian Eisel
e88970db82 Don't create a DirectX swap-chain
We only render offscreen anyway. There's no point in creating a
swap-chain. This simplifies things quite a bit.
2019-08-21 20:58:59 +02:00
Julian Eisel
20b0b36479 DirectX: Create an own render-target, don't use swapchain for blitting 2019-08-21 20:40:13 +02:00
Julian Eisel
2c77f2deac Remove GHOST API functions for OpenGL offscreen blitting
These were previously used to blit from a offscreen (non-OpenGL)
context, into an onscreen one. We do this differently, and only within
GHOST_XrGraphicsBinding now, so this can be removed.
2019-08-21 20:36:40 +02:00
Julian Eisel
fec7ac7c51 Refactor OpenGL/DirectX resource sharing for multiple shared resources
Rather than max. one shared resource per DirectX context, code can now
request a handle for a shared resource and ask the DirectX context to do
operations on that.
2019-08-21 19:44:08 +02:00
Julian Eisel
2a1ec8ec2a Fix missing CMake hint for OpenXR SDK path on linux 2019-08-21 13:24:16 +02:00
1548682cde Windows/deps: Add/fix openxr_sdk dependency
There were a few typos here and there, and the openxr_sdk does not respect the cflags we give it hence a patch was added to work around this undesirable behaviour.
2019-08-20 08:30:29 -06:00
Julian Eisel
588dea6751 Merge branch 'master' into soc-2019-openxr 2019-08-20 12:36:51 +02:00
Julian Eisel
ad4f9a4d98 Fix compiling with WITH_OPENXR disabled 2019-08-20 09:17:56 +02:00
Julian Eisel
84466c7d71 Blacklist VR add-on in Python tests if WITH_OPENXR is disabled 2019-08-20 00:55:21 +02:00
Julian Eisel
de037b031d Expose WITH_OPENXR build option to Python API 2019-08-20 00:12:59 +02:00
Julian Eisel
a21ae28f96 Prepare Toggle VR Session operator to be enabled by an add-on 2019-08-19 23:44:59 +02:00
Julian Eisel
b2424bd781 Windows: Print warning and disable WITH_OPENXR if SDK is not found 2019-08-19 15:41:27 +02:00
Julian Eisel
d0177fc541 Integrate the OpenXR SDK better into dependency management
Adds the SDK so that `make deps` works, on all platforms. On Windows,
the SDK headers and libraries are now assumed to be in the usual lib\
directory.
Also fixes a mistake in install_deps.sh

Note that none of this is tested as it requires an older Visual Studio
version than I have.
2019-08-18 13:06:48 +02:00
Julian Eisel
4cf8740790 Fix compile error on Linux 2019-08-17 23:59:39 +02:00
Julian Eisel
a53b90329a Merge branch 'master' into soc-2019-openxr 2019-08-17 23:58:09 +02:00
Julian Eisel
2c41ffa380 Fix compiling with external OpenXR SDK on Windows 2019-08-15 21:49:10 +02:00
Julian Eisel
cdc2768f65 Cleanup: Don't cast away const in GHOST C-API 2019-08-15 15:52:45 +02:00
Julian Eisel
eb477c67a1 Fix crash when no OpenXR runtime is found (or setup failed) 2019-08-15 15:50:01 +02:00
Julian Eisel
0dd3f3925b Remove bundled OpenXR-SDK sources from extern/
Removes the OPENXR_USE_BUNDLED_SRC option which allowed using the
OpenXR-SDK sources bundled in extern/. It would be too much hassle to
keept these updated.
Now these have to be provided on the system Blender is compiled on. I've
already added necessary bits to install_deps.sh and our Windows
maintainer is ready to provide builds with OpenXR-SDK linked.
Once this gets into master, platform maintainers will have to be
notified about the added dependency.

This also removes the JsonCpp dependency, it was only needed for the
OpenXR-SDK sources.
2019-08-15 15:42:23 +02:00
Julian Eisel
9e7c48575a Linux: Add OpenXR-SDK to install_deps.sh 2019-08-14 22:59:33 +02:00
Julian Eisel
8138099fa9 Silence warnings in OpenXR SDK API layer files 2019-08-14 17:44:02 +02:00
Julian Eisel
02de1b9860 Quiet warning 2019-08-14 16:11:14 +02:00
Julian Eisel
57712625f2 Merge branch 'master' into soc-2019-openxr 2019-08-14 16:01:30 +02:00
Julian Eisel
96a34d7f50 Cleanup: Unused includes, better structuring for wm_xr.c 2019-08-09 16:35:28 +02:00
Julian Eisel
873223f38d Cleanup: Remove unused support for non-OpenGL Windows
This was very useful for testing, but there's no current need for it.
Neither for VR code, nor for anything else in master.
2019-08-09 16:08:34 +02:00
Julian Eisel
716c6b0c01 Cleanup: Move secondary GHOST-context handle to XR surface data
This is used to refer to the DirectX context. For other (currently
non-existant) surfaces, this may not make much sense.
2019-08-09 13:29:08 +02:00
Julian Eisel
62bbd2e37d Don't swap buffers of offscreen surface OpenGL contexts
This does frame syncing to the regular screen, so hurts performance in
some cases.
2019-08-08 22:00:21 +02:00
Julian Eisel
ba223dfac8 Exit OpenXR context/session gracefully if the runtime reports loss pending
Handle XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING and
XR_SESSION_STATE_LOSS_PENDING according to the specification.
2019-08-07 23:58:28 +02:00
Julian Eisel
8557997e81 Disable specular highlights in the VR viewport 2019-08-07 23:05:24 +02:00
Julian Eisel
bcc8f7c6be Fix xrDestroy calls to data already implicitly destroyed by OpenXR
Would effectifely result in null-ops, since the loader does good sanity
checks. The failing functions would still lead to error prints though.
2019-08-07 22:17:55 +02:00
Julian Eisel
fe7b01a08f Correctly request session end according to OpenXR 1.0 specification 2019-08-07 22:13:35 +02:00
Julian Eisel
5350015d51 Fix dark VR rendering on Windows Mixed Reality by appyling SRGB OETF
Discussed this in length with @sobotka, and it seems WMR has an utterly
broken pixel color pipeline. So we apply a SRGB OETF for this specific
runtime to compensate.
2019-08-07 11:52:30 +02:00
Julian Eisel
3483aa57b3 Allow querying OpenXR runtime ID
Useful in case special treatment is needed for certain runtimes.
Currently only WMR gets its ID assigned correctly, others are TODO.
2019-08-07 03:22:33 +02:00
Julian Eisel
44a220f721 Merge branch 'master' into soc-2019-openxr 2019-08-07 02:20:22 +02:00
Julian Eisel
5274c5cce6 Merge branch 'master' into soc-2019-openxr 2019-08-05 12:52:57 +02:00
Julian Eisel
0d70afed19 Merge branch 'master' into soc-2019-openxr 2019-08-02 23:35:40 +02:00
Julian Eisel
03b09dbe32 Fix compile error after merge and warning 2019-08-01 21:53:57 +02:00
Julian Eisel
f04a5ad1e4 Merge branch 'master' into soc-2019-openxr 2019-08-01 21:27:08 +02:00
Julian Eisel
ab1455e972 Fix error in OpenGL version check for OpenXR 1.0 2019-08-01 01:56:35 +02:00
Julian Eisel
b94af38c31 Fix compile error on GCC 2019-08-01 01:39:27 +02:00
Julian Eisel
40db778de3 Add sources for OpenXR core API validation layer
Had to do quite some changes to CMakeLists.txt from the SDK this time.
2019-08-01 00:28:16 +02:00
Julian Eisel
22966f4d35 Address changes in OpenXR 1.0 to get rendering to work again 2019-07-31 02:19:35 +02:00
Julian Eisel
e8f66ff060 Update OpenXR to version 1.0
Only tested on Windows.

Updates loader sources from the OpenXR SDK to latest 1.0 SDK and updates
requiremed version to 1.0. The compile time generation of files is a
thing of the past now (although you can still force it).
1.0 got released yesterday. Only one line needed fixing in our OpenXR
code to get it to compile. Rendering is black though.

For now I tried to keep edits to CMakeList.txt files minimal. So now
there are OpenXR CMake options exposed (with bad names), CMake prints,
etc.
2019-07-31 02:18:28 +02:00
Julian Eisel
9ac33e56a1 Merge branch 'master' into soc-2019-openxr 2019-07-30 22:19:41 +02:00
Julian Eisel
091cc94379 Use fixed lighting, don't make it rotate with view rotation 2019-07-24 16:41:46 +02:00
Julian Eisel
fc31be5ab0 Fix broken VR viewport rotation and movement 2019-07-24 02:52:03 +02:00
Julian Eisel
aff49f607a Use draw manager offscreen context for the VR session
This way we avoid the big overhead of context switches. Makes frames
render about twice as fast here. For heavy Spring scenes I'm getting
around 20 FPS here, classroom scene is at 50 FPS.
This is great given that drawing itself still isn't optimized for dual
eye rendering.
2019-07-24 01:39:04 +02:00
Julian Eisel
57b77cd193 Print 8 Frame average FPS for --debug-xr-time
Much more useful than a per frame FPS estimation. 8 frames are used for
the Blender viewport FPS stats too.
Refactored drawing data storage a bit.
2019-07-23 19:16:39 +02:00
Julian Eisel
d58eb8d29d Add --debug-xr-time command line option for VR frame time info prints
Outputs frame render time in milliseconds and FPS this time would add up
to. We could average times so FPS is a bit more stable, but the
precision of un-averaged results may be helpful too.
2019-07-23 02:06:08 +02:00
Julian Eisel
a4310ba85f Merge branch 'master' into soc-2019-openxr 2019-07-23 00:18:08 +02:00
Julian Eisel
bd42740ef1 Silence GCC warning 2019-07-21 13:15:00 +02:00
Julian Eisel
679a4c34fc Fix compile error with bundled OpenXR sources 2019-07-21 03:18:02 +02:00
Julian Eisel
55362436d9 Fix troubling memory leak in offscreen viewport drawing
I'm not really sure why the leak happened - draw manager kept allocating
certain buffers - but I figured I could avoid it by not recreating
GPU_offscreen/GPU_viewport on every redraw.
This should also improve performance a bit.
2019-07-21 01:10:18 +02:00
Julian Eisel
c1d164070b Pure OpenGL backend works now, let it take priority over DirectX 2019-07-20 19:34:50 +02:00
Julian Eisel
3a4034a741 Make OpenGL-only session backend work
Previously, only DirectX HMD rendering would work (and still takes
priority).
2019-07-20 19:33:11 +02:00
Julian Eisel
a0be113d2e Fix DirectX context not freed on session exit
Also rename XrSurfaceData to wmXrSurfaceData
2019-07-17 15:18:27 +02:00
Julian Eisel
151fb129e7 Fix for previous commit, accidental call to xrDestroySwapchain 2019-07-17 14:51:21 +02:00
Julian Eisel
165c5a5259 Fix possible OpenXR swapchain leak by using new unique_oxr_ptr 2019-07-17 14:05:06 +02:00
Julian Eisel
74fc1db25b Add helper class to RAII manage OpenXR handles
Adds generic unique_oxr_ptr to wrap xrCreate and xrDestroy functions of
OpenXR handles into a unique_ptr like RAII interface.

While for most cases, OpenXR resources can be freed by their owning
object, sometimes errors may occor before final ownership is established.
E.g. swapchain ownership is only transfered to the session object once
its swapchain-images are created - which may fail. With this RAII
wrapper, the swapchain would be freed on error (as this triggers stack
unwinding through an exception), no matter who holds ownership to it
currently.
There are other solutions to this problem, e.g. by establishing final
ownership right after/upon creation, or by explicit freeing in case an
error is spotted; it's too easy to make mistakes here though. Plus, we
may want to experiment with using this API for all OpenXR resources, to
entirely avoid the possibility of them leaking.
2019-07-17 13:35:14 +02:00
Julian Eisel
03ff2a8dd5 Fix compile error with USE_FORCE_WINDOWED_SESSION enabled
At least get it to compile, this is still kinda broken though.
2019-07-17 11:29:34 +02:00
d99d15f48d Add helper batch file for testing on oculus rift. 2019-07-16 16:41:44 -06:00
Julian Eisel
6a998833cd Temporarily let DirectX take priority over OpenGL
OpenGL doesn't work yet. Shouldn't be too difficult to get working, but
I don't have a working OpenXR runtime with OpenGL support to test here.
2019-07-16 21:48:29 +02:00
Julian Eisel
fca4826c7f Merge branch 'master' into soc-2019-openxr 2019-07-16 21:09:30 +02:00
Julian Eisel
b0845fc133 Fix some issues, improve error message
Fixes:
* Destruct surface when destroying session on error. Fixes null pointer
  dereference when trying to draw the surface on next redraw.
* Fix trying to enable same extensions/API-layers multiple times due to
  static array usage not being cleared after error.
* Null pointer dereference with OpenGL drawing
2019-07-13 16:22:26 +02:00
Julian Eisel
70cf8bd9c6 Fix crash on OpenXR error before context is created fully 2019-07-13 01:19:58 +02:00
Julian Eisel
3a2e459f58 Show useful error reports in case of errors.
E.g. with an active OpenXR runtime installed, but no HMD plugged in,
Blender will now show: "Failed to get device information. Is a device
plugged in?".
In case of such errors, the VR-session will be cleanly exited, with
no side effects to the rest of Blender (at least if there are no bugs).

The GHOST_Xr API now allows setting a custom error handling callback
which may cleanly destroy all GHOST_Xr data.
2019-07-12 19:51:38 +02:00
Julian Eisel
330f062099 Safe and informative GHOST_Xr error handling
This wraps all functions that could fail into some proper (although
unfinished) error handling.

These were the requirements for the error handling strategy:
* If an error occurs, cleanly exit the VR session (or the context if the
  error happend during its set up), causing no resource leaks or side
  effects to the rest of Blender.
* Show a *useful* error message to the user.
* Don't impair readability of code too much with error handling.

After some back and forth I decided Ghost internal exceptions are the
best way to go about this. I get exceptions are a controversial feature,
IMHO that's because most people use them 'wrong' however. Here's the
rationale:
* Most alternatives require early exiting functions. This early exiting
  has to be 'bubbled up' the call stack to the point that performs error
  handling. So the code gets really impaired by error checking. Tried
  this first and wasn't happy with it at all. Even if error handling is
  wrapped into macros.
* All GHOST_Xr resources are managed via RAII. So stack unwinding will
  cause them to be released cleanly whenever an exception is thrown.
* GHOST_Xr has a clear boundary (the Ghost C-API) with only a handful of
  public functions. That is the only place we need to have try-catch
  blocks at.
  (Generally, try-catch blocks at kinda random places are a bad code
  smell IMHO. Module boundaries are a valid place to put them.)
* Exceptions allow us to pass multiple bits of error information through
  mulitple layers of the call stack. This information can also be made
  specific with a useful error message.
  As of now, they conain a user error message, the OpenXR error code (if
  any), as well as the exact source code location the error was caught
  at.

So if an error is caught inside GHOST_Xr code, an exception is thrown
with specific and hopefully useful information in it. In the Ghost C-API,
these exceptions are caught and passed on to a custom error handling
callback. This callback will be defined by the Blender window-manager
and output the error information via a usual user report popup (not done
yet). It can also ensure the entire session is shut down.

Note that the majority of errors OpenXR can return are for invalid API
usage. Assuming the API usage is valid though, most error messages
should never reach users and can be left a bit more vague. Maybe we can
add something like "This is probably a bug and should be reported" to
those.
2019-07-12 14:40:50 +02:00
Julian Eisel
09872df1c7 Check runtime DirectX requirements for graphics binding too 2019-07-11 21:05:13 +02:00
Julian Eisel
67d6399da6 Check runtime OpenGL requirements prior to creating graphics binding
DirectX will need these checks too.
Also fix warnings due to wrong printf formatting.
2019-07-11 20:36:54 +02:00
Julian Eisel
c29724912b Merge branch 'master' into soc-2019-openxr 2019-07-11 20:28:19 +02:00
Julian Eisel
99560d9657 Disable debug extension on Windows
Unfortunately, enabling XR_EXT_debug_utils crashes instance creation
with the Windows Mixed Reality runtime. Maybe I'm doing something wrong,
for now, just disable it.
2019-07-11 16:44:51 +02:00
Julian Eisel
b8a7b87873 General minor fixes and cleanup
* Initialize all class member variables
* Add version to runtime name printing
* Separate functionality code from debug prints
* Improve code structure using Doxygen groups
* Make accessors const functions
* Add (Doxygen) comments
* Naming
* Reorder functions
2019-07-11 16:36:04 +02:00
Julian Eisel
c9f6c1a054 Fix wrong return value causing OpenXR initialization to fail 2019-07-11 00:51:00 +02:00
Julian Eisel
dd0fcb50b4 Fix compile errors on windows and with bundled OpenXR SDK sources 2019-07-11 00:50:10 +02:00
Julian Eisel
c1e9cf00ac Refactor GHOST_XrContext into class + interface
Makes GHOST_Xr much more consistent with the rest of GHOST. Basically I
added a GHOST_IXrContext interface which can be called by the GHOST
C-API. The internal GHOST_XrContext class implements this.
Outside of GHOST only the opaque GHOST_XrContextHandle is accessible.

Kept GHOST_Xr_intern.h and GHOST_Xr.cpp for now. I'll probably remove
them soon. There's not much reason for both of them to be there.
2019-07-10 23:53:07 +02:00
Julian Eisel
84d609f67b Use OpenXR extension for extra debug messages in debug mode
Enables (or tries to) the XR_EXT_debug_utils extension which allows
setting a custom callback for additional debug prints. Note that I
haven't been able to test this really, as the Monado runtime appears to
not have this fully implemented (had to force sending a custom message
to find that out...). Will test with the Windows MR runtime in a bit. We
can also improve message quality then (it can print the exact function
name the message was sent from, print additional custom labels which
could indicate session state, etc.).
2019-07-10 15:08:52 +02:00
Julian Eisel
86178548d5 Only try to enable OpenXR API validation layer in debug mode 2019-07-10 15:08:10 +02:00
Julian Eisel
d711540a85 Cleanup: Rename XR_DEBUG_BEGIN -> XR_DEBUG_ONLY_BEGIN
Of course also renames XR_DEBUG_END accordingly.
2019-07-10 15:06:08 +02:00
Julian Eisel
6e3158cb00 Only print info messages with --debug-xr enabled 2019-07-09 17:49:54 +02:00
Julian Eisel
b216690505 Add --debug-xr commandline arg and pass on to GHOST XR contexts
Doesn't do anything yet.
2019-07-09 17:20:01 +02:00
Julian Eisel
b98896ddd9 Preparations to allow enabling OpenXR API validation layer
For this to work two environment variables have to be set:
XR_API_LAYER_PATH and LD_LIBRARY_PATH, both have to point to the API
layers built by the OpenXR SDK. It also requires you compile Blender
linked to your own SDK build (OPENXR_USE_BUNDLED_SRC disabled).
Further changes will make this set up unnecessary, so validation layers
can be enabled via some flag.
2019-07-09 16:45:21 +02:00
Julian Eisel
a05104d61f Fix warnings and compile error on GCC 2019-07-09 11:28:17 +02:00
Julian Eisel
7fe1cc8d24 Enable grid floor in VR session
Helps quite a bit orientating.
2019-07-09 10:56:37 +02:00
Julian Eisel
70e84a2c20 Merge branch 'master' into soc-2019-openxr 2019-07-08 14:18:08 +02:00
Julian Eisel
084a33ca94 Use base pose from Blender camera
I was trying to set the camera pose as OpenXR reference pose. But then I
got really strange poses for drawing back from OpenXR. Couldn't figure
out a way to solve this.
Now we just apply OpenXR's draw pose to the Blender camera pose. If we
later want to support moving around in the scene (e.g. via teleporting),
that has to be changed.

Also uses flipped drawing for DirectX surface to correct DirectX upside
down drawing (compared to OpenGL).
2019-07-08 14:13:14 +02:00
Julian Eisel
37f619aea3 Don't recreate reference local space on each redraw
Set this up on session start.
2019-07-07 13:07:51 +02:00
Julian Eisel
6904ab10f4 Add GHOST_XrSession class for cleaner & safer session management
Session code and data structures are now localized. But also, this
enables sessions to use deterministic destruction to end itself cleanly.
So whenever an error occurs, we can use stack unwinding which will cause
graceful ending of the session.
2019-07-07 12:45:37 +02:00
Julian Eisel
146ef0dd1e Properly end VR sessions fixing crash on next session start
Previously, you had to restart Blender to start another session.
2019-07-03 21:01:58 +02:00
Julian Eisel
5891e86420 Cleanup GHOST_Xr types and functions
* Move GHOST_Xr types to GHOST_Types.h
* Add GHOST_XrPose. We'll have to pass around pose data at multiple
  places.
* Don't require extra call to prepare session rendering, handle
everything through GHOST_XrSessionStart
* Naming
2019-07-01 19:04:54 +02:00
Julian Eisel
2cedad990a Initial HMD viewport rendering (DirectX only first)
Finally: This makes it possible to render a viewport to an HMD via
OpenXR. Pure OpenGL rendering will need some more tweaks to work.
To my great delight, performance is quite good for reasonably sized
scenes.

Had to do some hacks and marked some TODOs. Nothing too bad though.

Here are a couple of notes:
* Current initial pose is pretty useless, think it just looks downwards
  from world origin. Will change that soon.
* The rendered viewport has some issues: Too dark (bad lighting?), grid
  doesn't show up even though I told it to, lighting seems to change with
  view position/rotation, etc. Needs some polish.
* Ideally we'd just use the D3D11 Texture given to us via the OpenXR
  swapchain and blit the OpenGL framebuffer into that. However the
  NV_DX_interop extension fails doing this. Seems like this is a NVidia
  Optimus only issue, but I'm missing the hardware to confirm.
  So instead, we blit into the D3D11 back buffer first and then into the
  Texture.
* The draw-manager uses its own offscreen context so we have to get the
  render result from the draw-manager context to the VR session's
  context first. Luckily I've already added code to support blitting from
  one OpenGL context into another. But it requires blitting twice.
  Blitting should be very cheap, but still...
  Draw-manager could get a context to use passed instead.
2019-07-01 15:57:32 +02:00
Julian Eisel
109be29e42 Draw offscreen viewport in XR session surface callback
Not visible yet, but it should draw in the offscreen. The way this is
now, we don't depend on the Window->Workspace->bScreen->... chain. We
simply draw an offscreen viewport in the draw callback of the XR session
surface.

The drawing also uses view and projection matrices from OpenXR (or
calculated from OpenXR data).
2019-06-29 02:30:23 +02:00
Julian Eisel
13442da69f Move WM level XR functions to wm_xr.c
Operator is still in wm_operators.c, but only calls wm_xr.c functions.
2019-06-28 22:53:46 +02:00
Julian Eisel
231dbd53bf Support Window-less (offscreen) VR session on Windows
Uses the new wmSurface type (non-window drawable container) to manage
the OpenGL, DirectX and GPU module contexts. The draw callback of the XR
surface calls the GHOST_Xr session drawing routines.

What you should see when starting a VR session now (using the WMR
runtime): The Windows Mixed Reality Portal pops up, and a blue
background is drawn on the HMD. This is from the blue color clear call
we do in the drawing preparations of the GHOST_Xr session drawing.
2019-06-28 16:24:24 +02:00
Julian Eisel
d3e48fb096 Merge branch 'master' into soc-2019-openxr 2019-06-28 14:09:08 +02:00
Julian Eisel
d544a0b41c Avoid std::vector copy 2019-06-28 13:56:10 +02:00
Julian Eisel
cf4799e299 Add wmSurface for non-Window offscreen VR session drawing
Adds a wmSurface type which acts as a container for non-Window (offscreen)
draw surfaces. Ideally wmWindow would of course also just do C-style
inheritance from wmSurface, but I guess they can co-exist too.
For the VR session a surface is created on Linux and passed to the
graphics binding to use.

Note this is not used on Windows yet, it still opens a window there.
2019-06-28 13:38:34 +02:00
Julian Eisel
57d9f004aa Fix compile/CMake errors and warnings on Linux 2019-06-27 19:55:22 +02:00
Julian Eisel
62cde7a9ef Finish VR view drawing callback set up
* Allow passing custom data to session draw function, passed to the
  callback
* Actually call the callback
* Create and bind a WM level callback. Will later be used to draw the
  viewport.

Also, check if session is actually visible before drawing.
2019-06-27 19:17:12 +02:00
Julian Eisel
e4fcf25fc5 Don't use Microsoft::WRL::ComPtr<...> for COM types
Makes things difficult if you're not familar with COM.
2019-06-27 18:56:20 +02:00
Julian Eisel
215c919b33 Merge branch 'master' into soc-2019-openxr 2019-06-26 18:39:31 +02:00
Julian Eisel
da44a02d00 The first pixels pushed to Windows Mixed Reality HMDs!
Just a clear call for now, so all you see is a blue world. This blue
"world" is however drawn by Blender!

Also fixes use after destruction of compositor layer data.
2019-06-24 21:22:23 +02:00
Julian Eisel
867007d9d7 Set up OpenXR compositing layers for drawing
That should be one of the last steps to prepare pushing pixels to HMDs.
Fingers crossed!
2019-06-24 16:44:43 +02:00
Julian Eisel
e9ea814233 Merge branch 'master' into soc-2019-openxr 2019-06-24 12:52:37 +02:00
Julian Eisel
6b43c82cb5 Set up OpenXR views and spaces for drawing
Using a dummy identity pose at {0, 0, 0} to start with. This should
later use the current viewport position & orientation I guess.

Also adds function to set a draw callback. There's quite some OpenXR
related stuff to be done before and after drawing anyting, as well as
before and after drawing each view (eye). Quite some info would have to
be exposed to WM to let it manage drawing. So I think using a callback
called from GHOST_Xr to draw each eye instead is a good way to go.

VR session currently crashes after opening. Seems to be related to
blocking frame wait call. I'm not too worried about that though, might
disappear once OpenXR frame sync functions get proper timing info
passed.
2019-06-23 18:27:24 +02:00
Julian Eisel
afbb62fecd Bring back wm_xr.c for higher level XR functions 2019-06-23 15:42:18 +02:00
Julian Eisel
8a676e8fa9 Execute necessary OpenXR frame timing calls 2019-06-23 13:47:42 +02:00
Julian Eisel
c52111e983 Correctly destruct swapchain on shutdown 2019-06-23 02:46:25 +02:00
Julian Eisel
d749e8a2c4 Create graphics binding specific swapchain images
Following the OpenXR SDK's example code very closely here. We have to do
some nasty converting of graphics binding specific image vectors to
generalized base ones. The SDK's approach seems like a good way to go
about this.
2019-06-23 02:12:38 +02:00
Julian Eisel
8663aa80e6 Use abstract class/interface for generalized graphics binding operations
Rather than having switch case blocks throughout the session code, give
binding operations an own interface with dedicated implementations.
2019-06-23 00:37:17 +02:00
Julian Eisel
88b39444a0 Fix error causing sessions to not start correctly
With this, the Windows Mixed Reality Portal finally pops up when
starting the session. That is how it's supposed to work. After it's
initialization phase all you see is black. That's expected too as we
don't send anything to the device yet.
2019-06-22 22:08:50 +02:00
Julian Eisel
4cfc3caa09 OpenXR swapchain creation
Nothing special to say. Just calls needed OpenXR functions to create
swapchains. Swapchain images are not created yet.

Interestingly, the Windows Mixed Realtiy portal now pops up when closing
Blender after having created a VR session. So we're getting closer ;)
2019-06-22 21:25:58 +02:00
Julian Eisel
28428b3462 Merge branch 'master' into soc-2019-openxr 2019-06-21 22:31:01 +02:00
Julian Eisel
24146563a0 Cleanup: Use CamelCase for XR functions/types
Also silence warnings.
2019-06-21 22:27:40 +02:00
Julian Eisel
2ee9c60c20 Finish OpenXR GLX binding initialization
Monado now opens a window here when asking it to start a session. That
seems to be the case because it doesn't detect the HMD as direct mode
capable yet. But that shouldn't be an issue from our side.
2019-06-19 21:11:21 +02:00
Julian Eisel
fb64675209 Fix invalid delete operator use
Again a thing MSVC should warn about but didn't...
2019-06-19 19:09:40 +02:00
Julian Eisel
76385139cf Use friend class to access graphics data on Windows too
Also fix memory leak.
2019-06-19 18:30:19 +02:00
Julian Eisel
b18f5a6a81 Pass graphics context data to OpenXR graphics bindings on Linux too
Refactors function into a class, and make this class a friend of
GHOST_ContextGLX. That seems like a better way to access low level
graphics data for this specific case, rather than giving anyone access
via a getter.
2019-06-19 17:23:41 +02:00
Julian Eisel
9d29373b06 Init OpenXR graphics bindings with valid graphics context (Win only)
Will do this for Linux in a separate commit.

Also fixes a stupid mistake from previous commit on lazy-creation of XR
context.
2019-06-19 12:20:18 +02:00
Julian Eisel
f2f1e4dfed Remove CTX_wm_xr_context
Not really useful, plus XR-Context is now GHOST data.
2019-06-19 01:00:37 +02:00
Julian Eisel
0da0f29ade Rename of XR types (WM -> GHOST) 2019-06-19 00:57:50 +02:00
Julian Eisel
a8519dbac2 Lazy-create XR-context
Creating the context causes the OpenXR loader to try connect to a
runtime. That would involve reading the OS'es active_runtime.json and
dynamic linking based on that. So better avoid doing this on startup.
Also: don't pay for what you don't use!
2019-06-19 00:44:42 +02:00
Julian Eisel
d15d07c4f6 Rename wm_xr_ -> GHOST_XR 2019-06-19 00:28:37 +02:00
Julian Eisel
d356787a65 Merge branch 'soc-2019-openxr' into temp-ghost_openxr 2019-06-18 23:50:28 +02:00
Julian Eisel
a284fa3abb Merge branch 'master' into soc-2019-openxr 2019-06-18 23:49:54 +02:00
Julian Eisel
4eeb7523e9 Use some C++ features where useful
Also correct include guards.
2019-06-18 01:02:33 +02:00
Julian Eisel
f30fcd6c85 Initially move XR files to GHOST and compile in C++ 2019-06-17 23:45:28 +02:00
Julian Eisel
47e8133520 Merge branch 'master' into soc-2019-openxr 2019-06-17 22:24:51 +02:00
Julian Eisel
3fa7d59a04 Fail without crashing if no runtime is found
For now simply do nothing. Plan is to work on proper error handling and
reporting later.
2019-06-17 01:24:07 +02:00
Julian Eisel
df8dc43946 Fix compile errors and warnings on certain configs
Addresses:
* Compile error on Linux
* Compile error with WITH_OPENXR disabled
* Unused parameter warnings
2019-06-16 23:28:14 +02:00
Julian Eisel
3b0a2fecfc Fix missing return value... how did this work even?
Looking at the code this should've failed miserably. No idea how this
could still work fine... Maybe return vars used same stack location?
2019-06-16 23:12:13 +02:00
Julian Eisel
80af6d534d Merge branch 'master' into soc-2019-openxr 2019-06-16 22:58:56 +02:00
Julian Eisel
71c7d61808 Draw OpenGL framebuffer upside down in DirectX windows
DirectX is Y coordinates top to bottom, while OpenGL is the opposite.
For the final window manager on-screen drawing, draw to a texture first
and draw that upside down if needed.
2019-06-16 21:46:57 +02:00
Julian Eisel
637b803b97 Support drawing the VR view into a DirectX window!
Phew! That a fight. But this is also a pretty important feature as it
allows interfacing with Windows Mixed Reality OpenXR runtime and the
HMDs supported by it.

Important remaining issue: The rendered viewport is upside down :) That
is of course because DirectX has the opposite vertical direction than
OpenGL.

When creating a DirectX window, we also create an OpenGL offscreen
context to use for all drawing. Just before swapping framebuffers, the
DirectX window blits the framebuffer of the offscreen context into its
swapchain. This requires the WGL_NV_DX_interop and WGL_NV_DX_interop2
extensions.

For testing/dev purposes, also adds:
* Version of the offscreen to onscreen blitting that's OpenGL only. So
  it blits the default framebuffer of the offscreen context into the one
  of the onscreen context.
  This is disabled by a #define.
* Code to draw a colored triangle using DirectX, also for testing.
  Requires the D3DCompiler.lib to be available at compile time. This is
  also disabled using a #define.
2019-06-16 21:05:21 +02:00
Julian Eisel
314eef0df8 Merge branch 'master' into soc-2019-openxr 2019-06-14 20:41:09 +02:00
Julian Eisel
34fa0f8ac6 Set up DirectX window to support drawing
With this the VR window should open fine and get cleared in a red-ish
orange using Direct3D 11 calls (well, on Windows that is).

The window still draws a 3D view to an offscreen buffer. Where we
usually just swap the buffers, we now allow calling a GHOST function to
blit the offscreen OpenGL buffer to whatever type of graphics buffer the
window uses (DirectX here).
The nice thing about this approach is that all DirectX code stays in
GHOST_ContextD3D.cpp. And the entire compatibiliy code can go into a
single function higher level modules don't need to care about.

This also fixes a number of issues introduced in earlier commits.
2019-06-14 19:07:10 +02:00
Julian Eisel
16366724bd Draw into OpenGL offscreen context in the DirectX window.
The window doesn't show anything of course. However we draw (at least I
assume it does) as regular, just into a window offscreen context.

A valid 3D view is created in the window. It's not visible but you see
cursor changes as you move over the window. So handling works.
2019-06-14 02:41:59 +02:00
Julian Eisel
fc8127d070 (Disabled) code to open a DirectX window with the VR session
The window immediately crashes, hence keeping it disabled for now.

Not sure how much of this I'll leave in, for now this is mainly for
testing DirectX compatibility.
2019-06-13 23:24:30 +02:00
Julian Eisel
3ac37fb552 Fix compile errors with older MSVC/Win-SDK versions
Reported by @LazyDodo, thanks!
2019-06-13 18:22:26 +02:00
Julian Eisel
8e51a75772 Fix mistake causing compiler error on Windows
Stupid one...
2019-06-12 23:45:19 +02:00
Julian Eisel
ba2dab402d Merge branch 'master' into soc-2019-openxr 2019-06-12 21:28:40 +02:00
Julian Eisel
796994d222 Refactor graphics context binding to avoid memory leaks
* Retrieve graphics context to bind through callbacks so the XR code can
  manage their lifetime.
* Use static union to store system & chosen graphics lib specific
  graphics binding data.

Makes some things a bit cleaner, too.
2019-06-12 21:27:00 +02:00
Julian Eisel
66e90e9179 Fix compile error and warning on GCC 2019-06-12 19:28:51 +02:00
Julian Eisel
1404edbe3a Support DirectX Ghost context creation
Adds support for creating a DirectX 11 Ghost context. It's not used yet
and more stuff besides creation is needed. Also, other versions than 11
should probably be supported.
We need a DirectX context to support the Windows Mixed Reality OpenXR
Runtime, a rather important one to support. Idea is to use an extension
for OpenGL-DirectX interoperability for drawing the OpenGL offscreen
viewport render using DirectX.
2019-06-12 11:34:21 +02:00
Julian Eisel
2f68a76409 Split wm_xr.c into multiple files
Similar to gizmo/ and message_bus/, there's now a xr/ directory
containing header files and a intern/ directory for source and internal
header files.

Guess this is reasonable to do. And better to do early on to avoid
loosing much git history.
2019-06-11 17:52:04 +02:00
Julian Eisel
304d9a20fc Fix compile errors and crashes on Windows
* Include needed Windows/DirectX headers
* Disable zero sized arraz (GCC extension)
* Add missing break causing failling assert
* Add missing semicolon in Win only code :)
2019-06-11 16:35:31 +02:00
Julian Eisel
b169e327bf Fix compile error with system OpenXR SDK
Graphics binding types were disabled by a compiler flag then.
2019-06-11 14:18:35 +02:00
Julian Eisel
7238bbecc4 Cleanup: Rename graphics-lib to graphics-binding 2019-06-10 22:16:09 +02:00
Julian Eisel
a49b4ca402 Merge branch 'master' into soc-2019-openxr 2019-06-10 22:06:42 +02:00
Julian Eisel
eb0424c946 Proper management of OpenXR graphics bindings for session setup
OpenXR needs to interface with some graphics library (OpenGL, Vulkan,
DirectX, etc.). This is done through graphics binding extensions. The
OpenXR specification requires these to be properly set up before a
session is created.

Adds the following:
* Support priority list of multiple graphics binding extensions (e.g.
  check OpenGL extension availability first, DirectX on Windows second,
  etc.)
* Barebones for passing graphics library data to OpenXR session
  creation. This is highly system dependent, e.g. it requires GLX data
  for OpenGL on X11 systems (XrGraphicsBindingOpenGLXlibKHR). More work,
  including additions to GHOST, will be needed once I get to the more
  graphics related stuff.
* Create an own graphics context for the VR session. It's not doing
  anything useful yet. This is just to fool the Monado OpenXR runtime
  so that it actually attempts to create the OpenXR session.
* Had to add two CMake modules for platform dependent #define's required
  by the OpenXR specification.
2019-06-10 21:59:26 +02:00
Julian Eisel
5d6da57aa3 Cleanup: Move OpenXRData struct out of wmXRContext
Just to make access a bit more convenient.
2019-06-06 18:38:06 +02:00
Julian Eisel
e968bbc5d8 Fix wrong enum-type used for switch
Why doesn't MSVC warn about this, grr...
2019-06-06 17:37:18 +02:00
Julian Eisel
b1b0e05c54 Fix CMake errors after merge (?)
Not sure if this is caused by changes just merged in, or by a system
upgrade I just did. Either way, fixed it now.
2019-06-06 11:25:53 +02:00
Julian Eisel
e547026dfe Merge branch 'master' into soc-2019-openxr 2019-06-06 10:56:19 +02:00
Julian Eisel
f816380af5 Silence warning, fix mem-leak, don't destroy NULL session 2019-06-05 18:03:59 +02:00
Julian Eisel
5e642bfea1 Initial session state handling
To correctly start a session, a graphics extension specific object needs
to be passed to the OpenXR runtime. E.g. for the Windows Mixed Reality
runtime, XrGraphicsBindingD3D11KHR needs to be passed with a valid
DirectX device. Since we don't have any DirectX compatibility working,
I can't test this on Windows yet.
So to test this I finally need to get Monado to work on Linux and
correctly setup the OpenGL extension there.
2019-06-05 16:55:51 +02:00
Julian Eisel
0d1a22f74f Barebones for managing session changes
Adds initial OpenXR event querying so that session state change events
can be handled. Doesn't do any handling yet.
2019-06-05 10:20:51 +02:00
Julian Eisel
20a1af936d Enable graphics extensions if available
Check if available extensions includes the GL one, or the DirectX one on
Windows. Enable them if available.
2019-06-05 01:00:13 +02:00
Julian Eisel
c9c1b90532 Cleanup: Correct local variable name 2019-06-04 18:30:07 +02:00
Julian Eisel
4192281e14 Fix layer and extension count not stored as intended 2019-06-04 18:27:44 +02:00
Julian Eisel
329deadc9e Print OpenXR runtime name once connected 2019-06-04 17:39:12 +02:00
Julian Eisel
53845c554d Merge branch 'master' into soc-2019-openxr 2019-06-04 17:00:58 +02:00
Julian Eisel
35c9e3beed Initial VR-session starting/ending
Adds operator to toggle a VR session, exposed in the Window top-bar
menu. It triggers the needed calls for session creation and destruction.
Setting up the XR-system (a configuration of related devices) is also
done now.

Calling WMR runtime functions fails currently. Not sure why. So while
this executes required routines, it doesn't really work.
2019-06-04 16:42:42 +02:00
Julian Eisel
60f66768cf Fix GCC compile error, warnings and uninitialized use
extension_count could be used uninitialized if no valid OpenXR runtime
was run.
2019-06-02 22:04:52 +02:00
Julian Eisel
3e88974a80 Merge branch 'master' into soc-2019-openxr 2019-06-02 15:24:33 +02:00
Julian Eisel
09e7d6cd26 Correct previous commit
Intended to print out name, not address.
2019-06-02 02:24:04 +02:00
Julian Eisel
5d5ad5d6dd Gather available OpenXR extensions and API-layers on OpenXR setup
We don't actually enable any extension or layer yet. We just put their
names into arrays and print them. The printing can be disabled via a
compiler define, later we should put them behind a proper
logging/debugging mechanism (CLOG).

Also fixes a Visual Studio compile error.
2019-06-01 17:16:49 +02:00
Julian Eisel
e70894c360 Fix compiling with bundled OpenXR SDK sources
Also remove unused include.
2019-05-30 16:22:05 +02:00
Julian Eisel
4c11886c4e Corrections to previous commit
* Remove testing instance creation
* Rename function
2019-05-30 14:56:23 +02:00
Julian Eisel
41e2f2e75f Proper creation & destruction of OpenXR instances
Adds wm_xr.c for an XR management API and creates/destroys the OpenXR
instance through this. This is as planned in my proposal, to lock the
OpenXR calls behind an abstraction.
XR data will be stored in a (non-public) wmXRContext struct within the
window-manager.

For now, creates the OpenXR instance on startup. I think it's better to
lazy setup this, as in, only creating the instance once the user starts
the first XR session. Just to avoid costs for something that may not be
used (the OpenXR loader we use will try loading and parsing the system's
OpenXR active_runtime.json on instance creation). That's for later when
I introduce session management though.

Also added a context getter for the xr-context, which is unused but may
be handy later.
2019-05-30 14:48:13 +02:00
Julian Eisel
864abbd425 Support system installed OpenXR SDK
Adds OPENXR_USE_BUNDLED_SRC so that if disabled, CMake tries to find
the SDK headers and libraries in system paths or in specified root
directory.

I guess this is the way we'd want to do this in master. However for
people testing the branch the bundled sources are much more convenient
(should work out of the box, no need to compile the SDK manually).
2019-05-30 01:44:13 +02:00
Julian Eisel
f333e7eb56 Merge branch 'master' into soc-2019-openxr 2019-05-29 20:14:11 +02:00
Julian Eisel
a2ac2ec3c3 Merge branch 'master' into soc-2019-openxr 2019-05-29 15:17:41 +02:00
Julian Eisel
06d52afcda Linux: OpenXR Linking via OpenXR Loader
Calling OpenXR functions should now work on both Windows and Linux.

Also includes:
* Silence Linux only warnings
* Remove common_cmake_config.h and let CMake generate it when
  configuring (contents vary on system it's compiled on)
* Remove JsonCpp CMake install target
* Remove unnecessary CMakeLists for JsonCpp includes
* Remove unnecessary CMake commands
* Style cleanup
2019-05-28 21:58:18 +02:00
Julian Eisel
e65ba62c37 Windows: OpenXR Linking via OpenXR Loader
Adds needed headers for OpenXR, the loader from the OpenXR SDK and
JsonCpp into extern. Took a while to get this to compile/link, but on
Win10 it works fine now without patching #includes. Linux probably
needs more work.

Added a compile option WITH_OPENXR to toggle XR feature compiling.

Also does a dummy xrCreateInstance() call to test linking.
2019-05-28 01:21:25 +02:00
89 changed files with 4927 additions and 26 deletions

View File

@@ -236,6 +236,7 @@ option(WITH_BULLET "Enable Bullet (Physics Engine)" ON)
option(WITH_SYSTEM_BULLET "Use the systems bullet library (currently unsupported due to missing features in upstream!)" )
mark_as_advanced(WITH_SYSTEM_BULLET)
option(WITH_OPENCOLORIO "Enable OpenColorIO color management" ${_init_OPENCOLORIO})
option(WITH_OPENXR "Enable VR features through the OpenXR specification" ON)
# Compositor
option(WITH_COMPOSITOR "Enable the tile based nodal compositor" ON)
@@ -1762,6 +1763,7 @@ if(FIRST_RUN)
info_cfg_option(WITH_CYCLES)
info_cfg_option(WITH_FREESTYLE)
info_cfg_option(WITH_OPENCOLORIO)
info_cfg_option(WITH_OPENXR)
info_cfg_option(WITH_OPENIMAGEDENOISE)
info_cfg_option(WITH_OPENVDB)
info_cfg_option(WITH_ALEMBIC)

View File

@@ -99,6 +99,7 @@ else()
include(cmake/pugixml.cmake)
endif()
include(cmake/openimagedenoise.cmake)
include(cmake/openxr.cmake)
if(WITH_WEBP)
include(cmake/webp.cmake)

View File

@@ -174,6 +174,8 @@ harvest(opensubdiv/include opensubdiv/include "*.h")
harvest(opensubdiv/lib opensubdiv/lib "*.a")
harvest(openvdb/include/openvdb openvdb/include/openvdb "*.h")
harvest(openvdb/lib openvdb/lib "*.a")
harvest(openxr_sdk/include/openxr openxr_sdk/include/openxr "*.h")
harvest(openxr_sdk/lib openxr_sdk/src/loader "*.a")
harvest(osl/bin osl/bin "oslc")
harvest(osl/include osl/include "*.h")
harvest(osl/lib osl/lib "*.a")

View File

@@ -0,0 +1,53 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ***** END GPL LICENSE BLOCK *****
set(OPENXR_SDK_EXTRA_ARGS
-DBUILD_API_LAYERS=ON
-DBUILD_FORCE_GENERATION=ON
-DBUILD_LOADER=ON
-DBUILD_SPECIFICATION=OFF
-DBUILD_TESTS=OFF
-DDYNAMIC_LOADER=OFF
)
ExternalProject_Add(external_openxr_sdk
URL ${OPENXR_SDK_URI}
DOWNLOAD_DIR ${DOWNLOAD_DIR}
URL_HASH MD5=${OPENXR_SDK_HASH}
PREFIX ${BUILD_DIR}/openxr_sdk
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/openxr_sdk ${DEFAULT_CMAKE_FLAGS} ${OPENXR_SDK_EXTRA_ARGS}
PATCH_COMMAND ${PATCH_CMD} --verbose -p 1 -N -d ${BUILD_DIR}/openxr_sdk/src/external_openxr_sdk < ${PATCH_DIR}/openxr_sdk.diff
INSTALL_DIR ${LIBDIR}/openxr_sdk
)
if(WIN32)
if(BUILD_MODE STREQUAL Release)
ExternalProject_Add_Step(external_openxr_sdk after_install
COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/openxr_sdk/include/openxr ${HARVEST_TARGET}/openxr_sdk/include/openxr
COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/openxr_sdk/lib ${HARVEST_TARGET}/openxr_sdk/lib
DEPENDEES install
)
endif()
if(BUILD_MODE STREQUAL Debug)
ExternalProject_Add_Step(external_openxr_sdk after_install
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/openxr_sdk/lib/openxr_loader-1_0.lib ${HARVEST_TARGET}/openxr_sdk/lib/openxr_loader-1_0_d.lib
DEPENDEES install
)
endif()
endif()

View File

@@ -306,3 +306,7 @@ set(EMBREE_HASH 3d4a1147002ff43939d45140aa9d6fb8)
set(OIDN_VERSION 1.0.0)
set(OIDN_URI https://github.com/OpenImageDenoise/oidn/releases/download/v${OIDN_VERSION}/oidn-${OIDN_VERSION}.src.zip)
set(OIDN_HASH 19fe67b0164e8f020ac8a4f520defe60)
set(OPENXR_SDK_VERSION 1.0.0)
set(OPENXR_SDK_URI https://github.com/KhronosGroup/OpenXR-SDK-Source/archive/release-${OPENXR_SDK_VERSION}.tar.gz)
set(OPENXR_SDK_HASH 260bdc87b5a9b7ef35a540e39f875d79)

View File

@@ -27,16 +27,16 @@ getopt \
-o s:i:t:h \
--long source:,install:,tmp:,info:,threads:,help,show-deps,no-sudo,no-build,no-confirm,\
with-all,with-opencollada,with-jack,with-embree,with-oidn,\
ver-ocio:,ver-oiio:,ver-llvm:,ver-osl:,ver-osd:,ver-openvdb:,\
ver-ocio:,ver-oiio:,ver-llvm:,ver-osl:,ver-osd:,ver-openvdb:,ver-openxr\
force-all,force-python,force-numpy,force-boost,\
force-ocio,force-openexr,force-oiio,force-llvm,force-osl,force-osd,force-openvdb,\
force-ffmpeg,force-opencollada,force-alembic,force-embree,force-oidn,\
force-ffmpeg,force-opencollada,force-alembic,force-embree,force-oidn,foce-openxr\
build-all,build-python,build-numpy,build-boost,\
build-ocio,build-openexr,build-oiio,build-llvm,build-osl,build-osd,build-openvdb,\
build-ffmpeg,build-opencollada,build-alembic,build-embree,build-oidn,\
build-ffmpeg,build-opencollada,build-alembic,build-embree,build-oidn,build-openxr\
skip-python,skip-numpy,skip-boost,\
skip-ocio,skip-openexr,skip-oiio,skip-llvm,skip-osl,skip-osd,skip-openvdb,\
skip-ffmpeg,skip-opencollada,skip-alembic,skip-embree,skip-oidn \
skip-ffmpeg,skip-opencollada,skip-alembic,skip-embree,skip-oidn,skip-openxr \
-- "$@" \
)
@@ -144,6 +144,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
--ver-openvdb=<ver>
Force version of OpenVDB library.
--ver-openxr=<ver>
Force version of OpenXR-SDK.
Note about the --ver-foo options:
It may not always work as expected (some libs are actually checked out from a git rev...), yet it might help
to fix some build issues (like LLVM mismatch with the version used by your graphic system).
@@ -196,6 +199,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
--build-ffmpeg
Force the build of FFMpeg.
--build-openxr
Force the build of OpenXR-SDK.
Note about the --build-foo options:
* They force the script to prefer building dependencies rather than using available packages.
This may make things simpler and allow working around some distribution bugs, but on the other hand it will
@@ -254,6 +260,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
--force-ffmpeg
Force the rebuild of FFMpeg.
--force-openxr
Force the rebuild of OpenXR-SDK.
Note about the --force-foo options:
* They obviously only have an effect if those libraries are built by this script
(i.e. if there is no available and satisfactory package)!
@@ -303,7 +312,10 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
Unconditionally skip OpenImageDenoise installation/building.
--skip-ffmpeg
Unconditionally skip FFMpeg installation/building.\""
Unconditionally skip FFMpeg installation/building.
--skip-openxr
Unconditionally skip OpenXR-SDK installation/building.\""
##### Main Vars #####
@@ -416,6 +428,11 @@ FFMPEG_FORCE_REBUILD=false
FFMPEG_SKIP=false
_ffmpeg_list_sep=";"
OPENXR_VERSION="1.0.0"
OPENXR_FORCE_BUILD=false
OPENXR_FORCE_REBUILD=false
OPENXR_SKIP=false
# FFMPEG optional libs.
VORBIS_USE=false
VORBIS_DEV=""
@@ -581,6 +598,11 @@ while true; do
OPENVDB_VERSION_MIN=$OPENVDB_VERSION
shift; shift; continue
;;
--ver-openxr)
OPENXR_VERSION="$2"
OPENXR_VERSION_MIN=$OPENXR_VERSION
shift; shift; continue
;;
--build-all)
PYTHON_FORCE_BUILD=true
NUMPY_FORCE_BUILD=true
@@ -597,6 +619,7 @@ while true; do
OIDN_FORCE_BUILD=true
FFMPEG_FORCE_BUILD=true
ALEMBIC_FORCE_BUILD=true
OPENXR_FORCE_BUILD=true
shift; continue
;;
--build-python)
@@ -648,6 +671,9 @@ while true; do
--build-alembic)
ALEMBIC_FORCE_BUILD=true; shift; continue
;;
--build-openxr)
OPENXR_FORCE_BUILD=true; shift; continue
;;
--force-all)
PYTHON_FORCE_REBUILD=true
NUMPY_FORCE_REBUILD=true
@@ -664,6 +690,7 @@ while true; do
OIDN_FORCE_REBUILD=true
FFMPEG_FORCE_REBUILD=true
ALEMBIC_FORCE_REBUILD=true
OPENXR_FORCE_REBUILD=true
shift; continue
;;
--force-python)
@@ -713,6 +740,9 @@ while true; do
--force-alembic)
ALEMBIC_FORCE_REBUILD=true; shift; continue
;;
--force-openxr)
OPENXR_FORCE_REBUILD=true; shift; continue
;;
--skip-python)
PYTHON_SKIP=true; shift; continue
;;
@@ -758,6 +788,9 @@ while true; do
--skip-alembic)
ALEMBIC_SKIP=true; shift; continue
;;
--skip-openxr)
OPENXR_SKIP=true; shift; continue
;;
--)
# no more arguments to parse
break
@@ -884,6 +917,12 @@ OIDN_SOURCE=( "https://github.com/OpenImageDenoise/oidn/releases/download/v${OID
FFMPEG_SOURCE=( "http://ffmpeg.org/releases/ffmpeg-$FFMPEG_VERSION.tar.bz2" )
OPENXR_USE_REPO=false
OPENXR_SOURCE=("https://github.com/KhronosGroup/OpenXR-SDK-Source/archive/release-$OPENXR_VERSION.tar.gz")
#~ OPENXR_SOURCE_REPO=("https://github.com/KhronosGroup/OpenXR-SDK-Source.git")
#~ OPENXR_REPO_UID="348912bf9bfaf445ac2974bda19fd0d50496460b"
#~ OPENXR_REPO_BRANCH="master"
# C++11 is required now
CXXFLAGS_BACK=$CXXFLAGS
CXXFLAGS="$CXXFLAGS -std=c++11"
@@ -2799,6 +2838,103 @@ compile_FFmpeg() {
}
#### Build OpenXR SDK ####
_init_openxr_sdk() {
_src=$SRC/OpenXR-SDK-$OPENXR_VERSION
_git=true
_inst=$INST/openxr-sdk-$OPENXR_VERSION
_inst_shortcut=$INST/openxr-sdk
}
clean_OpenXR_SDK() {
_init_openxr_sdk
_clean
}
compile_OpenXR_SDK() {
if [ "$NO_BUILD" = true ]; then
WARNING "--no-build enabled, OpenXR will not be compiled!"
return
fi
# To be changed each time we make edits that would modify the compiled result!
openxr_magic=0
_init_openxr_sdk
# Clean install if needed!
magic_compile_check openxr-$OPENXR_VERSION $openxr_magic
if [ $? -eq 1 -o "$OPENXR_FORCE_REBUILD" = true ]; then
clean_OpenXR_SDK
fi
if [ ! -d $_inst ]; then
INFO "Building OpenXR-SDK-$OPENXR_VERSION"
prepare_opt
if [ ! -d $_src ]; then
mkdir -p $SRC
if [ "$OPENXR_USE_REPO" = true ]; then
git clone $OPENXR_SOURCE_REPO $_src
else
download OPENXR_SOURCE[@] "$_src.tar.gz"
INFO "Unpacking OpenXR-SDK-$OPENXR_VERSION"
tar -C $SRC --transform "s,(.*/?)OpenXR-SDK-[^/]*(.*),\1OpenXR-SDK-$OPENXR_VERSION\2,x" \
-xf $_src.tar.gz
fi
fi
cd $_src
if [ "$OPENXR_USE_REPO" = true ]; then
git pull origin $OPENXR_REPO_BRANCH
# Stick to same rev as windows' libs...
git checkout $OPENXR_REPO_UID
git reset --hard
fi
# Always refresh the whole build!
if [ -d build ]; then
rm -rf build
fi
mkdir build
cd build
cmake_d="-D CMAKE_BUILD_TYPE=Release"
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
cmake_d="$cmake_d -D BUILD_API_LAYERS=ON"
cmake_d="$cmake_d -D BUILD_FORCE_GENERATION=ON"
cmake_d="$cmake_d -D BUILD_LOADER=ON"
cmake_d="$cmake_d -D BUILD_SPECIFICATION=OFF"
cmake_d="$cmake_d -D BUILD_TESTS=OFF"
cmake $cmake_d ..
make -j$THREADS && make install
make clean
if [ -d $_inst ]; then
_create_inst_shortcut
else
ERROR "OpenXR-SDK-$OPENXR_VERSION failed to compile, exiting"
exit 1
fi
magic_compile_set openxr-$OPENXR_VERSION $openxr_magic
cd $CWD
INFO "Done compiling OpenXR-SDK-$OPENXR_VERSION!"
else
INFO "Own OpenXR-SDK-$OPENXR_VERSION is up to date, nothing to do!"
INFO "If you want to force rebuild of this lib, use the --force-openxr option."
fi
run_ldconfig "openxr"
}
#### Install on DEB-like ####
get_package_version_DEB() {
dpkg-query -W -f '${Version}' $1 | sed -r 's/([0-9]+:)?(([0-9]+\.?)+([0-9]+)).*/\2/'
@@ -3323,6 +3459,18 @@ install_DEB() {
compile_FFmpeg
fi
fi
PRINT ""
if [ "$OPENXR_SKIP" = true ]; then
WARNING "Skipping OpenXR-SDK installation, as requested..."
elif [ "$OPENXR_FORCE_BUILD" = true ]; then
INFO "Forced OpenXR-SDK building, as requested..."
compile_OpenXR_SDK
else
# No package currently!
PRINT ""
compile_OpenXR_SDK
fi
}
@@ -3909,6 +4057,17 @@ install_RPM() {
compile_FFmpeg
fi
fi
PRINT ""
if [ "$OPENXR_SKIP" = true ]; then
WARNING "Skipping OpenXR-SDK installation, as requested..."
elif [ "$OPENXR_FORCE_BUILD" = true ]; then
INFO "Forced OpenXR-SDK building, as requested..."
compile_OpenXR_SDK
else
# No package currently!
compile_OpenXR_SDK
fi
}
@@ -4391,6 +4550,17 @@ install_ARCH() {
compile_FFmpeg
fi
fi
PRINT ""
if [ "$OPENXR_SKIP" = true ]; then
WARNING "Skipping OpenXR-SDK installation, as requested..."
elif [ "$OPENXR_FORCE_BUILD" = true ]; then
INFO "Forced OpenXR-SDK building, as requested..."
compile_OpenXR_SDK
else
# No package currently!
compile_OpenXR_SDK
fi
}
@@ -4587,6 +4757,17 @@ install_OTHER() {
INFO "Forced FFMpeg building, as requested..."
compile_FFmpeg
fi
PRINT ""
if [ "$OPENXR_SKIP" = true ]; then
WARNING "Skipping OpenXR-SDK installation, as requested..."
elif [ "$OPENXR_FORCE_BUILD" = true ]; then
INFO "Forced OpenXR-SDK building, as requested..."
compile_OpenXR_SDK
else
# No package currently!
compile_OpenXR_SDK
fi
}
#### Printing User Info ####
@@ -4844,6 +5025,17 @@ print_info() {
fi
fi
if [ "$OPENXR_SKIP" = false ]; then
_1="-D WITH_OPENXR=ON"
PRINT " $_1"
_buildargs="$_buildargs $_1"
if [ -d $INST/openxr-sdk ]; then
_1="-D OPENXR_ROOT_DIR=$INST/openxr-sdk"
PRINT " $_1"
_buildargs="$_buildargs $_1"
fi
fi
PRINT ""
PRINT "Or even simpler, just run (in your blender-source dir):"
PRINT " make -j$THREADS BUILD_CMAKE_ARGS=\"$_buildargs\""

View File

@@ -0,0 +1,28 @@
diff -Naur orig/src/loader/CMakeLists.txt external_openxr_sdk/src/loader/CMakeLists.txt
--- orig/src/loader/CMakeLists.txt 2019-07-29 07:06:59 -0600
+++ external_openxr_sdk/src/loader/CMakeLists.txt 2019-08-20 07:56:51 -0600
@@ -128,24 +128,6 @@
configure_file("openxr.pc.in" "openxr.pc" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/openxr.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
- foreach(configuration in CMAKE_C_FLAGS_DEBUG
- CMAKE_C_FLAGS_RELEASE
- CMAKE_C_FLAGS_RELWITHDEBINFO
- CMAKE_CXX_FLAGS_DEBUG
- CMAKE_CXX_FLAGS_RELEASE
- CMAKE_CXX_FLAGS_RELWITHDEBINFO)
- # If building DLLs, force static CRT linkage
- if(DYNAMIC_LOADER)
- if (${configuration} MATCHES "/MD")
- string(REGEX REPLACE "/MD" "/MT" ${configuration} "${${configuration}}")
- endif()
- else() # Otherwise for static libs, link the CRT dynamically
- if (${configuration} MATCHES "/MT")
- string(REGEX REPLACE "/MT" "/MD" ${configuration} "${${configuration}}")
- endif()
- endif()
- endforeach()
-
target_link_libraries(${LOADER_NAME} shlwapi)
target_compile_options(${LOADER_NAME} PRIVATE)
generate_export_header(${LOADER_NAME})

View File

@@ -0,0 +1,68 @@
# - Find OpenXR-SDK library
# Find the native OpenXR-SDK includes and library
# This module defines
# OPENXR_SDK_INCLUDE_DIRS, where to find OpenXR-SDK headers, Set when
# OPENXR_SDK_INCLUDE_DIR is found.
# OPENXR_SDK_LIBRARIES, libraries to link against to use OpenXR-SDK.
# OPENXR_SDK_ROOT_DIR, the base directory to search for OpenXR-SDK.
# This can also be an environment variable.
# OPENXR_SDK_FOUND, if false, do not try to use OpenXR-SDK.
#
# also defined, but not for general use are
# OPENXR_LOADER_LIBRARY, where to find the OpenXR-SDK library.
#=============================================================================
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# If OPENXR_SDK_ROOT_DIR was defined in the environment, use it.
IF(NOT OPENXR_SDK_ROOT_DIR AND NOT $ENV{OPENXR_SDK_ROOT_DIR} STREQUAL "")
SET(OPENXR_SDK_ROOT_DIR $ENV{OPENXR_SDK_ROOT_DIR})
ENDIF()
SET(_openxr_sdk_SEARCH_DIRS
${OPENXR_SDK_ROOT_DIR}
/usr/local
/sw # Fink
/opt/local # DarwinPorts
/opt/lib/openxr-sdk
)
FIND_PATH(OPENXR_SDK_INCLUDE_DIR
NAMES
openxr/openxr.h
HINTS
${_openxr_sdk_SEARCH_DIRS}
PATH_SUFFIXES
include
)
FIND_LIBRARY(OPENXR_LOADER_LIBRARY
NAMES
openxr_loader
HINTS
${_openxr_sdk_SEARCH_DIRS}
PATH_SUFFIXES
lib64 lib
)
# handle the QUIETLY and REQUIRED arguments and set OPENXR_SDK_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(OPENXR_SDK DEFAULT_MSG
OPENXR_LOADER_LIBRARY OPENXR_SDK_INCLUDE_DIR)
IF(OPENXR_SDK_FOUND)
SET(OPENXR_SDK_LIBRARIES ${OPENXR_LOADER_LIBRARY})
SET(OPENXR_SDK_INCLUDE_DIRS ${OPENXR_SDK_INCLUDE_DIR})
ENDIF(OPENXR_SDK_FOUND)
MARK_AS_ADVANCED(
OPENXR_SDK_INCLUDE_DIR
OPENXR_LOADER_LIBRARY
)

View File

@@ -0,0 +1,115 @@
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Copied right from the OpenXR-SDK (src/cmake/presentation.cmake).
# Don't forget to add the license header above.
set(PRESENTATION_BACKENDS xlib xcb wayland)
set(PRESENTATION_BACKEND xlib CACHE STRING
"Presentation backend chosen at configure time")
set_property(CACHE PRESENTATION_BACKEND PROPERTY STRINGS
${PRESENTATION_BACKENDS})
list(FIND PRESENTATION_BACKENDS ${PRESENTATION_BACKEND} index)
if(index EQUAL -1)
message(FATAL_ERROR "Presentation backend must be one of
${PRESENTATION_BACKENDS}")
endif()
message(STATUS "Using presentation backend: ${PRESENTATION_BACKEND}")
if( PRESENTATION_BACKEND MATCHES "xlib" )
find_package(X11 REQUIRED)
if ((NOT X11_Xxf86vm_LIB) OR (NOT X11_Xrandr_LIB))
message(FATAL_ERROR "OpenXR xlib backend requires Xxf86vm and Xrandr")
endif()
add_definitions( -DSUPPORT_X )
add_definitions( -DOS_LINUX_XLIB )
set( XLIB_LIBRARIES
${X11_LIBRARIES}
${X11_Xxf86vm_LIB}
${X11_Xrandr_LIB} )
elseif( PRESENTATION_BACKEND MATCHES "xcb" )
find_package(PkgConfig REQUIRED)
# XCB + XCB GLX is limited to OpenGL 2.1
# add_definitions( -DOS_LINUX_XCB )
# XCB + Xlib GLX 1.3
add_definitions( -DOS_LINUX_XCB_GLX )
pkg_search_module(X11 REQUIRED x11)
pkg_search_module(XCB REQUIRED xcb)
pkg_search_module(XCB_RANDR REQUIRED xcb-randr)
pkg_search_module(XCB_KEYSYMS REQUIRED xcb-keysyms)
pkg_search_module(XCB_GLX REQUIRED xcb-glx)
pkg_search_module(XCB_DRI2 REQUIRED xcb-dri2)
pkg_search_module(XCB_ICCCM REQUIRED xcb-icccm)
set( XCB_LIBRARIES
${XCB_LIBRARIES}
${XCB_KEYSYMS_LIBRARIES}
${XCB_RANDR_LIBRARIES}
${XCB_GLX_LIBRARIES}
${XCB_DRI2_LIBRARIES}
${X11_LIBRARIES} )
elseif( PRESENTATION_BACKEND MATCHES "wayland" )
find_package(PkgConfig REQUIRED)
pkg_search_module(WAYLAND_CLIENT REQUIRED wayland-client)
pkg_search_module(WAYLAND_EGL REQUIRED wayland-egl)
pkg_search_module(WAYLAND_SCANNER REQUIRED wayland-scanner)
pkg_search_module(WAYLAND_PROTOCOLS REQUIRED wayland-protocols>=1.7)
pkg_search_module(EGL REQUIRED egl)
add_definitions( -DOS_LINUX_WAYLAND )
set( WAYLAND_LIBRARIES
${EGL_LIBRARIES}
${WAYLAND_CLIENT_LIBRARIES}
${WAYLAND_EGL_LIBRARIES} )
# generate wayland protocols
set(WAYLAND_PROTOCOLS_DIR ${CMAKE_SOURCE_DIR}/wayland-protocols/)
file(MAKE_DIRECTORY ${WAYLAND_PROTOCOLS_DIR})
pkg_get_variable(WAYLAND_PROTOCOLS_DATADIR wayland-protocols pkgdatadir)
pkg_get_variable(WAYLAND_SCANNER wayland-scanner wayland_scanner)
set(PROTOCOL xdg-shell-unstable-v6)
set(PROTOCOL_XML
${WAYLAND_PROTOCOLS_DATADIR}/unstable/xdg-shell/${PROTOCOL}.xml)
if( EXISTS ${PROTOCOL_XML} )
execute_process(COMMAND
${WAYLAND_SCANNER}
code
${PROTOCOL_XML}
${WAYLAND_PROTOCOLS_DIR}/${PROTOCOL}.c)
execute_process(COMMAND
${WAYLAND_SCANNER}
client-header
${PROTOCOL_XML}
${WAYLAND_PROTOCOLS_DIR}/${PROTOCOL}.h)
set( WAYLAND_PROTOCOL_SRC
${WAYLAND_PROTOCOLS_DIR}/${PROTOCOL}.c
${WAYLAND_PROTOCOLS_DIR}/${PROTOCOL}.h )
include_directories(${WAYLAND_PROTOCOLS_DIR})
else()
message(FATAL_ERROR
"xdg-shell-unstable-v6.xml not found in "
${WAYLAND_PROTOCOLS_DATADIR}
"\nYour wayland-protocols package does not "
"contain xdg-shell-unstable-v6.")
endif()
endif()

View File

@@ -0,0 +1,50 @@
# Copyright (c) 2017 The Khronos Group Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# These defines are required as per the OpenXR specification. We can
# just take them from the OpenXR-SDK's src/CMakeLists.txt. Not all of
# them are needed (e.g. XCB and Wayland), but we just copy them anyway.
if(WIN32)
add_definitions(-DXR_OS_WINDOWS)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_definitions(-DXR_OS_LINUX)
endif()
# Determine the presentation backend for Linux systems.
# Use an include because the code is pretty big.
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
include(presentation)
endif()
# Several files use these compile-time platform switches
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_definitions( -DXR_USE_PLATFORM_WIN32 )
elseif( PRESENTATION_BACKEND MATCHES "xlib" )
add_definitions( -DXR_USE_PLATFORM_XLIB )
elseif( PRESENTATION_BACKEND MATCHES "xcb" )
add_definitions( -DXR_USE_PLATFORM_XCB )
elseif( PRESENTATION_BACKEND MATCHES "wayland" )
add_definitions( -DXR_USE_PLATFORM_WAYLAND )
endif()
add_definitions(-DXR_USE_GRAPHICS_API_OPENGL)
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_definitions(-DXR_USE_GRAPHICS_API_D3D)
add_definitions(-DXR_USE_GRAPHICS_API_D3D10)
add_definitions(-DXR_USE_GRAPHICS_API_D3D11)
add_definitions(-DXR_USE_GRAPHICS_API_D3D12)
endif()

View File

@@ -41,6 +41,7 @@ set(WITH_OPENAL ON CACHE BOOL "" FORCE)
set(WITH_OPENCOLLADA ON CACHE BOOL "" FORCE)
set(WITH_OPENCOLORIO ON CACHE BOOL "" FORCE)
set(WITH_OPENIMAGEDENOISE ON CACHE BOOL "" FORCE)
set(WITH_OPENXR ON CACHE BOOL "" FORCE)
set(WITH_OPENMP ON CACHE BOOL "" FORCE)
set(WITH_OPENSUBDIV ON CACHE BOOL "" FORCE)
set(WITH_OPENVDB ON CACHE BOOL "" FORCE)

View File

@@ -46,6 +46,7 @@ set(WITH_OPENAL OFF CACHE BOOL "" FORCE)
set(WITH_OPENCOLLADA OFF CACHE BOOL "" FORCE)
set(WITH_OPENCOLORIO OFF CACHE BOOL "" FORCE)
set(WITH_OPENIMAGEDENOISE OFF CACHE BOOL "" FORCE)
set(WITH_OPENXR OFF CACHE BOOL "" FORCE)
set(WITH_OPENIMAGEIO OFF CACHE BOOL "" FORCE)
set(WITH_OPENMP OFF CACHE BOOL "" FORCE)
set(WITH_OPENSUBDIV OFF CACHE BOOL "" FORCE)

View File

@@ -42,6 +42,7 @@ set(WITH_OPENAL ON CACHE BOOL "" FORCE)
set(WITH_OPENCOLLADA ON CACHE BOOL "" FORCE)
set(WITH_OPENCOLORIO ON CACHE BOOL "" FORCE)
set(WITH_OPENIMAGEDENOISE ON CACHE BOOL "" FORCE)
set(WITH_OPENXR ON CACHE BOOL "" FORCE)
set(WITH_OPENMP ON CACHE BOOL "" FORCE)
set(WITH_OPENSUBDIV ON CACHE BOOL "" FORCE)
set(WITH_OPENVDB ON CACHE BOOL "" FORCE)

View File

@@ -474,6 +474,9 @@ function(setup_liblinks
if(WITH_OPENSUBDIV)
target_link_libraries(${target} ${OPENSUBDIV_LIBRARIES})
endif()
if(WITH_OPENXR)
target_link_libraries(${target} ${OPENXR_SDK_LIBRARIES})
endif()
if(WITH_CYCLES_EMBREE)
target_link_libraries(${target} ${EMBREE_LIBRARIES})
endif()

View File

@@ -416,6 +416,14 @@ if(WITH_OPENMP)
endif()
endif()
if(WITH_OPENXR)
find_package(OpenXR-SDK)
if(NOT OPENXR_SDK_FOUND)
message(WARNING "OpenXR-SDK was not found, disabling WITH_OPENXR")
set(WITH_OPENXR OFF)
endif()
endif()
set(EXETYPE MACOSX_BUNDLE)
set(CMAKE_C_FLAGS_DEBUG "-fno-strict-aliasing -g")

View File

@@ -416,6 +416,15 @@ if(WITH_OPENSUBDIV)
endif()
endif()
if(WITH_OPENXR)
find_package(OpenXR-SDK)
if(NOT OPENXR_SDK_FOUND)
message(WARNING "OpenXR-SDK was not found, disabling WITH_OPENXR")
set(WITH_OPENXR OFF)
endif()
endif()
# OpenSuse needs lutil, ArchLinux not, for now keep, can avoid by using --as-needed
if(HAIKU)
list(APPEND PLATFORM_LINKLIBS -lnetwork)

View File

@@ -691,3 +691,15 @@ if(WINDOWS_PYTHON_DEBUG)
</Project>")
endif()
endif()
if(WITH_OPENXR)
if(EXISTS ${LIBDIR}/openxr_sdk)
set(OPENXR_SDK ${LIBDIR}/openxr_sdk)
set(OPENXR_SDK_LIBPATH ${LIBDIR}/openxr_sdk/lib)
set(OPENXR_SDK_INCLUDE_DIR ${OPENXR_SDK}/include)
set(OPENXR_SDK_LIBRARIES optimized ${OPENXR_SDK_LIBPATH}/openxr_loader-1_0.lib debug ${OPENXR_SDK_LIBPATH}/openxr_loader-1_0_d.lib)
else()
message(WARNING "OpenXR-SDK was not found, disabling WITH_OPENXR")
set(WITH_OPENXR OFF)
endif()
endif()

View File

@@ -76,3 +76,7 @@ endif()
if(WITH_OPENVDB)
add_subdirectory(openvdb)
endif()
if(WITH_OPENXR)
add_subdirectory(vamr)
endif()

View File

@@ -270,11 +270,13 @@ elseif(WIN32)
)
list(APPEND SRC
intern/GHOST_ContextD3D.cpp
intern/GHOST_DisplayManagerWin32.cpp
intern/GHOST_DropTargetWin32.cpp
intern/GHOST_SystemWin32.cpp
intern/GHOST_WindowWin32.cpp
intern/GHOST_ContextD3D.h
intern/GHOST_DisplayManagerWin32.h
intern/GHOST_DropTargetWin32.h
intern/GHOST_SystemWin32.h

View File

@@ -30,21 +30,6 @@
extern "C" {
#endif
/**
* Creates a "handle" for a C++ GHOST object.
* A handle is just an opaque pointer to an empty struct.
* In the API the pointer is cast to the actual C++ class.
* The 'name' argument to the macro is the name of the handle to create.
*/
GHOST_DECLARE_HANDLE(GHOST_SystemHandle);
GHOST_DECLARE_HANDLE(GHOST_TimerTaskHandle);
GHOST_DECLARE_HANDLE(GHOST_WindowHandle);
GHOST_DECLARE_HANDLE(GHOST_EventHandle);
GHOST_DECLARE_HANDLE(GHOST_RectangleHandle);
GHOST_DECLARE_HANDLE(GHOST_EventConsumerHandle);
GHOST_DECLARE_HANDLE(GHOST_ContextHandle);
/**
* Definition of a callback routine that receives events.
* \param event The event received.
@@ -193,6 +178,25 @@ extern GHOST_ContextHandle GHOST_CreateOpenGLContext(GHOST_SystemHandle systemha
extern GHOST_TSuccess GHOST_DisposeOpenGLContext(GHOST_SystemHandle systemhandle,
GHOST_ContextHandle contexthandle);
#ifdef WIN32
/**
* Create a new offscreen context.
* Never explicitly delete the context, use disposeContext() instead.
* \param systemhandle The handle to the system
* \return A handle to the new context ( == NULL if creation failed).
*/
GHOST_ContextHandle GHOST_CreateDirectXContext(GHOST_SystemHandle systemhandle);
/**
* Dispose of a context.
* \param systemhandle The handle to the system
* \param contexthandle Handle to the context to be disposed.
* \return Indication of success.
*/
GHOST_TSuccess GHOST_DisposeDirectXContext(GHOST_SystemHandle systemhandle,
GHOST_ContextHandle contexthandle);
#endif
/**
* Returns the window user data.
* \param windowhandle The handle to the window
@@ -207,6 +211,11 @@ extern GHOST_TUserDataPtr GHOST_GetWindowUserData(GHOST_WindowHandle windowhandl
*/
extern void GHOST_SetWindowUserData(GHOST_WindowHandle windowhandle, GHOST_TUserDataPtr userdata);
/**
* Returns whether a window is rendered upside down compared to OpenGL.
*/
extern int GHOST_isUpsideDownWindow(GHOST_WindowHandle windowhandle);
/**
* Dispose a window.
* \param systemhandle The handle to the system
@@ -706,6 +715,16 @@ extern GHOST_TSuccess GHOST_ActivateOpenGLContext(GHOST_ContextHandle contexthan
*/
extern GHOST_TSuccess GHOST_ReleaseOpenGLContext(GHOST_ContextHandle contexthandle);
/**
* Get the OpenGL framebuffer handle that serves as a default framebuffer.
*/
extern unsigned int GHOST_GetContextDefaultOpenGLFramebuffer(GHOST_ContextHandle contexthandle);
/**
* Returns whether a context is rendered upside down compared to OpenGL.
*/
extern int GHOST_isUpsideDownContext(GHOST_ContextHandle contexthandle);
/**
* Get the OpenGL framebuffer handle that serves as a default framebuffer.
*/

View File

@@ -56,6 +56,15 @@ class GHOST_IContext {
*/
virtual GHOST_TSuccess releaseDrawingContext() = 0;
virtual unsigned int getDefaultFramebuffer() = 0;
virtual GHOST_TSuccess swapBuffers() = 0;
/**
* Returns if the window is rendered upside down compared to OpenGL.
*/
virtual bool isUpsideDown() const = 0;
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_IContext")
#endif

View File

@@ -264,6 +264,12 @@ class GHOST_ISystem {
*/
virtual GHOST_IContext *createOffscreenContext() = 0;
/**
* Overload to allow requesting a different context type. By default only OpenGL is supported.
* However by explicitly overloading this a system may add support for others.
*/
virtual GHOST_IContext *createOffscreenContext(GHOST_TDrawingContextType type) = 0;
/**
* Dispose of a context.
* \param context Pointer to the context to be disposed.

View File

@@ -241,6 +241,11 @@ class GHOST_IWindow {
*/
virtual void setUserData(const GHOST_TUserDataPtr userData) = 0;
/**
* Returns if the window is rendered upside down compared to OpenGL.
*/
virtual bool isUpsideDown() const = 0;
/**
* Returns the tablet data (pressure etc).
* \return The tablet data (pressure etc).

View File

@@ -41,6 +41,22 @@
} * name
#endif
/**
* Creates a "handle" for a C++ GHOST object.
* A handle is just an opaque pointer to an empty struct.
* In the API the pointer is cast to the actual C++ class.
* The 'name' argument to the macro is the name of the handle to create.
*/
GHOST_DECLARE_HANDLE(GHOST_SystemHandle);
GHOST_DECLARE_HANDLE(GHOST_TimerTaskHandle);
GHOST_DECLARE_HANDLE(GHOST_WindowHandle);
GHOST_DECLARE_HANDLE(GHOST_EventHandle);
GHOST_DECLARE_HANDLE(GHOST_RectangleHandle);
GHOST_DECLARE_HANDLE(GHOST_EventConsumerHandle);
GHOST_DECLARE_HANDLE(GHOST_ContextHandle);
GHOST_DECLARE_HANDLE(GHOST_XrContextHandle);
typedef char GHOST_TInt8;
typedef unsigned char GHOST_TUns8;
typedef short GHOST_TInt16;
@@ -129,7 +145,10 @@ typedef enum { GHOST_kWindowOrderTop = 0, GHOST_kWindowOrderBottom } GHOST_TWind
typedef enum {
GHOST_kDrawingContextTypeNone = 0,
GHOST_kDrawingContextTypeOpenGL
GHOST_kDrawingContextTypeOpenGL,
#ifdef WIN32
GHOST_kDrawingContextTypeD3D,
#endif
} GHOST_TDrawingContextType;
typedef enum {

View File

@@ -127,6 +127,25 @@ GHOST_TSuccess GHOST_DisposeOpenGLContext(GHOST_SystemHandle systemhandle,
return system->disposeContext(context);
}
#ifdef WIN32
GHOST_ContextHandle GHOST_CreateDirectXContext(GHOST_SystemHandle systemhandle)
{
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
return (GHOST_ContextHandle)system->createOffscreenContext(GHOST_kDrawingContextTypeD3D);
}
GHOST_TSuccess GHOST_DisposeDirectXContext(GHOST_SystemHandle systemhandle,
GHOST_ContextHandle contexthandle)
{
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
GHOST_IContext *context = (GHOST_IContext *)contexthandle;
return system->disposeContext(context);
}
#endif
GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle,
const char *title,
GHOST_TInt32 left,
@@ -156,6 +175,13 @@ void GHOST_SetWindowUserData(GHOST_WindowHandle windowhandle, GHOST_TUserDataPtr
window->setUserData(userdata);
}
int GHOST_isUpsideDownWindow(GHOST_WindowHandle windowhandle)
{
GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
return window->isUpsideDown();
}
GHOST_TSuccess GHOST_DisposeWindow(GHOST_SystemHandle systemhandle,
GHOST_WindowHandle windowhandle)
{
@@ -621,6 +647,20 @@ GHOST_TSuccess GHOST_ReleaseOpenGLContext(GHOST_ContextHandle contexthandle)
return context->releaseDrawingContext();
}
unsigned int GHOST_GetContextDefaultOpenGLFramebuffer(GHOST_ContextHandle contexthandle)
{
GHOST_IContext *context = (GHOST_IContext *)contexthandle;
return context->getDefaultFramebuffer();
}
int GHOST_isUpsideDownContext(GHOST_ContextHandle contexthandle)
{
GHOST_IContext *context = (GHOST_IContext *)contexthandle;
return context->isUpsideDown();
}
unsigned int GHOST_GetDefaultOpenGLFramebuffer(GHOST_WindowHandle windowhandle)
{
GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;

View File

@@ -149,3 +149,4 @@ void GHOST_Context::initClearGL()
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0.000, 0.000, 0.000, 0.000);
}

View File

@@ -38,7 +38,8 @@ class GHOST_Context : public GHOST_IContext {
* Constructor.
* \param stereoVisual Stereo visual for quad buffered stereo.
*/
GHOST_Context(bool stereoVisual) : m_stereoVisual(stereoVisual)
GHOST_Context(bool stereoVisual)
: m_stereoVisual(stereoVisual)
{
}
@@ -119,6 +120,14 @@ class GHOST_Context : public GHOST_IContext {
return m_stereoVisual;
}
/**
* Returns if the window is rendered upside down compared to OpenGL.
*/
inline bool isUpsideDown() const
{
return false;
}
/**
* Gets the OpenGL framebuffer associated with the OpenGL context
* \return The ID of an OpenGL framebuffer object.

View File

@@ -0,0 +1,446 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup GHOST
*/
#include <iostream>
#include <string>
#include <GL/glew.h>
#include <GL/wglew.h>
#include "GHOST_ContextWGL.h" /* For shared drawing */
#include "GHOST_ContextD3D.h"
//#define USE_DRAW_D3D_TEST_TRIANGLE
HMODULE GHOST_ContextD3D::s_d3d_lib = NULL;
PFN_D3D11_CREATE_DEVICE GHOST_ContextD3D::s_D3D11CreateDeviceFn = NULL;
#ifdef USE_DRAW_D3D_TEST_TRIANGLE
static void drawTestTriangle(ID3D11Device *m_device,
ID3D11DeviceContext *m_device_ctx,
GHOST_TInt32 width,
GHOST_TInt32 height);
#endif
GHOST_ContextD3D::GHOST_ContextD3D(bool stereoVisual, HWND hWnd)
: GHOST_Context(stereoVisual), m_hWnd(hWnd)
{
}
GHOST_ContextD3D::~GHOST_ContextD3D()
{
m_device->Release();
m_device_ctx->ClearState();
m_device_ctx->Release();
}
GHOST_TSuccess GHOST_ContextD3D::swapBuffers()
{
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextD3D::activateDrawingContext()
{
return GHOST_kFailure;
}
GHOST_TSuccess GHOST_ContextD3D::releaseDrawingContext()
{
return GHOST_kFailure;
}
GHOST_TSuccess GHOST_ContextD3D::setupD3DLib()
{
if (s_d3d_lib == NULL) {
s_d3d_lib = LoadLibraryA("d3d11.dll");
WIN32_CHK(s_d3d_lib != NULL);
if (s_d3d_lib == NULL) {
fprintf(stderr, "LoadLibrary(\"d3d11.dll\") failed!\n");
return GHOST_kFailure;
}
}
if (s_D3D11CreateDeviceFn == NULL) {
s_D3D11CreateDeviceFn = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(s_d3d_lib,
"D3D11CreateDevice");
WIN32_CHK(s_D3D11CreateDeviceFn != NULL);
if (s_D3D11CreateDeviceFn == NULL) {
fprintf(stderr, "GetProcAddress(s_d3d_lib, \"D3D11CreateDevice\") failed!\n");
return GHOST_kFailure;
}
}
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextD3D::initializeDrawingContext()
{
if (setupD3DLib() == GHOST_kFailure) {
return GHOST_kFailure;
}
HRESULT hres = s_D3D11CreateDeviceFn(NULL,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
// D3D11_CREATE_DEVICE_DEBUG,
0,
NULL,
0,
D3D11_SDK_VERSION,
&m_device,
NULL,
&m_device_ctx);
WIN32_CHK(hres == S_OK);
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextD3D::releaseNativeHandles()
{
return GHOST_kFailure;
}
class GHOST_SharedOpenGLResource {
struct SharedData {
HANDLE device;
GLuint fbo;
HANDLE render_buf{nullptr};
} m_shared;
public:
GHOST_SharedOpenGLResource(ID3D11Device *device,
ID3D11DeviceContext *device_ctx,
unsigned int width,
unsigned int height,
ID3D11RenderTargetView *render_target = nullptr)
: m_device(device), m_device_ctx(device_ctx), m_cur_width(width), m_cur_height(height)
{
ID3D11Resource *backbuffer_res;
if (!render_target) {
D3D11_TEXTURE2D_DESC texDesc{};
D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc{};
ID3D11Texture2D *tex;
texDesc.Width = width;
texDesc.Height = height;
texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texDesc.SampleDesc.Count = 1;
texDesc.ArraySize = 1;
texDesc.MipLevels = 1;
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
device->CreateTexture2D(&texDesc, NULL, &tex);
renderTargetViewDesc.Format = texDesc.Format;
renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
renderTargetViewDesc.Texture2D.MipSlice = 0;
device->CreateRenderTargetView(tex, &renderTargetViewDesc, &render_target);
tex->Release();
}
m_render_target = render_target;
m_render_target->GetResource(&backbuffer_res);
backbuffer_res->QueryInterface<ID3D11Texture2D>(&m_render_target_tex);
backbuffer_res->Release();
}
~GHOST_SharedOpenGLResource()
{
m_render_target_tex->Release();
m_render_target->Release();
if (m_is_initialized) {
if (m_shared.render_buf) {
wglDXUnregisterObjectNV(m_shared.device, m_shared.render_buf);
}
if (m_shared.device) {
wglDXCloseDeviceNV(m_shared.device);
}
glDeleteFramebuffers(1, &m_shared.fbo);
glDeleteRenderbuffers(1, &m_gl_render_buf);
}
}
void reregisterSharedObject()
{
if (m_shared.render_buf) {
wglDXUnregisterObjectNV(m_shared.device, m_shared.render_buf);
}
m_shared.render_buf = wglDXRegisterObjectNV(m_shared.device,
m_render_target_tex,
m_gl_render_buf,
GL_RENDERBUFFER,
WGL_ACCESS_READ_WRITE_NV);
if (!m_shared.render_buf) {
fprintf(stderr, "Error registering shared object using wglDXRegisterObjectNV()\n");
return;
}
}
GHOST_TSuccess initialize()
{
m_shared.device = wglDXOpenDeviceNV(m_device);
if (m_shared.device == NULL) {
fprintf(stderr, "Error opening shared device using wglDXOpenDeviceNV()\n");
return GHOST_kFailure;
}
/* Build the renderbuffer. */
glGenRenderbuffers(1, &m_gl_render_buf);
glBindRenderbuffer(GL_RENDERBUFFER, m_gl_render_buf);
reregisterSharedObject();
/* Build the framebuffer */
glGenFramebuffers(1, &m_shared.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_shared.fbo);
glFramebufferRenderbuffer(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_gl_render_buf);
m_is_initialized = true;
return GHOST_kSuccess;
}
void ensureUpdated(unsigned int width, unsigned int height)
{
if (m_is_initialized == false) {
initialize();
}
if ((m_cur_width != width) || (m_cur_height != height)) {
m_cur_width = width;
m_cur_height = height;
reregisterSharedObject();
}
}
GHOST_TSuccess blit(unsigned int width, unsigned int height)
{
GLint fbo;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fbo);
ensureUpdated(width, height);
const float clear_col[] = {0.8f, 0.5f, 1.0f, 1.0f};
m_device_ctx->ClearRenderTargetView(m_render_target, clear_col);
m_device_ctx->OMSetRenderTargets(1, &m_render_target, nullptr);
beginGLOnly();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_shared.fbo);
GLenum err = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (err != GL_FRAMEBUFFER_COMPLETE) {
fprintf(stderr, "Error: Framebuffer incomplete %u\n", err);
return GHOST_kFailure;
}
/* No glBlitNamedFramebuffer, gotta be 3.3 compatible. */
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
endGLOnly();
#ifdef USE_DRAW_D3D_TEST_TRIANGLE
drawTestTriangle(m_device, m_device_ctx, m_cur_width, m_cur_height);
#endif
return GHOST_kSuccess;
}
ID3D11RenderTargetView *m_render_target;
ID3D11Texture2D *m_render_target_tex;
private:
void beginGLOnly()
{
wglDXLockObjectsNV(m_shared.device, 1, &m_shared.render_buf);
}
void endGLOnly()
{
wglDXUnlockObjectsNV(m_shared.device, 1, &m_shared.render_buf);
}
ID3D11Device *m_device;
ID3D11DeviceContext *m_device_ctx;
GLuint m_gl_render_buf;
unsigned int m_cur_width, m_cur_height;
bool m_is_initialized{false};
};
GHOST_SharedOpenGLResource *GHOST_ContextD3D::createSharedOpenGLResource(
unsigned int width, unsigned int height, ID3D11RenderTargetView *render_target)
{
if (!(WGL_NV_DX_interop && WGL_NV_DX_interop2)) {
fprintf(stderr,
"Error: Can't render OpenGL framebuffer using Direct3D. NV_DX_interop extension not "
"available.");
return nullptr;
}
GHOST_SharedOpenGLResource *shared_res = new GHOST_SharedOpenGLResource(
m_device, m_device_ctx, width, height, render_target);
return shared_res;
}
GHOST_SharedOpenGLResource *GHOST_ContextD3D::createSharedOpenGLResource(unsigned int width,
unsigned int height)
{
return createSharedOpenGLResource(width, height, nullptr);
}
void GHOST_ContextD3D::disposeSharedOpenGLResource(GHOST_SharedOpenGLResource *shared_res)
{
delete shared_res;
}
GHOST_TSuccess GHOST_ContextD3D::blitFromOpenGLContext(GHOST_SharedOpenGLResource *shared_res,
unsigned int width,
unsigned int height)
{
return shared_res->blit(width, height);
}
ID3D11Texture2D *GHOST_ContextD3D::getSharedTexture2D(GHOST_SharedOpenGLResource *shared_res)
{
return shared_res->m_render_target_tex;
}
#ifdef USE_DRAW_D3D_TEST_TRIANGLE
# pragma comment(lib, "D3DCompiler.lib")
const static std::string vertex_shader_str{
"struct VSOut {"
"float3 color : Color;"
"float4 pos : SV_POSITION;"
"};"
"VSOut main(float2 pos : Position, float3 color : Color)"
"{"
" VSOut vso;"
" vso.pos = float4(pos.x, pos.y, 0.0f, 1.0f);"
" vso.color = color;"
" return vso;"
"}"};
const static std::string pixel_shader_str{
" float4 main(float3 color : Color) : SV_TARGET"
"{"
" return float4(color, 1.0f);"
"}"};
# include <wrl/module.h>
# include <d3dcompiler.h>
static void drawTestTriangle(ID3D11Device *m_device,
ID3D11DeviceContext *m_device_ctx,
GHOST_TInt32 width,
GHOST_TInt32 height)
{
struct Vertex {
float x, y;
unsigned char r, g, b, a;
};
Vertex vertices[] = {
{0.0f, 0.5f, 255, 0, 0},
{0.5f, -0.5f, 0, 255, 0},
{-0.5f, -0.5f, 0, 0, 255},
};
const unsigned int stride = sizeof(Vertex);
const unsigned int offset = 0;
ID3D11Buffer *vertex_buffer;
D3D11_BUFFER_DESC buffer_desc{};
buffer_desc.Usage = D3D11_USAGE_DEFAULT;
buffer_desc.ByteWidth = sizeof(vertices);
buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA init_data{};
init_data.pSysMem = vertices;
m_device->CreateBuffer(&buffer_desc, &init_data, &vertex_buffer);
m_device_ctx->IASetVertexBuffers(0, 1, &vertex_buffer, &stride, &offset);
Microsoft::WRL::ComPtr<ID3DBlob> blob;
D3DCompile(pixel_shader_str.c_str(),
pixel_shader_str.length(),
NULL,
NULL,
NULL,
"main",
"ps_5_0",
0,
0,
&blob,
NULL);
Microsoft::WRL::ComPtr<ID3D11PixelShader> pixel_shader;
m_device->CreatePixelShader(
blob->GetBufferPointer(), blob->GetBufferSize(), NULL, &pixel_shader);
D3DCompile(vertex_shader_str.c_str(),
vertex_shader_str.length(),
NULL,
NULL,
NULL,
"main",
"vs_5_0",
0,
0,
&blob,
NULL);
Microsoft::WRL::ComPtr<ID3D11VertexShader> vertex_shader;
m_device->CreateVertexShader(
blob->GetBufferPointer(), blob->GetBufferSize(), NULL, &vertex_shader);
m_device_ctx->PSSetShader(pixel_shader.Get(), NULL, 0);
m_device_ctx->VSSetShader(vertex_shader.Get(), 0, 0);
Microsoft::WRL::ComPtr<ID3D11InputLayout> input_layout;
const D3D11_INPUT_ELEMENT_DESC input_desc[] = {
{"Position", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"Color", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0},
};
m_device->CreateInputLayout(input_desc,
(unsigned int)std::size(input_desc),
blob->GetBufferPointer(),
blob->GetBufferSize(),
&input_layout);
m_device_ctx->IASetInputLayout(input_layout.Get());
m_device_ctx->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
D3D11_VIEWPORT viewport = {};
viewport.Width = width;
viewport.Height = height;
viewport.MaxDepth = 1;
m_device_ctx->RSSetViewports(1, &viewport);
m_device_ctx->Draw(std::size(vertices), 0);
}
#endif

View File

@@ -0,0 +1,136 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup GHOST
*/
#ifndef __GHOST_CONTEXTD3D_H__
#define __GHOST_CONTEXTD3D_H__
#ifndef WIN32
# error WIN32 only!
#endif // WIN32
#include <D3D11.h>
#include "GHOST_Context.h"
class GHOST_ContextD3D : public GHOST_Context {
/* XR code needs low level graphics data to send to OpenXR. */
friend class GHOST_XrGraphicsBindingD3D;
public:
GHOST_ContextD3D(bool stereoVisual, HWND hWnd);
~GHOST_ContextD3D();
/**
* Swaps front and back buffers of a window.
* \return A boolean success indicator.
*/
GHOST_TSuccess swapBuffers();
/**
* Activates the drawing context of this window.
* \return A boolean success indicator.
*/
GHOST_TSuccess activateDrawingContext();
/**
* Release the drawing context of the calling thread.
* \return A boolean success indicator.
*/
GHOST_TSuccess releaseDrawingContext();
/**
* Call immediately after new to initialize. If this fails then immediately delete the object.
* \return Indication as to whether initialization has succeeded.
*/
GHOST_TSuccess initializeDrawingContext();
/**
* Updates the drawing context of this window. Needed
* whenever the window is changed.
* \return Indication of success.
*/
GHOST_TSuccess updateDrawingContext()
{
return GHOST_kFailure;
}
/**
* Checks if it is OK for a remove the native display
* \return Indication as to whether removal has succeeded.
*/
GHOST_TSuccess releaseNativeHandles();
/**
* Sets the swap interval for swapBuffers.
* \param interval The swap interval to use.
* \return A boolean success indicator.
*/
GHOST_TSuccess setSwapInterval(int /*interval*/)
{
return GHOST_kFailure;
}
/**
* Gets the current swap interval for swapBuffers.
* \param intervalOut Variable to store the swap interval if it can be read.
* \return Whether the swap interval can be read.
*/
GHOST_TSuccess getSwapInterval(int &)
{
return GHOST_kFailure;
}
/**
* Gets the OpenGL framebuffer associated with the OpenGL context
* \return The ID of an OpenGL framebuffer object.
*/
unsigned int getDefaultFramebuffer()
{
return 0;
}
class GHOST_SharedOpenGLResource *createSharedOpenGLResource(
unsigned int width, unsigned int height, ID3D11RenderTargetView *render_target);
class GHOST_SharedOpenGLResource *createSharedOpenGLResource(unsigned int width,
unsigned int height);
void disposeSharedOpenGLResource(class GHOST_SharedOpenGLResource *shared_res);
GHOST_TSuccess blitFromOpenGLContext(class GHOST_SharedOpenGLResource *shared_res,
unsigned int width,
unsigned int height);
ID3D11Texture2D *getSharedTexture2D(class GHOST_SharedOpenGLResource *shared_res);
bool isUpsideDown() const
{
return true;
}
private:
GHOST_TSuccess setupD3DLib();
static HMODULE s_d3d_lib;
static PFN_D3D11_CREATE_DEVICE s_D3D11CreateDeviceFn;
HWND m_hWnd;
ID3D11Device *m_device;
ID3D11DeviceContext *m_device_ctx;
};
#endif /* __GHOST_CONTEXTD3D_H__ */

View File

@@ -273,6 +273,7 @@ GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext()
m_window = (Window)glXCreatePbuffer(m_display, framebuffer_config[0], pbuffer_attribs);
}
m_fbconfig = framebuffer_config[0];
XFree(framebuffer_config);
}
}

View File

@@ -38,6 +38,9 @@
#endif
class GHOST_ContextGLX : public GHOST_Context {
/* XR code needs low level graphics data to send to OpenXR. */
friend class GHOST_XrGraphicsBindingOpenGL;
public:
/**
* Constructor.

View File

@@ -30,7 +30,8 @@
class GHOST_ContextNone : public GHOST_Context {
public:
GHOST_ContextNone(bool stereoVisual) : GHOST_Context(stereoVisual), m_swapInterval(1)
GHOST_ContextNone(bool stereoVisual)
: GHOST_Context(stereoVisual), m_swapInterval(1)
{
}

View File

@@ -35,6 +35,9 @@
#endif
class GHOST_ContextWGL : public GHOST_Context {
/* XR code needs low level graphics data to send to OpenXR. */
friend class GHOST_XrGraphicsBindingOpenGL;
public:
/**
* Constructor.

View File

@@ -121,6 +121,16 @@ GHOST_TSuccess GHOST_System::disposeWindow(GHOST_IWindow *window)
return success;
}
GHOST_IContext *GHOST_System::createOffscreenContext(GHOST_TDrawingContextType type)
{
switch (type) {
case GHOST_kDrawingContextTypeOpenGL:
return createOffscreenContext();
default:
return NULL;
}
}
bool GHOST_System::validWindow(GHOST_IWindow *window)
{
return m_windowManager->getWindowFound(window);

View File

@@ -119,6 +119,19 @@ class GHOST_System : public GHOST_ISystem {
*/
GHOST_TSuccess disposeWindow(GHOST_IWindow *window);
/**
* Create a new offscreen context.
* Never explicitly delete the context, use disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
virtual GHOST_IContext *createOffscreenContext() = 0;
/**
* Overload to allow requesting a different context type. By default only OpenGL is supported.
* However by explicitly overloading this a system may add support for others.
*/
GHOST_IContext *createOffscreenContext(GHOST_TDrawingContextType type);
/**
* Returns whether a window is valid.
* \param window Pointer to the window to be checked.

View File

@@ -21,6 +21,7 @@
* \ingroup GHOST
*/
#include "GHOST_ContextD3D.h"
#include "GHOST_SystemWin32.h"
#include "GHOST_EventDragnDrop.h"
@@ -406,6 +407,47 @@ GHOST_TSuccess GHOST_SystemWin32::disposeContext(GHOST_IContext *context)
return GHOST_kSuccess;
}
/**
* Create a new offscreen DirectX 11 context.
* Never explicitly delete the window, use #disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
GHOST_IContext *GHOST_SystemWin32::createOffscreenContextD3D()
{
GHOST_Context *context;
HWND wnd = CreateWindowA("STATIC",
"BlenderD3D",
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
0,
0,
64,
64,
NULL,
NULL,
GetModuleHandle(NULL),
NULL);
context = new GHOST_ContextD3D(false, wnd);
if (context->initializeDrawingContext() == GHOST_kFailure) {
delete context;
}
return context;
}
GHOST_IContext *GHOST_SystemWin32::createOffscreenContext(GHOST_TDrawingContextType type)
{
switch (type) {
case GHOST_kDrawingContextTypeOpenGL:
return createOffscreenContext();
case GHOST_kDrawingContextTypeD3D:
return createOffscreenContextD3D();
default:
return NULL;
}
}
bool GHOST_SystemWin32::processEvents(bool waitForEvent)
{
MSG msg;

View File

@@ -133,6 +133,13 @@ class GHOST_SystemWin32 : public GHOST_System {
*/
GHOST_IContext *createOffscreenContext();
/**
* Create a new offscreen context.
* Never explicitly delete the window, use disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
GHOST_IContext *createOffscreenContext(GHOST_TDrawingContextType type);
/**
* Dispose of a context.
* \param context Pointer to the context to be disposed.
@@ -235,6 +242,13 @@ class GHOST_SystemWin32 : public GHOST_System {
*/
GHOST_TSuccess exit();
/**
* Create a new offscreen DirectX context.
* Never explicitly delete the window, use disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
GHOST_IContext *createOffscreenContextD3D();
/**
* Converts raw WIN32 key codes from the wndproc to GHOST keys.
* \param vKey The virtual key from hardKey

View File

@@ -114,6 +114,11 @@ unsigned int GHOST_Window::getDefaultFramebuffer()
return (m_context) ? m_context->getDefaultFramebuffer() : 0;
}
bool GHOST_Window::isUpsideDown() const
{
return m_context->isUpsideDown();
}
GHOST_TSuccess GHOST_Window::activateDrawingContext()
{
return m_context->activateDrawingContext();

View File

@@ -279,6 +279,11 @@ class GHOST_Window : public GHOST_IWindow {
m_userData = userData;
}
/**
* Returns if the window is rendered upside down compared to OpenGL.
*/
bool isUpsideDown() const;
float getNativePixelSize(void)
{
if (m_nativePixelSize > 0.0f)

View File

@@ -23,6 +23,7 @@
#define _USE_MATH_DEFINES
#include "GHOST_ContextD3D.h"
#include "GHOST_WindowWin32.h"
#include "GHOST_SystemWin32.h"
#include "GHOST_DropTargetWin32.h"
@@ -724,6 +725,19 @@ GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType ty
# error // must specify either core or compat at build time
#endif
}
else if (type == GHOST_kDrawingContextTypeD3D) {
GHOST_Context *context;
context = new GHOST_ContextD3D(false, m_hWnd);
if (context->initializeDrawingContext()) {
return context;
}
else {
delete context;
}
return context;
}
return NULL;
}

View File

@@ -0,0 +1,59 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ***** END GPL LICENSE BLOCK *****
set(INC
.
)
set(INC_SYS
${OPENXR_SDK_INCLUDE_DIR}
)
set(SRC
intern/VAMR.cc
intern/CAPI.cc
intern/Context.cc
intern/Event.cc
intern/GraphicsBinding.cc
intern/Session.cc
VAMR_capi.h
VAMR_IContext.h
VAMR_Types.h
intern/Context.h
intern/Exception.h
intern/utils.h
intern/IGraphicsBinding.h
intern/openxr_includes.h
intern/Session.h
)
if(WIN32)
list(APPEND LIB
shlwapi
)
elseif(WITH_X11)
add_definitions(-DWITH_X11)
endif()
add_definitions(${GL_DEFINITIONS})
include(xr_platform_defines)
blender_add_lib(bf_intern_vamr "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@@ -0,0 +1,46 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*/
#ifndef __VAMR_ICONTEXT_H__
#define __VAMR_ICONTEXT_H__
#include "VAMR_Types.h"
namespace VAMR {
class IContext {
public:
virtual ~IContext() = default;
virtual void startSession(const VAMR_SessionBeginInfo *begin_info) = 0;
virtual void endSession() = 0;
virtual bool isSessionRunning() const = 0;
virtual void drawSessionViews(void *draw_customdata) = 0;
virtual void dispatchErrorMessage(const class Exception *) const = 0;
virtual void setGraphicsContextBindFuncs(VAMR_GraphicsContextBindFn bind_fn,
VAMR_GraphicsContextUnbindFn unbind_fn) = 0;
virtual void setDrawViewFunc(VAMR_DrawViewFn draw_view_fn) = 0;
};
} // namespace VAMR
#endif // __VAMR_ICONTEXT_H__

103
intern/vamr/VAMR_Types.h Normal file
View File

@@ -0,0 +1,103 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*/
#ifndef __VAMR_TYPES_H__
#define __VAMR_TYPES_H__
typedef struct VAMR_Context__ {
int dummy;
} * VAMR_ContextHandle;
typedef enum { VAMR_Failure = 0, VAMR_Success } VAMR_TSuccess;
/**
* The XR view (i.e. the OpenXR runtime) may require a different graphics library than OpenGL. An
* offscreen texture of the viewport will then be drawn into using OpenGL, but the final texture
* draw call will happen through another lib (say DirectX).
*
* This enum defines the possible graphics bindings to attempt to enable.
*/
typedef enum {
VAMR_GraphicsBindingTypeUnknown = 0,
VAMR_GraphicsBindingTypeOpenGL,
#ifdef WIN32
VAMR_GraphicsBindingTypeD3D11,
#endif
/* For later */
// VAMR_GraphicsBindingVulkan,
} VAMR_GraphicsBindingType;
/* An array of VAMR_GraphicsBindingType items defining the candidate bindings to use. The first
* available candidate will be chosen, so order defines priority. */
typedef const VAMR_GraphicsBindingType *VAMR_GraphicsBindingCandidates;
typedef struct {
float position[3];
/* Blender convention (w, x, y, z) */
float orientation_quat[4];
} VAMR_Pose;
enum {
VAMR_ContextDebug = (1 << 0),
VAMR_ContextDebugTime = (1 << 1),
};
typedef struct {
const VAMR_GraphicsBindingCandidates gpu_binding_candidates;
unsigned int gpu_binding_candidates_count;
unsigned int context_flag;
} VAMR_ContextCreateInfo;
typedef struct {
VAMR_Pose base_pose;
} VAMR_SessionBeginInfo;
typedef struct {
int ofsx, ofsy;
int width, height;
VAMR_Pose pose;
struct {
float angle_left, angle_right;
float angle_up, angle_down;
} fov;
/** Set if the buffer should be submitted with a srgb transfer applied. */
char expects_srgb_buffer;
} VAMR_DrawViewInfo;
typedef struct {
const char *user_message;
/** File path and line number the error was found at. */
const char *source_location;
void *customdata;
} VAMR_Error;
typedef void (*VAMR_ErrorHandlerFn)(const VAMR_Error *);
typedef void *(*VAMR_GraphicsContextBindFn)(VAMR_GraphicsBindingType graphics_lib);
typedef void (*VAMR_GraphicsContextUnbindFn)(VAMR_GraphicsBindingType graphics_lib,
void *graphics_context);
typedef void (*VAMR_DrawViewFn)(const VAMR_DrawViewInfo *draw_view, void *customdata);
#endif // __VAMR_TYPES_H__

60
intern/vamr/VAMR_capi.h Normal file
View File

@@ -0,0 +1,60 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*/
#ifndef __VAMR_CAPI_H__
#define __VAMR_CAPI_H__
#include "VAMR_Types.h"
#ifdef __cplusplus
extern "C" {
#endif
/* xr-context */
/**
* Set a custom callback to be executed whenever an error occurs. Should be set before calling
* #VAMR_ContextCreate().
*/
void VAMR_ErrorHandler(VAMR_ErrorHandlerFn handler_fn, void *customdata);
VAMR_ContextHandle VAMR_ContextCreate(const VAMR_ContextCreateInfo *create_info);
void VAMR_ContextDestroy(VAMR_ContextHandle xr_context);
void VAMR_GraphicsContextBindFuncs(VAMR_ContextHandle xr_context,
VAMR_GraphicsContextBindFn bind_fn,
VAMR_GraphicsContextUnbindFn unbind_fn);
void VAMR_DrawViewFunc(VAMR_ContextHandle xr_context, VAMR_DrawViewFn draw_view_fn);
/* sessions */
int VAMR_SessionIsRunning(const VAMR_ContextHandle xr_context);
void VAMR_SessionStart(VAMR_ContextHandle xr_context, const VAMR_SessionBeginInfo *begin_info);
void VAMR_SessionEnd(VAMR_ContextHandle xr_context);
void VAMR_SessionDrawViews(VAMR_ContextHandle xr_context, void *customdata);
/* events */
VAMR_TSuccess VAMR_EventsHandle(VAMR_ContextHandle xr_context);
#ifdef __cplusplus
}
#endif
#endif // __VAMR_CAPI_H__

View File

@@ -0,0 +1,81 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*/
#include "VAMR_Types.h"
#include "VAMR_capi.h"
#include "VAMR_IContext.h"
#include "Exception.h"
#define VAMR_CAPI_CALL(call, ctx) \
try { \
call; \
} \
catch (VAMR::Exception & e) { \
(ctx)->dispatchErrorMessage(&e); \
}
#define VAMR_CAPI_CALL_RET(call, ctx) \
try { \
return call; \
} \
catch (VAMR::Exception & e) { \
(ctx)->dispatchErrorMessage(&e); \
}
void VAMR_SessionStart(VAMR_ContextHandle xr_contexthandle,
const VAMR_SessionBeginInfo *begin_info)
{
VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle;
VAMR_CAPI_CALL(xr_context->startSession(begin_info), xr_context);
}
void VAMR_SessionEnd(VAMR_ContextHandle xr_contexthandle)
{
VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle;
VAMR_CAPI_CALL(xr_context->endSession(), xr_context);
}
int VAMR_SessionIsRunning(const VAMR_ContextHandle xr_contexthandle)
{
const VAMR::IContext *xr_context = (const VAMR::IContext *)xr_contexthandle;
VAMR_CAPI_CALL_RET(xr_context->isSessionRunning(), xr_context);
return 0; // Only reached if exception is thrown.
}
void VAMR_SessionDrawViews(VAMR_ContextHandle xr_contexthandle, void *draw_customdata)
{
VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle;
VAMR_CAPI_CALL(xr_context->drawSessionViews(draw_customdata), xr_context);
}
void VAMR_GraphicsContextBindFuncs(VAMR_ContextHandle xr_contexthandle,
VAMR_GraphicsContextBindFn bind_fn,
VAMR_GraphicsContextUnbindFn unbind_fn)
{
VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle;
VAMR_CAPI_CALL(xr_context->setGraphicsContextBindFuncs(bind_fn, unbind_fn), xr_context);
}
void VAMR_DrawViewFunc(VAMR_ContextHandle xr_contexthandle, VAMR_DrawViewFn draw_view_fn)
{
VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle;
VAMR_CAPI_CALL(xr_context->setDrawViewFunc(draw_view_fn), xr_context);
}

View File

@@ -0,0 +1,555 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*
* Abstraction for XR (VR, AR, MR, ..) access via OpenXR.
*/
#include <cassert>
#include <sstream>
#include <string>
#include "VAMR_Types.h"
#include "openxr_includes.h"
#include "Context.h"
#include "Exception.h"
#include "Session.h"
#include "utils.h"
namespace VAMR {
struct OpenXRInstanceData {
XrInstance instance{XR_NULL_HANDLE};
XrInstanceProperties instance_properties;
std::vector<XrExtensionProperties> extensions;
std::vector<XrApiLayerProperties> layers;
static PFN_xrCreateDebugUtilsMessengerEXT s_xrCreateDebugUtilsMessengerEXT_fn;
static PFN_xrDestroyDebugUtilsMessengerEXT s_xrDestroyDebugUtilsMessengerEXT_fn;
XrDebugUtilsMessengerEXT debug_messenger{XR_NULL_HANDLE};
};
PFN_xrCreateDebugUtilsMessengerEXT OpenXRInstanceData::s_xrCreateDebugUtilsMessengerEXT_fn =
nullptr;
PFN_xrDestroyDebugUtilsMessengerEXT OpenXRInstanceData::s_xrDestroyDebugUtilsMessengerEXT_fn =
nullptr;
VAMR_ErrorHandlerFn Context::s_error_handler = nullptr;
void *Context::s_error_handler_customdata = nullptr;
/* -------------------------------------------------------------------- */
/** \name Create, Initialize and Destruct
*
* \{ */
Context::Context(const VAMR_ContextCreateInfo *create_info)
: m_oxr(new OpenXRInstanceData()),
m_debug(create_info->context_flag & VAMR_ContextDebug),
m_debug_time(create_info->context_flag & VAMR_ContextDebugTime)
{
}
Context::~Context()
{
/* Destroy session data first. Otherwise xrDestroyInstance will implicitly do it, before the
* session had a chance to do so explicitly. */
m_session = nullptr;
if (m_oxr->debug_messenger != XR_NULL_HANDLE) {
assert(m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn != nullptr);
m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn(m_oxr->debug_messenger);
}
if (m_oxr->instance != XR_NULL_HANDLE) {
CHECK_XR_ASSERT(xrDestroyInstance(m_oxr->instance));
m_oxr->instance = XR_NULL_HANDLE;
}
}
void Context::initialize(const VAMR_ContextCreateInfo *create_info)
{
enumerateApiLayers();
enumerateExtensions();
XR_DEBUG_ONLY_CALL(this, printAvailableAPILayersAndExtensionsInfo());
m_gpu_binding_type = determineGraphicsBindingTypeToEnable(create_info);
assert(m_oxr->instance == XR_NULL_HANDLE);
createOpenXRInstance();
storeInstanceProperties();
printInstanceInfo();
XR_DEBUG_ONLY_CALL(this, initDebugMessenger());
}
void Context::createOpenXRInstance()
{
XrInstanceCreateInfo create_info{XR_TYPE_INSTANCE_CREATE_INFO};
std::string("Blender").copy(create_info.applicationInfo.applicationName,
XR_MAX_APPLICATION_NAME_SIZE);
create_info.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
getAPILayersToEnable(m_enabled_layers);
getExtensionsToEnable(m_enabled_extensions);
create_info.enabledApiLayerCount = m_enabled_layers.size();
create_info.enabledApiLayerNames = m_enabled_layers.data();
create_info.enabledExtensionCount = m_enabled_extensions.size();
create_info.enabledExtensionNames = m_enabled_extensions.data();
XR_DEBUG_ONLY_CALL(this, printExtensionsAndAPILayersToEnable());
CHECK_XR(xrCreateInstance(&create_info, &m_oxr->instance),
"Failed to connect to an OpenXR runtime.");
}
void Context::storeInstanceProperties()
{
const std::map<std::string, OpenXRRuntimeID> runtime_map{
{"Monado(XRT) by Collabora et al", OPENXR_RUNTIME_MONADO},
{"Oculus", OPENXR_RUNTIME_OCULUS},
{"Windows Mixed Reality Runtime", OPENXR_RUNTIME_WMR}};
decltype(runtime_map)::const_iterator runtime_map_iter;
m_oxr->instance_properties.type = XR_TYPE_INSTANCE_PROPERTIES;
CHECK_XR(xrGetInstanceProperties(m_oxr->instance, &m_oxr->instance_properties),
"Failed to get OpenXR runtime information. Do you have an active runtime set up?");
runtime_map_iter = runtime_map.find(m_oxr->instance_properties.runtimeName);
if (runtime_map_iter != runtime_map.end()) {
m_runtime_id = runtime_map_iter->second;
}
}
/** \} */ /* Create, Initialize and Destruct */
/* -------------------------------------------------------------------- */
/** \name Debug Printing
*
* \{ */
void Context::printInstanceInfo()
{
assert(m_oxr->instance != XR_NULL_HANDLE);
printf("Connected to OpenXR runtime: %s (Version %u.%u.%u)\n",
m_oxr->instance_properties.runtimeName,
XR_VERSION_MAJOR(m_oxr->instance_properties.runtimeVersion),
XR_VERSION_MINOR(m_oxr->instance_properties.runtimeVersion),
XR_VERSION_PATCH(m_oxr->instance_properties.runtimeVersion));
}
void Context::printAvailableAPILayersAndExtensionsInfo()
{
puts("Available OpenXR API-layers/extensions:");
for (XrApiLayerProperties &layer_info : m_oxr->layers) {
printf("Layer: %s\n", layer_info.layerName);
}
for (XrExtensionProperties &ext_info : m_oxr->extensions) {
printf("Extension: %s\n", ext_info.extensionName);
}
}
void Context::printExtensionsAndAPILayersToEnable()
{
for (const char *layer_name : m_enabled_layers) {
printf("Enabling OpenXR API-Layer: %s\n", layer_name);
}
for (const char *ext_name : m_enabled_extensions) {
printf("Enabling OpenXR Extension: %s\n", ext_name);
}
}
static XrBool32 debug_messenger_func(XrDebugUtilsMessageSeverityFlagsEXT /*messageSeverity*/,
XrDebugUtilsMessageTypeFlagsEXT /*messageTypes*/,
const XrDebugUtilsMessengerCallbackDataEXT *callbackData,
void * /*userData*/)
{
puts("OpenXR Debug Message:");
puts(callbackData->message);
return XR_FALSE; // OpenXR spec suggests always returning false.
}
void Context::initDebugMessenger()
{
XrDebugUtilsMessengerCreateInfoEXT create_info{XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT};
/* Extension functions need to be obtained through xrGetInstanceProcAddr */
if (XR_FAILED(xrGetInstanceProcAddr(
m_oxr->instance,
"xrCreateDebugUtilsMessengerEXT",
(PFN_xrVoidFunction *)&m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn)) ||
XR_FAILED(xrGetInstanceProcAddr(
m_oxr->instance,
"xrDestroyDebugUtilsMessengerEXT",
(PFN_xrVoidFunction *)&m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn))) {
m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn = nullptr;
m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn = nullptr;
fprintf(stderr,
"Could not use XR_EXT_debug_utils to enable debug prints. Not a fatal error, "
"continuing without the messenger.\n");
return;
}
create_info.messageSeverities = XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
create_info.messageTypes = XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
create_info.userCallback = debug_messenger_func;
if (XR_FAILED(m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn(
m_oxr->instance, &create_info, &m_oxr->debug_messenger))) {
fprintf(stderr,
"Failed to create OpenXR debug messenger. Not a fatal error, continuing without the "
"messenger.\n");
return;
}
}
/** \} */ /* Debug Printing */
/* -------------------------------------------------------------------- */
/** \name Error handling
*
* \{ */
void Context::dispatchErrorMessage(const Exception *exception) const
{
std::ostringstream stream_err_location;
std::string str_err_location;
VAMR_Error error;
stream_err_location << exception->m_file << ":" << exception->m_line;
str_err_location = stream_err_location.str();
error.user_message = exception->m_msg;
error.source_location = str_err_location.c_str();
error.customdata = s_error_handler_customdata;
XR_DEBUG_ONLY_CALL(this,
fprintf(stderr,
"Error: \t%s\n\tOpenXR error value: %i\n\tSource: (%s)\n",
error.user_message,
exception->m_res,
error.source_location));
/* Potentially destroys VAMR-context */
s_error_handler(&error);
}
void Context::setErrorHandler(VAMR_ErrorHandlerFn handler_fn, void *customdata)
{
s_error_handler = handler_fn;
s_error_handler_customdata = customdata;
}
/** \} */ /* Error handling */
/* -------------------------------------------------------------------- */
/** \name OpenXR API-Layers and Extensions
*
* \{ */
/**
* \param layer_name May be NULL for extensions not belonging to a specific layer.
*/
void Context::enumerateExtensionsEx(std::vector<XrExtensionProperties> &extensions,
const char *layer_name)
{
uint32_t extension_count = 0;
/* Get count for array creation/init first. */
CHECK_XR(xrEnumerateInstanceExtensionProperties(layer_name, 0, &extension_count, nullptr),
"Failed to query OpenXR runtime information. Do you have an active runtime set up?");
if (extension_count == 0) {
/* Extensions are optional, can successfully exit. */
return;
}
for (uint32_t i = 0; i < extension_count; i++) {
XrExtensionProperties ext{XR_TYPE_EXTENSION_PROPERTIES};
extensions.push_back(ext);
}
/* Actually get the extensions. */
CHECK_XR(xrEnumerateInstanceExtensionProperties(
layer_name, extension_count, &extension_count, extensions.data()),
"Failed to query OpenXR runtime information. Do you have an active runtime set up?");
}
void Context::enumerateExtensions()
{
enumerateExtensionsEx(m_oxr->extensions, nullptr);
}
void Context::enumerateApiLayers()
{
uint32_t layer_count = 0;
/* Get count for array creation/init first. */
CHECK_XR(xrEnumerateApiLayerProperties(0, &layer_count, nullptr),
"Failed to query OpenXR runtime information. Do you have an active runtime set up?");
if (layer_count == 0) {
/* Layers are optional, can safely exit. */
return;
}
m_oxr->layers = std::vector<XrApiLayerProperties>(layer_count);
for (XrApiLayerProperties &layer : m_oxr->layers) {
layer.type = XR_TYPE_API_LAYER_PROPERTIES;
}
/* Actually get the layers. */
CHECK_XR(xrEnumerateApiLayerProperties(layer_count, &layer_count, m_oxr->layers.data()),
"Failed to query OpenXR runtime information. Do you have an active runtime set up?");
for (XrApiLayerProperties &layer : m_oxr->layers) {
/* Each layer may have own extensions */
enumerateExtensionsEx(m_oxr->extensions, layer.layerName);
}
}
static bool openxr_layer_is_available(const std::vector<XrApiLayerProperties> layers_info,
const std::string &layer_name)
{
for (const XrApiLayerProperties &layer_info : layers_info) {
if (layer_info.layerName == layer_name) {
return true;
}
}
return false;
}
static bool openxr_extension_is_available(const std::vector<XrExtensionProperties> extensions_info,
const std::string &extension_name)
{
for (const XrExtensionProperties &ext_info : extensions_info) {
if (ext_info.extensionName == extension_name) {
return true;
}
}
return false;
}
/**
* Gather an array of names for the API-layers to enable.
*/
void Context::getAPILayersToEnable(std::vector<const char *> &r_ext_names)
{
static std::vector<std::string> try_layers;
try_layers.clear();
XR_DEBUG_ONLY_CALL(this, try_layers.push_back("XR_APILAYER_LUNARG_core_validation"));
r_ext_names.reserve(try_layers.size());
for (const std::string &layer : try_layers) {
if (openxr_layer_is_available(m_oxr->layers, layer)) {
r_ext_names.push_back(layer.c_str());
}
}
}
static const char *openxr_ext_name_from_wm_gpu_binding(VAMR_GraphicsBindingType binding)
{
switch (binding) {
case VAMR_GraphicsBindingTypeOpenGL:
return XR_KHR_OPENGL_ENABLE_EXTENSION_NAME;
#ifdef WIN32
case VAMR_GraphicsBindingTypeD3D11:
return XR_KHR_D3D11_ENABLE_EXTENSION_NAME;
#endif
case VAMR_GraphicsBindingTypeUnknown:
assert(false);
return nullptr;
}
return nullptr;
}
/**
* Gather an array of names for the extensions to enable.
*/
void Context::getExtensionsToEnable(std::vector<const char *> &r_ext_names)
{
assert(m_gpu_binding_type != VAMR_GraphicsBindingTypeUnknown);
const char *gpu_binding = openxr_ext_name_from_wm_gpu_binding(m_gpu_binding_type);
static std::vector<std::string> try_ext;
try_ext.clear();
/* Try enabling debug extension */
#ifndef WIN32
XR_DEBUG_ONLY_CALL(this, try_ext.push_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME));
#endif
r_ext_names.reserve(try_ext.size() + 1); /* + 1 for graphics binding extension. */
/* Add graphics binding extension. */
assert(gpu_binding);
assert(openxr_extension_is_available(m_oxr->extensions, gpu_binding));
r_ext_names.push_back(gpu_binding);
for (const std::string &ext : try_ext) {
if (openxr_extension_is_available(m_oxr->extensions, ext)) {
r_ext_names.push_back(ext.c_str());
}
}
}
/**
* Decide which graphics binding extension to use based on
* #VAMR_ContextCreateInfo.gpu_binding_candidates and available extensions.
*/
VAMR_GraphicsBindingType Context::determineGraphicsBindingTypeToEnable(
const VAMR_ContextCreateInfo *create_info)
{
assert(create_info->gpu_binding_candidates != NULL);
assert(create_info->gpu_binding_candidates_count > 0);
for (uint32_t i = 0; i < create_info->gpu_binding_candidates_count; i++) {
assert(create_info->gpu_binding_candidates[i] != VAMR_GraphicsBindingTypeUnknown);
const char *ext_name = openxr_ext_name_from_wm_gpu_binding(
create_info->gpu_binding_candidates[i]);
if (openxr_extension_is_available(m_oxr->extensions, ext_name)) {
return create_info->gpu_binding_candidates[i];
}
}
return VAMR_GraphicsBindingTypeUnknown;
}
/** \} */ /* OpenXR API-Layers and Extensions */
/* -------------------------------------------------------------------- */
/** \name Session management
*
* Manage session lifetime and delegate public calls to #VAMR::Session.
* \{ */
void Context::startSession(const VAMR_SessionBeginInfo *begin_info)
{
if (m_session == nullptr) {
m_session = std::unique_ptr<Session>(new Session(this));
}
m_session->start(begin_info);
}
void Context::endSession()
{
m_session->requestEnd();
}
bool Context::isSessionRunning() const
{
return m_session && m_session->isRunning();
}
void Context::drawSessionViews(void *draw_customdata)
{
m_session->draw(draw_customdata);
}
/**
* Delegates event to session, allowing context to destruct the session if needed.
*/
void Context::handleSessionStateChange(const XrEventDataSessionStateChanged *lifecycle)
{
if (m_session && m_session->handleStateChangeEvent(lifecycle) == Session::SESSION_DESTROY) {
m_session = nullptr;
}
}
/** \} */ /* Session Management */
/* -------------------------------------------------------------------- */
/** \name Public Accessors and Mutators
*
* Public as in, exposed in the VAMR API.
* \{ */
/**
* Set context for binding and unbinding a graphics context for a session. The binding callback
* may create a new context thereby. In fact that's the sole reason for this callback approach to
* binding. Just make sure to have an unbind function set that properly destructs.
*
* \param bind_fn Function to retrieve (possibly create) a graphics context.
* \param unbind_fn Function to release (possibly free) a graphics context.
*/
void Context::setGraphicsContextBindFuncs(VAMR_GraphicsContextBindFn bind_fn,
VAMR_GraphicsContextUnbindFn unbind_fn)
{
if (m_session) {
m_session->unbindGraphicsContext();
}
m_custom_funcs.gpu_ctx_bind_fn = bind_fn;
m_custom_funcs.gpu_ctx_unbind_fn = unbind_fn;
}
void Context::setDrawViewFunc(VAMR_DrawViewFn draw_view_fn)
{
m_custom_funcs.draw_view_fn = draw_view_fn;
}
/** \} */ /* Public Accessors and Mutators */
/* -------------------------------------------------------------------- */
/** \name VAMR Internal Accessors and Mutators
*
* \{ */
OpenXRRuntimeID Context::getOpenXRRuntimeID() const
{
return m_runtime_id;
}
const CustomFuncs *Context::getCustomFuncs() const
{
return &m_custom_funcs;
}
VAMR_GraphicsBindingType Context::getGraphicsBindingType() const
{
return m_gpu_binding_type;
}
XrInstance Context::getInstance() const
{
return m_oxr->instance;
}
bool Context::isDebugMode() const
{
return m_debug;
}
bool Context::isDebugTimeMode() const
{
return m_debug_time;
}
/** \} */ /* VAMR Internal Accessors and Mutators */
} // namespace VAMR

View File

@@ -0,0 +1,130 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*/
#ifndef __CONTEXT_H__
#define __CONTEXT_H__
#include <memory>
#include <vector>
#include "VAMR_IContext.h"
namespace VAMR {
struct CustomFuncs {
/** Function to retrieve (possibly create) a graphics context */
VAMR_GraphicsContextBindFn gpu_ctx_bind_fn{nullptr};
/** Function to release (possibly free) a graphics context */
VAMR_GraphicsContextUnbindFn gpu_ctx_unbind_fn{nullptr};
/** Custom per-view draw function for Blender side drawing. */
VAMR_DrawViewFn draw_view_fn{nullptr};
};
/**
* In some occasions, runtime specific handling is needed, e.g. to work around runtime bugs.
*/
enum OpenXRRuntimeID {
OPENXR_RUNTIME_MONADO,
OPENXR_RUNTIME_OCULUS,
OPENXR_RUNTIME_WMR, /* Windows Mixed Reality */
OPENXR_RUNTIME_UNKNOWN
};
/**
* \brief Main VAMR container to manage OpenXR through.
*
* Creating a context using #VAMR_ContextCreate involves dynamically connecting to the OpenXR
* runtime, likely reading the OS OpenXR configuration (i.e. active_runtime.json). So this is
* something that should better be done using lazy-initialization.
*/
class Context : public VAMR::IContext {
public:
Context(const VAMR_ContextCreateInfo *create_info);
~Context();
void initialize(const VAMR_ContextCreateInfo *create_info);
void startSession(const VAMR_SessionBeginInfo *begin_info) override;
void endSession() override;
bool isSessionRunning() const override;
void drawSessionViews(void *draw_customdata) override;
static void setErrorHandler(VAMR_ErrorHandlerFn handler_fn, void *customdata);
void dispatchErrorMessage(const class Exception *exception) const override;
void setGraphicsContextBindFuncs(VAMR_GraphicsContextBindFn bind_fn,
VAMR_GraphicsContextUnbindFn unbind_fn) override;
void setDrawViewFunc(VAMR_DrawViewFn draw_view_fn) override;
void handleSessionStateChange(const XrEventDataSessionStateChanged *lifecycle);
OpenXRRuntimeID getOpenXRRuntimeID() const;
const CustomFuncs *getCustomFuncs() const;
VAMR_GraphicsBindingType getGraphicsBindingType() const;
XrInstance getInstance() const;
bool isDebugMode() const;
bool isDebugTimeMode() const;
private:
std::unique_ptr<struct OpenXRInstanceData> m_oxr;
OpenXRRuntimeID m_runtime_id{OPENXR_RUNTIME_UNKNOWN};
/* The active VAMR XR Session. Null while no session runs. */
std::unique_ptr<class Session> m_session;
/** Active graphics binding type. */
VAMR_GraphicsBindingType m_gpu_binding_type{VAMR_GraphicsBindingTypeUnknown};
/** Names of enabled extensions */
std::vector<const char *> m_enabled_extensions;
/** Names of enabled API-layers */
std::vector<const char *> m_enabled_layers;
static VAMR_ErrorHandlerFn s_error_handler;
static void *s_error_handler_customdata;
CustomFuncs m_custom_funcs;
/** Enable debug message prints and OpenXR API validation layers */
bool m_debug{false};
bool m_debug_time{false};
void createOpenXRInstance();
void storeInstanceProperties();
void initDebugMessenger();
void printInstanceInfo();
void printAvailableAPILayersAndExtensionsInfo();
void printExtensionsAndAPILayersToEnable();
void enumerateApiLayers();
void enumerateExtensions();
void enumerateExtensionsEx(std::vector<XrExtensionProperties> &extensions,
const char *layer_name);
void getAPILayersToEnable(std::vector<const char *> &r_ext_names);
void getExtensionsToEnable(std::vector<const char *> &r_ext_names);
VAMR_GraphicsBindingType determineGraphicsBindingTypeToEnable(
const VAMR_ContextCreateInfo *create_info);
};
} // namespace VAMR
#endif // __CONTEXT_H__

View File

@@ -0,0 +1,70 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*/
#include <iostream>
#include "VAMR_Types.h"
#include "VAMR_capi.h"
#include "openxr_includes.h"
#include "Context.h"
#include "utils.h"
namespace VAMR {
static bool VAMR_EventPollNext(XrInstance instance, XrEventDataBuffer &r_event_data)
{
/* (Re-)initialize as required by specification */
r_event_data.type = XR_TYPE_EVENT_DATA_BUFFER;
r_event_data.next = nullptr;
return (xrPollEvent(instance, &r_event_data) == XR_SUCCESS);
}
::VAMR_TSuccess VAMR_EventsHandle(Context *xr_context)
{
XrEventDataBuffer event_buffer; /* structure big enought to hold all possible events */
if (xr_context == NULL) {
return VAMR_Failure;
}
while (VAMR_EventPollNext(xr_context->getInstance(), event_buffer)) {
XrEventDataBaseHeader *event = (XrEventDataBaseHeader *)&event_buffer; /* base event struct */
switch (event->type) {
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
xr_context->handleSessionStateChange((XrEventDataSessionStateChanged *)event);
return VAMR_Success;
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
VAMR_ContextDestroy((VAMR_ContextHandle)xr_context);
return VAMR_Success;
default:
XR_DEBUG_PRINTF(xr_context, "Unhandled event: %i\n", event->type);
return VAMR_Failure;
}
}
return VAMR_Failure;
}
} // namespace VAMR

View File

@@ -0,0 +1,55 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*/
#ifndef __EXCEPTION_H__
#define __EXCEPTION_H__
#include <exception>
namespace VAMR {
/**
* For now just a general Exception type (note that it's in namespace VAMR, so name shouldn't cause
* conflicts).
*/
class Exception : public std::exception {
friend class Context;
public:
Exception(const char *msg, const char *file, int line, int res = 0)
: std::exception(), m_msg(msg), m_file(file), m_line(line), m_res(res)
{
}
const char *what() const noexcept override
{
return m_msg;
}
private:
const char *m_msg;
const char *m_file;
const int m_line;
int m_res;
};
} // namespace VAMR
#endif // __EXCEPTION_H__

View File

@@ -0,0 +1,308 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*/
#include <algorithm>
#include <list>
#include <sstream>
#if 0
# if defined(WITH_X11)
# include "GHOST_ContextGLX.h"
# elif defined(WIN32)
# include "GHOST_ContextWGL.h"
# include "GHOST_ContextD3D.h"
# endif
#endif
#include "VAMR_capi.h"
#include "IGraphicsBinding.h"
#include "utils.h"
namespace VAMR {
static bool choose_swapchain_format_from_candidates(std::vector<int64_t> gpu_binding_formats,
std::vector<int64_t> runtime_formats,
int64_t *r_result)
{
if (gpu_binding_formats.empty()) {
return false;
}
auto res = std::find_first_of(gpu_binding_formats.begin(),
gpu_binding_formats.end(),
runtime_formats.begin(),
runtime_formats.end());
if (res == gpu_binding_formats.end()) {
return false;
}
*r_result = *res;
return true;
}
class GraphicsBindingOpenGL : public IGraphicsBinding {
public:
~GraphicsBindingOpenGL()
{
if (m_fbo != 0) {
glDeleteFramebuffers(1, &m_fbo);
}
}
bool checkVersionRequirements(GHOST_Context *ghost_ctx,
XrInstance instance,
XrSystemId system_id,
std::string *r_requirement_info) const override
{
#if 0
# if defined(WITH_X11)
GHOST_ContextGLX *ctx_gl = static_cast<GHOST_ContextGLX *>(ghost_ctx);
# else
GHOST_ContextWGL *ctx_gl = static_cast<GHOST_ContextWGL *>(ghost_ctx);
# endif
XrGraphicsRequirementsOpenGLKHR gpu_requirements{XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR};
const XrVersion gl_version = XR_MAKE_VERSION(
ctx_gl->m_contextMajorVersion, ctx_gl->m_contextMinorVersion, 0);
xrGetOpenGLGraphicsRequirementsKHR(instance, system_id, &gpu_requirements);
if (r_requirement_info) {
std::ostringstream strstream;
strstream << "Min OpenGL version "
<< XR_VERSION_MAJOR(gpu_requirements.minApiVersionSupported) << "."
<< XR_VERSION_MINOR(gpu_requirements.minApiVersionSupported) << std::endl;
strstream << "Max OpenGL version "
<< XR_VERSION_MAJOR(gpu_requirements.maxApiVersionSupported) << "."
<< XR_VERSION_MINOR(gpu_requirements.maxApiVersionSupported) << std::endl;
*r_requirement_info = strstream.str();
}
return (gl_version >= gpu_requirements.minApiVersionSupported) &&
(gl_version <= gpu_requirements.maxApiVersionSupported);
#endif
return true;
}
void initFromGhostContext(GHOST_Context *ghost_ctx) override
{
#if 0
# if defined(WITH_X11)
GHOST_ContextGLX *ctx_glx = static_cast<GHOST_ContextGLX *>(ghost_ctx);
XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx->m_display, ctx_glx->m_fbconfig);
oxr_binding.glx.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
oxr_binding.glx.xDisplay = ctx_glx->m_display;
oxr_binding.glx.glxFBConfig = ctx_glx->m_fbconfig;
oxr_binding.glx.glxDrawable = ctx_glx->m_window;
oxr_binding.glx.glxContext = ctx_glx->m_context;
oxr_binding.glx.visualid = visual_info->visualid;
# elif defined(WIN32)
GHOST_ContextWGL *ctx_wgl = static_cast<GHOST_ContextWGL *>(ghost_ctx);
oxr_binding.wgl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
oxr_binding.wgl.hDC = ctx_wgl->m_hDC;
oxr_binding.wgl.hGLRC = ctx_wgl->m_hGLRC;
# endif
#endif
/* Generate a framebuffer to use for blitting into the texture */
glGenFramebuffers(1, &m_fbo);
}
bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
int64_t *r_result) const override
{
std::vector<int64_t> gpu_binding_formats = {GL_RGBA8};
return choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result);
}
std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
{
std::vector<XrSwapchainImageOpenGLKHR> ogl_images(image_count);
std::vector<XrSwapchainImageBaseHeader *> base_images;
// Need to return vector of base header pointers, so of a different type. Need to build a new
// list with this type, and keep the initial one alive.
for (XrSwapchainImageOpenGLKHR &image : ogl_images) {
image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
base_images.push_back(reinterpret_cast<XrSwapchainImageBaseHeader *>(&image));
}
// Keep alive.
m_image_cache.push_back(std::move(ogl_images));
return base_images;
}
void submitToSwapchain(XrSwapchainImageBaseHeader *swapchain_image,
const VAMR_DrawViewInfo *draw_info) override
{
XrSwapchainImageOpenGLKHR *ogl_swapchain_image = reinterpret_cast<XrSwapchainImageOpenGLKHR *>(
swapchain_image);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);
glFramebufferTexture2D(
GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ogl_swapchain_image->image, 0);
glBlitFramebuffer(draw_info->ofsx,
draw_info->ofsy,
draw_info->ofsx + draw_info->width,
draw_info->ofsy + draw_info->height,
draw_info->ofsx,
draw_info->ofsy,
draw_info->ofsx + draw_info->width,
draw_info->ofsy + draw_info->height,
GL_COLOR_BUFFER_BIT,
GL_LINEAR);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
private:
std::list<std::vector<XrSwapchainImageOpenGLKHR>> m_image_cache;
GLuint m_fbo{0};
};
#ifdef WIN32
class GraphicsBindingD3D : public IGraphicsBinding {
public:
~GraphicsBindingD3D()
{
if (m_shared_resource) {
m_ghost_ctx->disposeSharedOpenGLResource(m_shared_resource);
}
}
bool checkVersionRequirements(GHOST_Context *ghost_ctx,
XrInstance instance,
XrSystemId system_id,
std::string *r_requirement_info) const override
{
GHOST_ContextD3D *ctx_dx = static_cast<GHOST_ContextD3D *>(ghost_ctx);
XrGraphicsRequirementsD3D11KHR gpu_requirements{XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR};
xrGetD3D11GraphicsRequirementsKHR(instance, system_id, &gpu_requirements);
if (r_requirement_info) {
std::ostringstream strstream;
strstream << "Min DirectX 11 Feature Level " << gpu_requirements.minFeatureLevel
<< std::endl;
*r_requirement_info = std::move(strstream.str());
}
return ctx_dx->m_device->GetFeatureLevel() >= gpu_requirements.minFeatureLevel;
}
void initFromGhostContext(GHOST_Context *ghost_ctx) override
{
GHOST_ContextD3D *ctx_d3d = static_cast<GHOST_ContextD3D *>(ghost_ctx);
oxr_binding.d3d11.type = XR_TYPE_GRAPHICS_BINDING_D3D11_KHR;
oxr_binding.d3d11.device = ctx_d3d->m_device;
m_ghost_ctx = ctx_d3d;
}
bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
int64_t *r_result) const override
{
std::vector<int64_t> gpu_binding_formats = {DXGI_FORMAT_R8G8B8A8_UNORM};
return choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result);
}
std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
{
std::vector<XrSwapchainImageD3D11KHR> d3d_images(image_count);
std::vector<XrSwapchainImageBaseHeader *> base_images;
// Need to return vector of base header pointers, so of a different type. Need to build a new
// list with this type, and keep the initial one alive.
for (XrSwapchainImageD3D11KHR &image : d3d_images) {
image.type = XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR;
base_images.push_back(reinterpret_cast<XrSwapchainImageBaseHeader *>(&image));
}
// Keep alive.
m_image_cache.push_back(std::move(d3d_images));
return base_images;
}
void submitToSwapchain(XrSwapchainImageBaseHeader *swapchain_image,
const VAMR_DrawViewInfo *draw_info) override
{
XrSwapchainImageD3D11KHR *d3d_swapchain_image = reinterpret_cast<XrSwapchainImageD3D11KHR *>(
swapchain_image);
# if 0
/* Ideally we'd just create a render target view for the OpenXR swapchain image texture and
* blit from the OpenGL context into it. The NV_DX_interop extension doesn't want to work with
* this though. At least not with Optimus hardware. See:
* https://github.com/mpv-player/mpv/issues/2949#issuecomment-197262807.
*/
ID3D11RenderTargetView *rtv;
CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D,
DXGI_FORMAT_R8G8B8A8_UNORM);
m_ghost_ctx->m_device->CreateRenderTargetView(d3d_swapchain_image->texture, &rtv_desc, &rtv);
if (!m_shared_resource) {
m_shared_resource = m_ghost_ctx->createSharedOpenGLResource(
draw_info->width, draw_info->height, rtv);
}
m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height);
# else
if (!m_shared_resource) {
m_shared_resource = m_ghost_ctx->createSharedOpenGLResource(draw_info->width,
draw_info->height);
}
m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height);
m_ghost_ctx->m_device_ctx->OMSetRenderTargets(0, nullptr, nullptr);
m_ghost_ctx->m_device_ctx->CopyResource(d3d_swapchain_image->texture,
m_ghost_ctx->getSharedTexture2D(m_shared_resource));
# endif
}
private:
GHOST_ContextD3D *m_ghost_ctx;
GHOST_SharedOpenGLResource *m_shared_resource;
std::list<std::vector<XrSwapchainImageD3D11KHR>> m_image_cache;
};
#endif // WIN32
std::unique_ptr<IGraphicsBinding> GraphicsBindingCreateFromType(VAMR_GraphicsBindingType type)
{
switch (type) {
case VAMR_GraphicsBindingTypeOpenGL:
return std::unique_ptr<GraphicsBindingOpenGL>(new GraphicsBindingOpenGL());
#ifdef WIN32
case VAMR_GraphicsBindingTypeD3D11:
return std::unique_ptr<GraphicsBindingD3D>(new GraphicsBindingD3D());
#endif
default:
return nullptr;
}
}
} // namespace VAMR

View File

@@ -0,0 +1,74 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*/
#ifndef __IGRAPHICSBINDING_H__
#define __IGRAPHICSBINDING_H__
#include <memory>
#include <string>
#include <vector>
#include "openxr_includes.h"
namespace VAMR {
class IGraphicsBinding {
friend std::unique_ptr<IGraphicsBinding> GraphicsBindingCreateFromType(
VAMR_GraphicsBindingType type);
public:
union {
#if defined(WITH_X11)
XrGraphicsBindingOpenGLXlibKHR glx;
#elif defined(WIN32)
XrGraphicsBindingOpenGLWin32KHR wgl;
XrGraphicsBindingD3D11KHR d3d11;
#endif
} oxr_binding;
/**
* Does __not__ require this object is initialized (can be called prior to
* #initFromGhostContext). It's actually meant to be called first.
*
* \param r_requirement_info Return argument to retrieve an informal string on the requirements
* to be met. Useful for error/debug messages.
*/
virtual bool checkVersionRequirements(class GHOST_Context *ghost_ctx,
XrInstance instance,
XrSystemId system_id,
std::string *r_requirement_info) const = 0;
virtual void initFromGhostContext(class GHOST_Context *ghost_ctx) = 0;
virtual bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
int64_t *r_result) const = 0;
virtual std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(
uint32_t image_count) = 0;
virtual void submitToSwapchain(XrSwapchainImageBaseHeader *swapchain_image,
const VAMR_DrawViewInfo *draw_info) = 0;
protected:
/* Use GraphicsBindingCreateFromType */
IGraphicsBinding() = default;
};
std::unique_ptr<IGraphicsBinding> GraphicsBindingCreateFromType(VAMR_GraphicsBindingType type);
} // namespace VAMR
#endif /* __IGRAPHICSBINDING_H__ */

View File

@@ -0,0 +1,575 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*/
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cstdio>
#include <list>
#include <sstream>
#include "VAMR_capi.h"
#include "openxr_includes.h"
#include "Context.h"
#include "Exception.h"
#include "IGraphicsBinding.h"
#include "Session.h"
#include "utils.h"
namespace VAMR {
struct OpenXRSessionData {
XrSystemId system_id{XR_NULL_SYSTEM_ID};
XrSession session{XR_NULL_HANDLE};
XrSessionState session_state{XR_SESSION_STATE_UNKNOWN};
// Only stereo rendering supported now.
const XrViewConfigurationType view_type{XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO};
XrSpace reference_space;
std::vector<XrView> views;
std::vector<XrSwapchain> swapchains;
std::map<XrSwapchain, std::vector<XrSwapchainImageBaseHeader *>> swapchain_images;
int32_t swapchain_image_width, swapchain_image_height;
};
struct DrawInfo {
XrFrameState frame_state;
/** Time at frame start to benchmark frame render durations. */
std::chrono::high_resolution_clock::time_point frame_begin_time;
/* Time previous frames took for rendering (in ms) */
std::list<double> last_frame_times;
};
/* -------------------------------------------------------------------- */
/** \name Create, Initialize and Destruct
*
* \{ */
Session::Session(Context *xr_context) : m_context(xr_context), m_oxr(new OpenXRSessionData())
{
}
Session::~Session()
{
unbindGraphicsContext();
for (XrSwapchain &swapchain : m_oxr->swapchains) {
CHECK_XR_ASSERT(xrDestroySwapchain(swapchain));
}
m_oxr->swapchains.clear();
if (m_oxr->reference_space != XR_NULL_HANDLE) {
CHECK_XR_ASSERT(xrDestroySpace(m_oxr->reference_space));
}
if (m_oxr->session != XR_NULL_HANDLE) {
CHECK_XR_ASSERT(xrDestroySession(m_oxr->session));
}
m_oxr->session = XR_NULL_HANDLE;
m_oxr->session_state = XR_SESSION_STATE_UNKNOWN;
}
/**
* A system in OpenXR the combination of some sort of HMD plus controllers and whatever other
* devices are managed through OpenXR. So this attempts to init the HMD and the other devices.
*/
void Session::initSystem()
{
assert(m_context->getInstance() != XR_NULL_HANDLE);
assert(m_oxr->system_id == XR_NULL_SYSTEM_ID);
XrSystemGetInfo system_info{};
system_info.type = XR_TYPE_SYSTEM_GET_INFO;
system_info.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
CHECK_XR(xrGetSystem(m_context->getInstance(), &system_info, &m_oxr->system_id),
"Failed to get device information. Is a device plugged in?");
}
/** \} */ /* Create, Initialize and Destruct */
/* -------------------------------------------------------------------- */
/** \name State Management
*
* \{ */
static void create_reference_space(OpenXRSessionData *oxr, const VAMR_Pose *base_pose)
{
XrReferenceSpaceCreateInfo create_info{XR_TYPE_REFERENCE_SPACE_CREATE_INFO};
create_info.poseInReferenceSpace.orientation.w = 1.0f;
create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
#if 0
/* Proper reference space set up is not supported yet. We simply hand OpenXR
* the global space as reference space and apply its pose onto the active
* camera matrix to get a basic viewing experience going. If there's no active
* camera with stick to the world origin.
*
* Once we have proper reference space set up (i.e. a way to define origin, up-
* direction and an initial view rotation perpendicular to the up-direction),
* we can hand OpenXR a proper reference pose/space.
*/
create_info.poseInReferenceSpace.position.x = base_pose->position[0];
create_info.poseInReferenceSpace.position.y = base_pose->position[2];
create_info.poseInReferenceSpace.position.z = -base_pose->position[1];
create_info.poseInReferenceSpace.orientation.x = base_pose->orientation_quat[1];
create_info.poseInReferenceSpace.orientation.y = base_pose->orientation_quat[3];
create_info.poseInReferenceSpace.orientation.z = -base_pose->orientation_quat[2];
create_info.poseInReferenceSpace.orientation.w = base_pose->orientation_quat[0];
#else
(void)base_pose;
#endif
CHECK_XR(xrCreateReferenceSpace(oxr->session, &create_info, &oxr->reference_space),
"Failed to create reference space.");
}
void Session::start(const VAMR_SessionBeginInfo *begin_info)
{
assert(m_context->getInstance() != XR_NULL_HANDLE);
assert(m_oxr->session == XR_NULL_HANDLE);
if (m_context->getCustomFuncs()->gpu_ctx_bind_fn == nullptr) {
THROW_XR(
"Invalid API usage: No way to bind graphics context to the XR session. Call "
"VAMR_GraphicsContextBindFuncs() with valid parameters before starting the "
"session (through VAMR_SessionStart()).");
}
initSystem();
bindGraphicsContext();
if (m_gpu_ctx == nullptr) {
THROW_XR(
"Invalid API usage: No graphics context returned through the callback set with "
"VAMR_GraphicsContextBindFuncs(). This is required for session starting (through "
"VAMR_SessionStart()).");
}
std::string requirement_str;
m_gpu_binding = GraphicsBindingCreateFromType(m_context->getGraphicsBindingType());
if (!m_gpu_binding->checkVersionRequirements(
m_gpu_ctx, m_context->getInstance(), m_oxr->system_id, &requirement_str)) {
std::ostringstream strstream;
strstream << "Available graphics context version does not meet the following requirements: "
<< requirement_str;
THROW_XR(strstream.str().c_str());
}
m_gpu_binding->initFromGhostContext(m_gpu_ctx);
XrSessionCreateInfo create_info{};
create_info.type = XR_TYPE_SESSION_CREATE_INFO;
create_info.systemId = m_oxr->system_id;
create_info.next = &m_gpu_binding->oxr_binding;
CHECK_XR(xrCreateSession(m_context->getInstance(), &create_info, &m_oxr->session),
"Failed to create VR session. The OpenXR runtime may have additional requirements for "
"the graphics driver that are not met. Other causes are possible too however.\nTip: "
"The --debug-xr command line option for Blender might allow the runtime to output "
"detailed error information to the command line.");
prepareDrawing();
create_reference_space(m_oxr.get(), &begin_info->base_pose);
}
void Session::requestEnd()
{
xrRequestExitSession(m_oxr->session);
}
void Session::end()
{
assert(m_oxr->session != XR_NULL_HANDLE);
CHECK_XR(xrEndSession(m_oxr->session), "Failed to cleanly end the VR session.");
unbindGraphicsContext();
m_draw_info = nullptr;
}
Session::eLifeExpectancy Session::handleStateChangeEvent(
const XrEventDataSessionStateChanged *lifecycle)
{
m_oxr->session_state = lifecycle->state;
/* Runtime may send events for apparently destroyed session. Our handle should be NULL then. */
assert((m_oxr->session == XR_NULL_HANDLE) || (m_oxr->session == lifecycle->session));
switch (lifecycle->state) {
case XR_SESSION_STATE_READY: {
XrSessionBeginInfo begin_info{};
begin_info.type = XR_TYPE_SESSION_BEGIN_INFO;
begin_info.primaryViewConfigurationType = m_oxr->view_type;
CHECK_XR(xrBeginSession(m_oxr->session, &begin_info),
"Failed to cleanly begin the VR session.");
break;
}
case XR_SESSION_STATE_STOPPING:
/* Runtime will change state to STATE_EXITING, don't destruct session yet. */
end();
break;
case XR_SESSION_STATE_EXITING:
case XR_SESSION_STATE_LOSS_PENDING:
return SESSION_DESTROY;
default:
break;
}
return SESSION_KEEP_ALIVE;
}
/** \} */ /* State Management */
/* -------------------------------------------------------------------- */
/** \name Drawing
*
* \{ */
static std::vector<XrSwapchainImageBaseHeader *> swapchain_images_create(
XrSwapchain swapchain, IGraphicsBinding *gpu_binding)
{
std::vector<XrSwapchainImageBaseHeader *> images;
uint32_t image_count;
CHECK_XR(xrEnumerateSwapchainImages(swapchain, 0, &image_count, nullptr),
"Failed to get count of swapchain images to create for the VR session.");
images = gpu_binding->createSwapchainImages(image_count);
CHECK_XR(xrEnumerateSwapchainImages(swapchain, images.size(), &image_count, images[0]),
"Failed to create swapchain images for the VR session.");
return images;
}
static unique_oxr_ptr<XrSwapchain> swapchain_create(const XrSession session,
IGraphicsBinding *gpu_binding,
const XrViewConfigurationView *xr_view)
{
XrSwapchainCreateInfo create_info{XR_TYPE_SWAPCHAIN_CREATE_INFO};
unique_oxr_ptr<XrSwapchain> swapchain(xrDestroySwapchain);
uint32_t format_count = 0;
int64_t chosen_format;
CHECK_XR(xrEnumerateSwapchainFormats(session, 0, &format_count, nullptr),
"Failed to get count of swapchain image formats.");
std::vector<int64_t> swapchain_formats(format_count);
CHECK_XR(xrEnumerateSwapchainFormats(
session, swapchain_formats.size(), &format_count, swapchain_formats.data()),
"Failed to get swapchain image formats.");
assert(swapchain_formats.size() == format_count);
if (!gpu_binding->chooseSwapchainFormat(swapchain_formats, &chosen_format)) {
THROW_XR("Error: No format matching OpenXR runtime supported swapchain formats found.");
}
create_info.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT |
XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
create_info.format = chosen_format;
create_info.sampleCount = xr_view->recommendedSwapchainSampleCount;
create_info.width = xr_view->recommendedImageRectWidth;
create_info.height = xr_view->recommendedImageRectHeight;
create_info.faceCount = 1;
create_info.arraySize = 1;
create_info.mipCount = 1;
CHECK_XR(swapchain.construct(xrCreateSwapchain, session, &create_info),
"Failed to create OpenXR swapchain.");
return swapchain;
}
void Session::prepareDrawing()
{
std::vector<XrViewConfigurationView> view_configs;
uint32_t view_count;
CHECK_XR(
xrEnumerateViewConfigurationViews(
m_context->getInstance(), m_oxr->system_id, m_oxr->view_type, 0, &view_count, nullptr),
"Failed to get count of view configurations.");
view_configs.resize(view_count, {XR_TYPE_VIEW_CONFIGURATION_VIEW});
CHECK_XR(xrEnumerateViewConfigurationViews(m_context->getInstance(),
m_oxr->system_id,
m_oxr->view_type,
view_configs.size(),
&view_count,
view_configs.data()),
"Failed to get count of view configurations.");
for (const XrViewConfigurationView &view : view_configs) {
unique_oxr_ptr<XrSwapchain> swapchain = swapchain_create(
m_oxr->session, m_gpu_binding.get(), &view);
auto images = swapchain_images_create(swapchain.get(), m_gpu_binding.get());
m_oxr->swapchain_image_width = view.recommendedImageRectWidth;
m_oxr->swapchain_image_height = view.recommendedImageRectHeight;
m_oxr->swapchains.push_back(swapchain.get());
m_oxr->swapchain_images.insert(std::make_pair(swapchain.get(), std::move(images)));
swapchain.release();
}
m_oxr->views.resize(view_count, {XR_TYPE_VIEW});
m_draw_info = std::unique_ptr<DrawInfo>(new DrawInfo());
}
void Session::beginFrameDrawing()
{
XrFrameWaitInfo wait_info{XR_TYPE_FRAME_WAIT_INFO};
XrFrameBeginInfo begin_info{XR_TYPE_FRAME_BEGIN_INFO};
XrFrameState frame_state{XR_TYPE_FRAME_STATE};
// TODO Blocking call. Does this intefer with other drawing?
CHECK_XR(xrWaitFrame(m_oxr->session, &wait_info, &frame_state),
"Failed to synchronize frame rates between Blender and the device.");
CHECK_XR(xrBeginFrame(m_oxr->session, &begin_info),
"Failed to submit frame rendering start state.");
m_draw_info->frame_state = frame_state;
if (m_context->isDebugTimeMode()) {
m_draw_info->frame_begin_time = std::chrono::high_resolution_clock::now();
}
}
static void print_debug_timings(DrawInfo *draw_info)
{
/** Render time of last 8 frames (in ms) to calculate an average. */
std::chrono::duration<double, std::milli> duration = std::chrono::high_resolution_clock::now() -
draw_info->frame_begin_time;
const double duration_ms = duration.count();
const int avg_frame_count = 8;
double avg_ms_tot = 0.0;
if (draw_info->last_frame_times.size() >= avg_frame_count) {
draw_info->last_frame_times.pop_front();
assert(draw_info->last_frame_times.size() == avg_frame_count - 1);
}
draw_info->last_frame_times.push_back(duration_ms);
for (double ms_iter : draw_info->last_frame_times) {
avg_ms_tot += ms_iter;
}
printf("VR frame render time: %.0fms - %.2f FPS (%.2f FPS 8 frames average)\n",
duration_ms,
1000.0 / duration_ms,
1000.0 / (avg_ms_tot / draw_info->last_frame_times.size()));
}
void Session::endFrameDrawing(std::vector<XrCompositionLayerBaseHeader *> *layers)
{
XrFrameEndInfo end_info{XR_TYPE_FRAME_END_INFO};
end_info.displayTime = m_draw_info->frame_state.predictedDisplayTime;
end_info.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
end_info.layerCount = layers->size();
end_info.layers = layers->data();
CHECK_XR(xrEndFrame(m_oxr->session, &end_info), "Failed to submit rendered frame.");
if (m_context->isDebugTimeMode()) {
print_debug_timings(m_draw_info.get());
}
}
void Session::draw(void *draw_customdata)
{
std::vector<XrCompositionLayerProjectionView>
projection_layer_views; // Keep alive until xrEndFrame() call!
XrCompositionLayerProjection proj_layer;
std::vector<XrCompositionLayerBaseHeader *> layers;
beginFrameDrawing();
if (m_draw_info->frame_state.shouldRender) {
proj_layer = drawLayer(projection_layer_views, draw_customdata);
layers.push_back(reinterpret_cast<XrCompositionLayerBaseHeader *>(&proj_layer));
}
endFrameDrawing(&layers);
}
static void vamr_draw_view_info_from_view(const XrView &view, VAMR_DrawViewInfo &r_info)
{
#if 0
/* Set and convert to Blender coodinate space */
r_info.pose.position[0] = view.pose.position.x;
r_info.pose.position[1] = -view.pose.position.z;
r_info.pose.position[2] = view.pose.position.y;
r_info.pose.orientation_quat[0] = view.pose.orientation.w;
r_info.pose.orientation_quat[1] = view.pose.orientation.x;
r_info.pose.orientation_quat[2] = -view.pose.orientation.z;
r_info.pose.orientation_quat[3] = view.pose.orientation.y;
#else
r_info.pose.position[0] = view.pose.position.x;
r_info.pose.position[1] = view.pose.position.y;
r_info.pose.position[2] = view.pose.position.z;
r_info.pose.orientation_quat[0] = view.pose.orientation.w;
r_info.pose.orientation_quat[1] = view.pose.orientation.x;
r_info.pose.orientation_quat[2] = view.pose.orientation.y;
r_info.pose.orientation_quat[3] = view.pose.orientation.z;
#endif
r_info.fov.angle_left = view.fov.angleLeft;
r_info.fov.angle_right = view.fov.angleRight;
r_info.fov.angle_up = view.fov.angleUp;
r_info.fov.angle_down = view.fov.angleDown;
}
static bool vamr_draw_view_expects_srgb_buffer(const Context *context)
{
/* WMR seems to be faulty and doesn't do OETF transform correctly. So expect a SRGB buffer to
* compensate. */
return context->getOpenXRRuntimeID() == OPENXR_RUNTIME_WMR;
}
void Session::drawView(XrSwapchain swapchain,
XrCompositionLayerProjectionView &proj_layer_view,
XrView &view,
void *draw_customdata)
{
XrSwapchainImageAcquireInfo acquire_info{XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO};
XrSwapchainImageWaitInfo wait_info{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO};
XrSwapchainImageReleaseInfo release_info{XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO};
XrSwapchainImageBaseHeader *swapchain_image;
VAMR_DrawViewInfo draw_view_info{};
uint32_t swapchain_idx;
CHECK_XR(xrAcquireSwapchainImage(swapchain, &acquire_info, &swapchain_idx),
"Failed to acquire swapchain image for the VR session.");
wait_info.timeout = XR_INFINITE_DURATION;
CHECK_XR(xrWaitSwapchainImage(swapchain, &wait_info),
"Failed to acquire swapchain image for the VR session.");
proj_layer_view.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
proj_layer_view.pose = view.pose;
proj_layer_view.fov = view.fov;
proj_layer_view.subImage.swapchain = swapchain;
proj_layer_view.subImage.imageRect.offset = {0, 0};
proj_layer_view.subImage.imageRect.extent = {m_oxr->swapchain_image_width,
m_oxr->swapchain_image_height};
swapchain_image = m_oxr->swapchain_images[swapchain][swapchain_idx];
draw_view_info.expects_srgb_buffer = vamr_draw_view_expects_srgb_buffer(m_context);
draw_view_info.ofsx = proj_layer_view.subImage.imageRect.offset.x;
draw_view_info.ofsy = proj_layer_view.subImage.imageRect.offset.y;
draw_view_info.width = proj_layer_view.subImage.imageRect.extent.width;
draw_view_info.height = proj_layer_view.subImage.imageRect.extent.height;
vamr_draw_view_info_from_view(view, draw_view_info);
m_context->getCustomFuncs()->draw_view_fn(&draw_view_info, draw_customdata);
m_gpu_binding->submitToSwapchain(swapchain_image, &draw_view_info);
CHECK_XR(xrReleaseSwapchainImage(swapchain, &release_info),
"Failed to release swapchain image used to submit VR session frame.");
}
XrCompositionLayerProjection Session::drawLayer(
std::vector<XrCompositionLayerProjectionView> &proj_layer_views, void *draw_customdata)
{
XrViewLocateInfo viewloc_info{XR_TYPE_VIEW_LOCATE_INFO};
XrViewState view_state{XR_TYPE_VIEW_STATE};
XrCompositionLayerProjection layer{XR_TYPE_COMPOSITION_LAYER_PROJECTION};
uint32_t view_count;
viewloc_info.viewConfigurationType = m_oxr->view_type;
viewloc_info.displayTime = m_draw_info->frame_state.predictedDisplayTime;
viewloc_info.space = m_oxr->reference_space;
CHECK_XR(xrLocateViews(m_oxr->session,
&viewloc_info,
&view_state,
m_oxr->views.size(),
&view_count,
m_oxr->views.data()),
"Failed to query frame view and projection state.");
assert(m_oxr->swapchains.size() == view_count);
proj_layer_views.resize(view_count);
for (uint32_t view_idx = 0; view_idx < view_count; view_idx++) {
drawView(m_oxr->swapchains[view_idx],
proj_layer_views[view_idx],
m_oxr->views[view_idx],
draw_customdata);
}
layer.space = m_oxr->reference_space;
layer.viewCount = proj_layer_views.size();
layer.views = proj_layer_views.data();
return layer;
}
/** \} */ /* Drawing */
/* -------------------------------------------------------------------- */
/** \name State Queries
*
* \{ */
bool Session::isRunning() const
{
if (m_oxr->session == XR_NULL_HANDLE) {
return false;
}
switch (m_oxr->session_state) {
case XR_SESSION_STATE_READY:
case XR_SESSION_STATE_SYNCHRONIZED:
case XR_SESSION_STATE_VISIBLE:
case XR_SESSION_STATE_FOCUSED:
return true;
default:
return false;
}
}
/** \} */ /* State Queries */
/* -------------------------------------------------------------------- */
/** \name Graphics Context Injection
*
* Sessions need access to Ghost graphics context information. Additionally, this API allows
* creating contexts on the fly (created on start, destructed on end). For this, callbacks to bind
* (potentially create) and unbind (potentially destruct) a Ghost graphics context have to be set,
* which will be called on session start and end respectively.
*
* \{ */
void Session::bindGraphicsContext()
{
const CustomFuncs *custom_funcs = m_context->getCustomFuncs();
assert(custom_funcs->gpu_ctx_bind_fn);
m_gpu_ctx = static_cast<GHOST_Context *>(
custom_funcs->gpu_ctx_bind_fn(m_context->getGraphicsBindingType()));
}
void Session::unbindGraphicsContext()
{
const CustomFuncs *custom_funcs = m_context->getCustomFuncs();
if (custom_funcs->gpu_ctx_unbind_fn) {
custom_funcs->gpu_ctx_unbind_fn(m_context->getGraphicsBindingType(), m_gpu_ctx);
}
m_gpu_ctx = nullptr;
}
/** \} */ /* Graphics Context Injection */
} // namespace VAMR

View File

@@ -0,0 +1,82 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*/
#ifndef __SESSION_H__
#define __SESSION_H__
#include <map>
#include <memory>
namespace VAMR {
class Session {
public:
enum eLifeExpectancy {
SESSION_KEEP_ALIVE,
SESSION_DESTROY,
};
Session(class Context *xr_context);
~Session();
void start(const VAMR_SessionBeginInfo *begin_info);
void requestEnd();
eLifeExpectancy handleStateChangeEvent(const struct XrEventDataSessionStateChanged *lifecycle);
bool isRunning() const;
void unbindGraphicsContext(); /* public so context can ensure it's unbound as needed. */
void draw(void *draw_customdata);
private:
/** Pointer back to context managing this session. Would be nice to avoid, but needed to access
* custom callbacks set before session start. */
class Context *m_context;
std::unique_ptr<struct OpenXRSessionData> m_oxr; /* Could use stack, but PImpl is preferable */
/** Active Ghost graphic context. Owned by Blender, not VAMR. */
class GHOST_Context *m_gpu_ctx{nullptr};
std::unique_ptr<class IGraphicsBinding> m_gpu_binding;
/** Rendering information. Set when drawing starts. */
std::unique_ptr<struct DrawInfo> m_draw_info;
void initSystem();
void end();
void bindGraphicsContext();
void prepareDrawing();
XrCompositionLayerProjection drawLayer(
std::vector<XrCompositionLayerProjectionView> &proj_layer_views, void *draw_customdata);
void drawView(XrSwapchain swapchain,
XrCompositionLayerProjectionView &proj_layer_view,
XrView &view,
void *draw_customdata);
void beginFrameDrawing();
void endFrameDrawing(std::vector<XrCompositionLayerBaseHeader *> *layers);
};
} // namespace VAMR
#endif /* __SESSION_H__ */

View File

@@ -0,0 +1,73 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*
* Abstraction for XR (VR, AR, MR, ..) access via OpenXR.
*/
#include <cassert>
#include <string>
#include "VAMR_capi.h"
#include "openxr_includes.h"
#include "Context.h"
#include "Exception.h"
#include "utils.h"
using namespace VAMR;
/**
* \brief Initialize the XR-Context.
* Includes setting up the OpenXR instance, querying available extensions and API layers,
* enabling extensions (currently graphics binding extension only) and API layers.
*/
VAMR_ContextHandle VAMR_ContextCreate(const VAMR_ContextCreateInfo *create_info)
{
Context *xr_context = new Context(create_info);
// TODO VAMR_Context's should probably be owned by the GHOST_System, which will handle context
// creation and destruction. Try-catch logic can be moved to C-API then.
try {
xr_context->initialize(create_info);
}
catch (Exception &e) {
xr_context->dispatchErrorMessage(&e);
delete xr_context;
return nullptr;
}
return (VAMR_ContextHandle)xr_context;
}
void VAMR_ContextDestroy(VAMR_ContextHandle xr_contexthandle)
{
delete (Context *)xr_contexthandle;
}
void VAMR_ErrorHandler(VAMR_ErrorHandlerFn handler_fn, void *customdata)
{
Context::setErrorHandler(handler_fn, customdata);
}
VAMR_TSuccess VAMR_EventsHandle(VAMR_ContextHandle xr_contexthandle)
{
return VAMR_EventsHandle((Context *)xr_contexthandle);
}

View File

@@ -0,0 +1,52 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*
* \note This is taken mostly from the OpenXR SDK, but with modified D3D versions (e.g. d3d11_4.h
* -> d3d11.h). Take care for that when updating, we don't want to require newest Win SDKs to be
* installed.
*/
#ifndef __VAMR_OPENXR_INCLUDES_H__
#define __VAMR_OPENXR_INCLUDES_H__
/* Platform headers */
#ifdef XR_USE_PLATFORM_WIN32
# define WIN32_LEAN_AND_MEAN
# define NOMINMAX
# include <windows.h>
#endif
/* Graphics headers */
#ifdef XR_USE_GRAPHICS_API_D3D10
# include <d3d10_1.h>
#endif
#ifdef XR_USE_GRAPHICS_API_D3D11
# include <d3d11.h>
#endif
#ifdef XR_USE_GRAPHICS_API_D3D12
# include <d3d12.h>
#endif
#ifdef WITH_X11
# include <GL/glxew.h>
#endif
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#endif /* __VAMR_OPENXR_INCLUDES_H__ */

136
intern/vamr/intern/utils.h Normal file
View File

@@ -0,0 +1,136 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup VAMR
*/
#ifndef __UTILS_H__
#define __UTILS_H__
#include <memory>
#include <vector>
namespace VAMR {
#define CHECK_XR(call, error_msg) \
{ \
XrResult _res = call; \
if (XR_FAILED(_res)) { \
throw VAMR::Exception(error_msg, __FILE__, __LINE__, _res); \
} \
} \
(void)0
#define CHECK_XR_ASSERT(call) \
{ \
XrResult _res = call; \
assert(_res == XR_SUCCESS); \
(void)_res; \
} \
(void)0
#define THROW_XR(error_msg) throw VAMR::Exception(error_msg, __FILE__, __LINE__);
#define XR_DEBUG_ONLY_BEGIN(ctx) \
if ((ctx)->isDebugMode()) { \
(void)0
#define XR_DEBUG_ONLY_END \
} \
(void)0
#define XR_DEBUG_PRINTF(ctx, ...) \
if ((ctx)->isDebugMode()) { \
printf(__VA_ARGS__); \
} \
(void)0
#define XR_DEBUG_ONLY_CALL(ctx, call) \
if ((ctx)->isDebugMode()) { \
call; \
} \
(void)0
/**
* Helper for RAII usage of OpenXR handles (defined with XR_DEFINE_HANDLE). This is based on
* `std::unique_ptr`, to give similar behavior and usage (e.g. functions like #get() and #release()
* are supported).
*/
template<typename _OXR_HANDLE> class unique_oxr_ptr {
public:
using xr_destroy_func = XrResult (*)(_OXR_HANDLE);
unique_oxr_ptr(xr_destroy_func destroy_fn) : m_destroy_fn(destroy_fn)
{
}
unique_oxr_ptr(unique_oxr_ptr &&other) : m_ptr(other.release()), m_destroy_fn(other.m_destroy_fn)
{
}
/**
* Construct the pointer from a xrCreate function passed as \a create_fn, with additional
* arguments forwarded from \a args.
* For example, this:
* \code
* some_unique_oxr_ptr.contruct(xrCreateFoo, some_arg, some_other_arg);
* \endcode
* effectively results in this call:
* \code
* xrCreateFoo(some_arg, some_other_arg, &some_unique_oxr_ptr.m_ptr);
* \endcode
*/
template<typename _create_func, typename... _Args>
XrResult construct(_create_func create_fn, _Args &&... args)
{
assert(m_ptr == XR_NULL_HANDLE);
return create_fn(std::forward<_Args>(args)..., &m_ptr);
}
~unique_oxr_ptr()
{
if (m_ptr != XR_NULL_HANDLE) {
m_destroy_fn(m_ptr);
}
}
_OXR_HANDLE get()
{
return m_ptr;
}
_OXR_HANDLE release()
{
_OXR_HANDLE ptr = get();
m_ptr = XR_NULL_HANDLE;
return ptr;
}
/* operator= defines not needed for now. */
unique_oxr_ptr(const unique_oxr_ptr &) = delete;
unique_oxr_ptr &operator=(const unique_oxr_ptr &) = delete;
private:
_OXR_HANDLE m_ptr{XR_NULL_HANDLE};
xr_destroy_func m_destroy_fn;
};
::VAMR_TSuccess VAMR_EventsHandle(class Context *xr_context);
} // namespace VAMR
#endif /* __UTILS_H__ */

View File

@@ -0,0 +1,8 @@
@echo off
echo Starting Blender with Oculus OpenXR support. This assumes the Oculus runtime
echo is installed in the default location. If this is not the case, please adjust
echo the path inside oculus.json.
echo.
pause
set XR_RUNTIME_JSON=%~dp0oculus.json
blender

View File

@@ -0,0 +1,7 @@
{
"file_format_version": "1.0.0",
"runtime": {
"library_path": "c:\\Program Files\\Oculus\\Support\\oculus-runtime\\LibOVRRT64_1.dll"
},
"api_layer": { }
}

View File

@@ -151,6 +151,8 @@ enum {
G_DEBUG_IO = (1 << 17), /* IO Debugging (for Collada, ...)*/
G_DEBUG_GPU_SHADERS = (1 << 18), /* GLSL shaders */
G_DEBUG_GPU_FORCE_WORKAROUNDS = (1 << 19), /* force gpu workarounds bypassing detections. */
G_DEBUG_XR = (1 << 20), /* XR/OpenXR messages */
G_DEBUG_XR_TIME = (1 << 21), /* XR/OpenXR timing messages */
};
#define G_DEBUG_ALL \

View File

@@ -627,6 +627,13 @@ void perspective_m4(float mat[4][4],
const float top,
const float nearClip,
const float farClip);
void perspective_m4_fov(float mat[4][4],
const float angle_left,
const float angle_right,
const float angle_up,
const float angle_down,
const float nearClip,
const float farClip);
void orthographic_m4(float mat[4][4],
const float left,
const float right,

View File

@@ -4637,6 +4637,33 @@ void perspective_m4(float mat[4][4],
mat[3][3] = 0.0f;
}
void perspective_m4_fov(float mat[4][4],
const float angle_left,
const float angle_right,
const float angle_up,
const float angle_down,
const float nearClip,
const float farClip)
{
const float tan_angle_left = tanf(angle_left);
const float tan_angle_right = tanf(angle_right);
const float tan_angle_up = tanf(angle_up);
const float tan_angle_down = tanf(angle_down);
const float Xdelta = tan_angle_right - tan_angle_left;
const float Ydelta = tan_angle_up - tan_angle_down;
mat[0][0] = 2 / Xdelta;
mat[1][1] = 2 / Ydelta;
mat[2][0] = (tan_angle_right + tan_angle_left) / Xdelta;
mat[2][1] = (tan_angle_up + tan_angle_down) / Ydelta;
mat[2][2] = -(farClip + nearClip) / (farClip - nearClip);
mat[2][3] = -1;
mat[3][2] = -(farClip * (nearClip + nearClip)) / (farClip - nearClip);
mat[0][1] = mat[0][2] = mat[0][3] = mat[1][0] = mat[1][2] = mat[1][3] = mat[3][0] = mat[3][1] =
mat[3][3] = 0.0f;
}
/* translate a matrix created by orthographic_m4 or perspective_m4 in XY coords
* (used to jitter the view) */
void window_translate_m4(float winmat[4][4], float perspmat[4][4], const float x, const float y)

View File

@@ -7695,6 +7695,7 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm)
wm->undo_stack = NULL;
wm->message_bus = NULL;
wm->xr_context = NULL;
BLI_listbase_clear(&wm->jobs);
BLI_listbase_clear(&wm->drags);

View File

@@ -149,6 +149,11 @@ void DRW_opengl_context_destroy(void);
void DRW_opengl_context_enable(void);
void DRW_opengl_context_disable(void);
/* Not nice to expose these. Code to render offscreen viewports can save expensive context switches
* by using this directly however. */
void *DRW_opengl_context_get(void);
void *DRW_gpu_context_get(void);
/* For garbage collection */
void DRW_cache_free_old_batches(struct Main *bmain);

View File

@@ -3004,6 +3004,16 @@ void DRW_opengl_context_disable(void)
DRW_opengl_context_disable_ex(true);
}
void *DRW_opengl_context_get(void)
{
return DST.gl_context;
}
void *DRW_gpu_context_get(void)
{
return DST.gpu_context;
}
void DRW_opengl_render_context_enable(void *re_gl_context)
{
/* If thread is main you should use DRW_opengl_context_enable(). */

View File

@@ -575,6 +575,24 @@ void ED_view3d_draw_offscreen(struct Depsgraph *depsgraph,
const bool do_color_management,
struct GPUOffScreen *ofs,
struct GPUViewport *viewport);
void ED_view3d_draw_offscreen_simple(struct Depsgraph *depsgraph,
struct Scene *scene,
struct View3DShading *shading_override,
int drawtype,
int winx,
int winy,
unsigned int draw_flags,
float viewmat[4][4],
float winmat[4][4],
float clip_start,
float clip_end,
float lens,
bool do_sky,
bool is_persp,
const char *viewname,
const bool do_color_management,
struct GPUOffScreen *ofs,
struct GPUViewport *viewport);
void ED_view3d_draw_setup_view(struct wmWindow *win,
struct Depsgraph *depsgraph,
struct Scene *scene,

View File

@@ -1673,6 +1673,86 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
G.f &= ~G_FLAG_RENDER_VIEWPORT;
}
void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph,
Scene *scene,
View3DShading *shading_override,
int drawtype,
int winx,
int winy,
uint draw_flags,
float viewmat[4][4],
float winmat[4][4],
float clip_start,
float clip_end,
float lens,
bool do_sky,
bool is_persp,
const char *viewname,
const bool do_color_management,
GPUOffScreen *ofs,
GPUViewport *viewport)
{
View3D v3d = {NULL};
ARegion ar = {NULL};
RegionView3D rv3d = {{{0}}};
/* connect data */
v3d.regionbase.first = v3d.regionbase.last = &ar;
ar.regiondata = &rv3d;
ar.regiontype = RGN_TYPE_WINDOW;
View3DShading *source_shading_settings = &scene->display.shading;
if (draw_flags & V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS && shading_override != NULL) {
source_shading_settings = shading_override;
}
memcpy(&v3d.shading, source_shading_settings, sizeof(View3DShading));
v3d.shading.type = drawtype;
if (drawtype == OB_MATERIAL) {
v3d.shading.flag = V3D_SHADING_SCENE_WORLD | V3D_SHADING_SCENE_LIGHTS;
}
if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) {
v3d.flag2 |= V3D_SHOW_ANNOTATION;
}
if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) {
v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y;
v3d.grid = 1.0f;
v3d.gridlines = 16;
v3d.gridsubdiv = 10;
/* Show grid, disable other overlays (set all available _HIDE_ flags). */
v3d.overlay.flag |= V3D_OVERLAY_HIDE_CURSOR | V3D_OVERLAY_HIDE_TEXT |
V3D_OVERLAY_HIDE_MOTION_PATHS | V3D_OVERLAY_HIDE_BONES |
V3D_OVERLAY_HIDE_OBJECT_XTRAS | V3D_OVERLAY_HIDE_OBJECT_ORIGINS;
v3d.flag |= V3D_HIDE_HELPLINES;
}
else {
v3d.flag2 = V3D_HIDE_OVERLAYS;
}
rv3d.persp = RV3D_PERSP;
v3d.clip_start = clip_start;
v3d.clip_end = clip_end;
v3d.lens = lens;
ED_view3d_draw_offscreen(depsgraph,
scene,
drawtype,
&v3d,
&ar,
winx,
winy,
viewmat,
winmat,
do_sky,
is_persp,
viewname,
do_color_management,
ofs,
viewport);
}
/**
* Utility func for ED_view3d_draw_offscreen
*
@@ -1866,6 +1946,9 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph,
if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) {
v3d.flag2 |= V3D_SHOW_ANNOTATION;
}
if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) {
v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y;
}
v3d.shading.background_type = V3D_SHADING_BACKGROUND_WORLD;

View File

@@ -185,6 +185,7 @@ typedef enum eGPUBuiltinShader {
GPU_SHADER_3D_CLIPPED_UNIFORM_COLOR,
/* basic image drawing */
GPU_SHADER_2D_IMAGE_LINEAR_TO_SRGB,
GPU_SHADER_2D_IMAGE_RECT_LINEAR_TO_SRGB,
GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR,
GPU_SHADER_2D_IMAGE_MASK_UNIFORM_COLOR,
/**

View File

@@ -93,6 +93,7 @@ GPUViewport *GPU_viewport_create(void);
void GPU_viewport_bind(GPUViewport *viewport, const rcti *rect);
void GPU_viewport_unbind(GPUViewport *viewport);
void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect);
void GPU_viewport_draw_to_screen_ex(GPUViewport *viewport, const rcti *rect, bool to_srgb);
void GPU_viewport_free(GPUViewport *viewport);
GPUViewport *GPU_viewport_create_from_offscreen(struct GPUOffScreen *ofs);

View File

@@ -1015,6 +1015,11 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = {
.vert = datatoc_gpu_shader_2D_image_vert_glsl,
.frag = datatoc_gpu_shader_image_linear_frag_glsl,
},
[GPU_SHADER_2D_IMAGE_RECT_LINEAR_TO_SRGB] =
{
.vert = datatoc_gpu_shader_2D_image_rect_vert_glsl,
.frag = datatoc_gpu_shader_image_linear_frag_glsl,
},
[GPU_SHADER_2D_IMAGE] =
{
.vert = datatoc_gpu_shader_2D_image_vert_glsl,

View File

@@ -505,7 +505,7 @@ void GPU_viewport_bind(GPUViewport *viewport, const rcti *rect)
}
}
void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect)
void GPU_viewport_draw_to_screen_ex(GPUViewport *viewport, const rcti *rect, bool to_srgb)
{
DefaultFramebufferList *dfbl = viewport->fbl;
@@ -532,7 +532,8 @@ void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect)
float y1 = rect->ymin;
float y2 = rect->ymin + h;
GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR);
GPUShader *shader = GPU_shader_get_builtin_shader(
to_srgb ? GPU_SHADER_2D_IMAGE_RECT_LINEAR_TO_SRGB : GPU_SHADER_2D_IMAGE_RECT_COLOR);
GPU_shader_bind(shader);
GPU_texture_bind(color, 0);
@@ -550,6 +551,11 @@ void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect)
GPU_texture_unbind(color);
}
void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect)
{
GPU_viewport_draw_to_screen_ex(viewport, rect, false);
}
void GPU_viewport_unbind(GPUViewport *UNUSED(viewport))
{
GPU_framebuffer_restore();

View File

@@ -585,6 +585,7 @@ enum {
V3D_OFSDRAW_NONE = (0),
V3D_OFSDRAW_SHOW_ANNOTATION = (1 << 0),
V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS = (1 << 1),
V3D_OFSDRAW_SHOW_GRIDFLOOR = (1 << 2),
};
#define RV3D_CAMZOOM_MIN -30

View File

@@ -42,6 +42,7 @@ struct wmKeyMap;
struct wmMsgBus;
struct wmOperator;
struct wmOperatorType;
struct GHOST_XrContext;
/* forwards */
struct PointerRNA;
@@ -180,6 +181,9 @@ typedef struct wmWindowManager {
struct wmMsgBus *message_bus;
//#ifdef WITH_OPENXR
void *xr_context; /* GHOST_XrContextHandle */
//#endif
} wmWindowManager;
/* wmWindowManager.initialized */

View File

@@ -309,6 +309,10 @@ if(WITH_OPENSUBDIV)
)
endif()
if(WITH_OPENXR)
add_definitions(-DWITH_OPENXR)
endif()
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_python "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@@ -59,6 +59,7 @@ static PyStructSequence_Field app_builtopts_info_fields[] = {
{(char *)"openmp", NULL},
{(char *)"openvdb", NULL},
{(char *)"alembic", NULL},
{(char *)"openxr", NULL},
{NULL},
};
@@ -268,6 +269,12 @@ static PyObject *make_builtopts_info(void)
SetObjIncref(Py_False);
#endif
#ifdef WITH_OPENXR
SetObjIncref(Py_True);
#else
SetObjIncref(Py_False);
#endif
#undef SetObjIncref
return builtopts_info;

View File

@@ -72,6 +72,7 @@ set(SRC
intern/wm_splash_screen.c
intern/wm_stereo.c
intern/wm_subwindow.c
intern/wm_surface.c
intern/wm_toolsystem.c
intern/wm_tooltip.c
intern/wm_uilist_type.c
@@ -98,6 +99,7 @@ set(SRC
wm_event_system.h
wm_event_types.h
wm_files.h
wm_surface.h
wm_window.h
gizmo/WM_gizmo_api.h
gizmo/WM_gizmo_types.h
@@ -175,4 +177,20 @@ if(WITH_COMPOSITOR)
add_definitions(-DWITH_COMPOSITOR)
endif()
if(WITH_OPENXR)
add_definitions(-DWITH_OPENXR)
list(APPEND INC
../../../intern/vamr
)
list(APPEND SRC
intern/wm_xr.c
)
list(APPEND LIB
bf_intern_vamr
)
endif()
blender_add_lib_nolist(bf_windowmanager "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@@ -152,6 +152,10 @@ void *WM_opengl_context_create(void);
void WM_opengl_context_dispose(void *context);
void WM_opengl_context_activate(void *context);
void WM_opengl_context_release(void *context);
#ifdef WIN32
void *WM_directx_context_create(void);
void WM_directx_context_dispose(void *context);
#endif
/* defines for 'type' WM_window_open_temp */
enum {

View File

@@ -374,6 +374,10 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
WM_msgbus_destroy(wm->message_bus);
}
#ifdef WITH_OPENXR
wm_xr_context_destroy(wm);
#endif
BLI_freelistN(&wm->paintcursors);
WM_drag_free_list(&wm->drags);

View File

@@ -72,6 +72,7 @@
#include "wm_draw.h"
#include "wm_window.h"
#include "wm_event_system.h"
#include "wm_surface.h"
#ifdef WITH_OPENSUBDIV
# include "BKE_subsurf.h"
@@ -315,7 +316,7 @@ static void wm_draw_region_buffer_free(ARegion *ar)
}
}
static void wm_draw_offscreen_texture_parameters(GPUOffScreen *offscreen)
void wm_draw_offscreen_texture_parameters(GPUOffScreen *offscreen)
{
/* Setup offscreen color texture for drawing. */
GPUTexture *texture = GPU_offscreen_color_texture(offscreen);
@@ -747,6 +748,39 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
}
}
void wm_draw_upside_down(int sizex, int sizey, bool to_srgb)
{
GPUVertFormat *format = immVertexFormat();
uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(to_srgb ? GPU_SHADER_2D_IMAGE_LINEAR_TO_SRGB : GPU_SHADER_2D_IMAGE);
/* wmOrtho for the screen has this same offset */
const float halfx = GLA_PIXEL_OFS / sizex;
const float halfy = GLA_PIXEL_OFS / sizex;
immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */
immBegin(GPU_PRIM_TRI_FAN, 4);
immAttr2f(texcoord, halfx, 1.0f + halfy);
immVertex2f(pos, 0.0f, 0.0f);
immAttr2f(texcoord, 1.0f + halfx, 1.0f + halfy);
immVertex2f(pos, sizex, 0.0f);
immAttr2f(texcoord, 1.0f + halfx, halfy);
immVertex2f(pos, sizex, sizey);
immAttr2f(texcoord, halfx, halfy);
immVertex2f(pos, 0.0f, sizey);
immEnd();
immUnbindProgram();
}
static void wm_draw_window(bContext *C, wmWindow *win)
{
bScreen *screen = WM_window_get_active_screen(win);
@@ -817,6 +851,20 @@ static void wm_draw_window(bContext *C, wmWindow *win)
screen->do_draw = false;
}
/**
* Draw offscreen contexts not bound to a specific window.
*/
static void wm_draw_surface(bContext *C, wmSurface *surface)
{
wm_window_clear_drawable(CTX_wm_manager(C));
wm_surface_make_drawable(surface);
surface->draw(C);
/* Avoid interference with window drawable */
wm_surface_clear_drawable();
}
/****************** main update call **********************/
/* quick test to prevent changing window drawable */
@@ -944,6 +992,9 @@ void wm_draw_update(bContext *C)
CTX_wm_window_set(C, NULL);
}
}
/* Draw non-windows (surfaces) */
wm_surfaces_iter(C, wm_draw_surface);
}
void wm_draw_region_clear(wmWindow *win, ARegion *UNUSED(ar))

View File

@@ -99,6 +99,7 @@
#include "wm_event_system.h"
#include "wm.h"
#include "wm_files.h"
#include "wm_surface.h"
#include "wm_window.h"
#include "ED_anim_api.h"
@@ -526,6 +527,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
BKE_keyconfig_pref_type_free();
wm_operatortype_free();
wm_surfaces_free();
wm_dropbox_free();
WM_menutype_free();
WM_uilisttype_free();

View File

@@ -3325,6 +3325,39 @@ static void WM_OT_stereo3d_set(wmOperatorType *ot)
/** \} */
#ifdef WITH_OPENXR
static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op))
{
wmWindowManager *wm = CTX_wm_manager(C);
/* Lazy-create xr context - tries to dynlink to the runtime, reading active_runtime.json. */
if (wm_xr_context_ensure(C, wm) == false) {
return OPERATOR_CANCELLED;
}
wm_xr_session_toggle(C, wm->xr_context);
return OPERATOR_FINISHED;
}
static void WM_OT_xr_session_toggle(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Toggle VR Session";
ot->idname = "WM_OT_xr_session_toggle";
ot->description =
"Attempt to open a view for use with virtual reality headsets, or close it if already "
"opened";
/* callbacks */
ot->exec = wm_xr_session_toggle_exec;
/* XXX INTERNAL just to hide it from the search menu by default, an Add-on will expose it in the
* UI instead. Not meant as a permanent solution. */
ot->flag = OPTYPE_INTERNAL;
}
#endif /* WITH_OPENXR */
/* -------------------------------------------------------------------- */
/** \name Operator Registration & Keymaps
* \{ */
@@ -3366,6 +3399,9 @@ void wm_operatortypes_register(void)
WM_operatortype_append(WM_OT_call_panel);
WM_operatortype_append(WM_OT_radial_control);
WM_operatortype_append(WM_OT_stereo3d_set);
#ifdef WITH_OPENXR
WM_operatortype_append(WM_OT_xr_session_toggle);
#endif
#if defined(WIN32)
WM_operatortype_append(WM_OT_console_toggle);
#endif

View File

@@ -0,0 +1,118 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup wm
*/
#include "BKE_context.h"
#include "BLF_api.h"
#include "BLI_listbase.h"
#include "BLI_threads.h"
#include "GHOST_C-api.h"
#include "GPU_batch_presets.h"
#include "GPU_framebuffer.h"
#include "GPU_immediate.h"
#include "GPU_context.h"
#include "MEM_guardedalloc.h"
#include "WM_types.h"
#include "WM_api.h"
#include "wm.h"
#include "wm_surface.h"
static ListBase global_surface_list = {NULL, NULL};
static wmSurface *g_drawable = NULL;
void wm_surfaces_iter(bContext *C, void (*cb)(bContext *C, wmSurface *))
{
for (wmSurface *surf = global_surface_list.first; surf; surf = surf->next) {
cb(C, surf);
}
}
void wm_surface_clear_drawable(void)
{
if (g_drawable) {
BLF_batch_reset();
gpu_batch_presets_reset();
immDeactivate();
g_drawable = NULL;
}
}
void wm_surface_set_drawable(wmSurface *surface, bool activate)
{
BLI_assert(ELEM(g_drawable, NULL, surface));
g_drawable = surface;
if (activate) {
GHOST_ActivateOpenGLContext(surface->ghost_ctx);
}
GPU_context_active_set(surface->gpu_ctx);
immActivate();
}
void wm_surface_make_drawable(wmSurface *surface)
{
BLI_assert(GPU_framebuffer_active_get() == NULL);
if (surface != g_drawable) {
wm_surface_clear_drawable();
wm_surface_set_drawable(surface, true);
}
}
void wm_surface_reset_drawable(void)
{
BLI_assert(BLI_thread_is_main());
BLI_assert(GPU_framebuffer_active_get() == NULL);
if (g_drawable) {
wm_surface_clear_drawable();
wm_surface_set_drawable(g_drawable, true);
}
}
void wm_surface_add(wmSurface *surface)
{
BLI_addtail(&global_surface_list, surface);
}
void wm_surface_remove(wmSurface *surface)
{
BLI_remlink(&global_surface_list, surface);
surface->free_data(surface);
MEM_freeN(surface);
}
void wm_surfaces_free(void)
{
for (wmSurface *surf = global_surface_list.first, *surf_next; surf; surf = surf_next) {
surf_next = surf->next;
wm_surface_remove(surf);
}
BLI_assert(BLI_listbase_is_empty(&global_surface_list));
}

View File

@@ -57,6 +57,11 @@
#include "RNA_define.h"
#include "RNA_enum_types.h"
/* TODO Call through wm_xr.c */
#ifdef WITH_OPENXR
# include "VAMR_capi.h"
#endif
#include "WM_api.h"
#include "WM_types.h"
#include "wm.h"
@@ -1612,6 +1617,9 @@ void wm_window_process_events(const bContext *C)
GHOST_DispatchEvents(g_system);
}
hasevent |= wm_window_timer(C);
#ifdef WITH_OPENXR
hasevent |= VAMR_EventsHandle(CTX_wm_manager(C)->xr_context);
#endif
/* no event, we sleep 5 milliseconds */
if (hasevent == 0) {
@@ -1932,6 +1940,9 @@ void wm_window_raise(wmWindow *win)
/** \name Window Buffers
* \{ */
/**
* \brief Push rendered buffer to the screen.
*/
void wm_window_swap_buffers(wmWindow *win)
{
GHOST_SwapWindowBuffers(win->ghostwin);
@@ -2433,3 +2444,24 @@ void WM_opengl_context_release(void *context)
}
/** \} */
#ifdef WIN32
/* -------------------------------------------------------------------- */
/** \name Direct DirectX Context Management
* \{ */
void *WM_directx_context_create(void)
{
BLI_assert(GPU_framebuffer_active_get() == NULL);
return GHOST_CreateDirectXContext(g_system);
}
void WM_directx_context_dispose(void *context)
{
BLI_assert(GPU_framebuffer_active_get() == NULL);
GHOST_DisposeDirectXContext(g_system, context);
}
/** \} */
#endif

View File

@@ -0,0 +1,518 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup wm
*
* \name Window-Manager XR API
*
* Implements Blender specific functionality for the VAMR API.
*/
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BKE_screen.h"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_view3d_types.h"
#include "DRW_engine.h"
#include "ED_view3d.h"
#include "GHOST_C-api.h"
#include "GPU_context.h"
#include "GPU_draw.h"
#include "GPU_matrix.h"
#include "GPU_viewport.h"
#include "MEM_guardedalloc.h"
#include "UI_interface.h"
#include "WM_types.h"
#include "WM_api.h"
#include "wm.h"
#include "wm_surface.h"
#include "wm_window.h"
#include "VAMR_capi.h"
static wmSurface *g_xr_surface = NULL;
typedef struct {
VAMR_GraphicsBindingType gpu_binding_type;
GPUOffScreen *offscreen;
GPUViewport *viewport;
GPUFrameBuffer *fbo;
GPUTexture *fbo_tex;
bool viewport_bound;
GHOST_ContextHandle secondary_ghost_ctx;
} wmXrSurfaceData;
typedef struct {
wmWindowManager *wm;
bContext *evil_C;
} wmXrErrorHandlerData;
void wm_xr_draw_view(const VAMR_DrawViewInfo *, void *);
void *wm_xr_session_gpu_binding_context_create(VAMR_GraphicsBindingType);
void wm_xr_session_gpu_binding_context_destroy(VAMR_GraphicsBindingType, void *);
wmSurface *wm_xr_session_surface_create(wmWindowManager *, unsigned int);
/* -------------------------------------------------------------------- */
/** \name XR-Context
*
* All XR functionality is accessed through a #VAMR_Context handle.
* The lifetime of this context also determines the lifetime of the OpenXR instance, which is the
* representation of the OpenXR runtime connection within the application.
*
* \{ */
static void wm_xr_error_handler(const VAMR_Error *error)
{
wmXrErrorHandlerData *handler_data = error->customdata;
wmWindowManager *wm = handler_data->wm;
BKE_reports_clear(&wm->reports);
WM_report(RPT_ERROR, error->user_message);
WM_report_banner_show();
UI_popup_menu_reports(handler_data->evil_C, &wm->reports);
if (wm->xr_context) {
/* Just play safe and destroy the entire context. */
VAMR_ContextDestroy(wm->xr_context);
wm->xr_context = NULL;
}
}
bool wm_xr_context_ensure(bContext *C, wmWindowManager *wm)
{
if (wm->xr_context) {
return true;
}
static wmXrErrorHandlerData error_customdata;
/* Set up error handling */
error_customdata.wm = wm;
error_customdata.evil_C = C;
VAMR_ErrorHandler(wm_xr_error_handler, &error_customdata);
{
const VAMR_GraphicsBindingType gpu_bindings_candidates[] = {
VAMR_GraphicsBindingTypeOpenGL,
#ifdef WIN32
VAMR_GraphicsBindingTypeD3D11,
#endif
};
VAMR_ContextCreateInfo create_info = {
.gpu_binding_candidates = gpu_bindings_candidates,
.gpu_binding_candidates_count = ARRAY_SIZE(gpu_bindings_candidates)};
if (G.debug & G_DEBUG_XR) {
create_info.context_flag |= VAMR_ContextDebug;
}
if (G.debug & G_DEBUG_XR_TIME) {
create_info.context_flag |= VAMR_ContextDebugTime;
}
if (!(wm->xr_context = VAMR_ContextCreate(&create_info))) {
return false;
}
/* Set up context callbacks */
VAMR_GraphicsContextBindFuncs(wm->xr_context,
wm_xr_session_gpu_binding_context_create,
wm_xr_session_gpu_binding_context_destroy);
VAMR_DrawViewFunc(wm->xr_context, wm_xr_draw_view);
}
BLI_assert(wm->xr_context != NULL);
return true;
}
void wm_xr_context_destroy(wmWindowManager *wm)
{
if (wm->xr_context != NULL) {
VAMR_ContextDestroy(wm->xr_context);
}
}
/** \} */ /* XR-Context */
/* -------------------------------------------------------------------- */
/** \name XR-Session
*
* \{ */
void *wm_xr_session_gpu_binding_context_create(VAMR_GraphicsBindingType graphics_binding)
{
wmSurface *surface = wm_xr_session_surface_create(G_MAIN->wm.first, graphics_binding);
wmXrSurfaceData *data = surface->customdata;
wm_surface_add(surface);
return data->secondary_ghost_ctx ? data->secondary_ghost_ctx : surface->ghost_ctx;
}
void wm_xr_session_gpu_binding_context_destroy(VAMR_GraphicsBindingType UNUSED(graphics_lib),
void *UNUSED(context))
{
if (g_xr_surface) { /* Might have been freed already */
wm_surface_remove(g_xr_surface);
}
wm_window_reset_drawable();
}
static void wm_xr_session_begin_info_create(const Scene *scene, VAMR_SessionBeginInfo *begin_info)
{
if (scene->camera) {
copy_v3_v3(begin_info->base_pose.position, scene->camera->loc);
if (ELEM(scene->camera->rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) {
axis_angle_to_quat(
begin_info->base_pose.orientation_quat, scene->camera->rotAxis, scene->camera->rotAngle);
}
else if (scene->camera->rotmode == ROT_MODE_QUAT) {
copy_v4_v4(begin_info->base_pose.orientation_quat, scene->camera->quat);
}
else {
eul_to_quat(begin_info->base_pose.orientation_quat, scene->camera->rot);
}
}
else {
copy_v3_fl(begin_info->base_pose.position, 0.0f);
unit_qt(begin_info->base_pose.orientation_quat);
}
}
void wm_xr_session_toggle(bContext *C, void *xr_context_ptr)
{
VAMR_ContextHandle xr_context = xr_context_ptr;
if (xr_context && VAMR_SessionIsRunning(xr_context)) {
VAMR_SessionEnd(xr_context);
}
else {
VAMR_SessionBeginInfo begin_info;
wm_xr_session_begin_info_create(CTX_data_scene(C), &begin_info);
VAMR_SessionStart(xr_context, &begin_info);
}
}
/** \} */ /* XR-Session */
/* -------------------------------------------------------------------- */
/** \name XR-Session Surface
*
* A wmSurface is used to manage drawing of the VR viewport. It's created and destroyed with the
* session.
*
* \{ */
static void wm_xr_surface_viewport_bind(wmXrSurfaceData *surface_data, const rcti *rect)
{
if (surface_data->viewport_bound == false) {
GPU_viewport_bind(surface_data->viewport, rect);
}
surface_data->viewport_bound = true;
}
static void wm_xr_surface_viewport_unbind(wmXrSurfaceData *surface_data)
{
if (surface_data->viewport_bound) {
GPU_viewport_unbind(surface_data->viewport);
}
surface_data->viewport_bound = false;
}
/**
* \brief Call Ghost-XR to draw a frame
*
* Draw callback for the XR-session surface. It's expected to be called on each main loop iteration
* and tells Ghost-XR to submit a new frame by drawing its views. Note that for drawing each view,
* #wm_xr_draw_view() will be called through Ghost-XR (see VAMR_DrawViewFunc()).
*/
static void wm_xr_session_surface_draw(bContext *C)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmXrSurfaceData *surface_data = g_xr_surface->customdata;
if (!VAMR_SessionIsRunning(wm->xr_context)) {
return;
}
VAMR_SessionDrawViews(wm->xr_context, C);
if (surface_data->viewport) {
/* Still bound from view drawing. */
wm_xr_surface_viewport_unbind(surface_data);
}
}
static void wm_xr_session_free_data(wmSurface *surface)
{
wmXrSurfaceData *data = surface->customdata;
if (data->secondary_ghost_ctx) {
#ifdef WIN32
if (data->gpu_binding_type == VAMR_GraphicsBindingTypeD3D11) {
WM_directx_context_dispose(data->secondary_ghost_ctx);
}
#endif
}
GPU_context_active_set(surface->gpu_ctx);
DRW_opengl_context_enable_ex(false);
if (data->viewport) {
GPU_viewport_clear_from_offscreen(data->viewport);
GPU_viewport_free(data->viewport);
}
if (data->offscreen) {
GPU_offscreen_free(data->offscreen);
}
if (data->fbo) {
GPU_framebuffer_free(data->fbo);
}
if (data->fbo_tex) {
GPU_texture_free(data->fbo_tex);
}
DRW_opengl_context_disable_ex(false);
MEM_freeN(surface->customdata);
g_xr_surface = NULL;
}
static bool wm_xr_session_surface_offscreen_ensure(const VAMR_DrawViewInfo *draw_view)
{
wmXrSurfaceData *surface_data = g_xr_surface->customdata;
char err_out[256] = "unknown";
bool failure = false;
if (surface_data->offscreen &&
(GPU_offscreen_width(surface_data->offscreen) == draw_view->width) &&
(GPU_offscreen_height(surface_data->offscreen) == draw_view->height)) {
BLI_assert(surface_data->viewport);
return true;
}
DRW_opengl_context_enable();
if (surface_data->offscreen) {
GPU_viewport_clear_from_offscreen(surface_data->viewport);
GPU_viewport_free(surface_data->viewport);
GPU_offscreen_free(surface_data->offscreen);
}
if (!(surface_data->offscreen = GPU_offscreen_create(
draw_view->width, draw_view->height, 0, true, false, err_out))) {
failure = true;
}
if ((failure == false) &&
!(surface_data->viewport = GPU_viewport_create_from_offscreen(surface_data->offscreen))) {
GPU_offscreen_free(surface_data->offscreen);
failure = true;
}
surface_data->fbo = GPU_framebuffer_create();
surface_data->fbo_tex = GPU_texture_create_2d(
draw_view->width, draw_view->height, GPU_RGBA8, NULL, NULL);
GPU_framebuffer_ensure_config(
&surface_data->fbo, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(surface_data->fbo_tex)});
DRW_opengl_context_disable();
if (failure) {
fprintf(stderr, "%s: failed to get buffer, %s\n", __func__, err_out);
return false;
}
return true;
}
wmSurface *wm_xr_session_surface_create(wmWindowManager *UNUSED(wm), unsigned int gpu_binding_type)
{
if (g_xr_surface) {
BLI_assert(false);
return g_xr_surface;
}
wmSurface *surface = MEM_callocN(sizeof(*surface), __func__);
wmXrSurfaceData *data = MEM_callocN(sizeof(*data), "XrSurfaceData");
#ifndef WIN32
BLI_assert(gpu_binding_type == VAMR_GraphicsBindingTypeOpenGL);
#endif
surface->draw = wm_xr_session_surface_draw;
surface->free_data = wm_xr_session_free_data;
data->gpu_binding_type = gpu_binding_type;
surface->customdata = data;
surface->ghost_ctx = DRW_opengl_context_get();
DRW_opengl_context_enable();
switch (gpu_binding_type) {
case VAMR_GraphicsBindingTypeOpenGL:
break;
#ifdef WIN32
case VAMR_GraphicsBindingTypeD3D11:
data->secondary_ghost_ctx = WM_directx_context_create();
break;
#endif
}
surface->gpu_ctx = DRW_gpu_context_get();
DRW_opengl_context_disable();
g_xr_surface = surface;
return surface;
}
/** \} */ /* XR-Session Surface */
/* -------------------------------------------------------------------- */
/** \name XR Drawing
*
* \{ */
/**
* Proper reference space set up is not supported yet. We simply hand OpenXR the global space as
* reference space and apply its pose onto the active camera matrix to get a basic viewing
* experience going. If there's no active camera with stick to the world origin.
*/
static void wm_xr_draw_matrices_create(const Scene *scene,
const VAMR_DrawViewInfo *draw_view,
const float clip_start,
const float clip_end,
float r_view_mat[4][4],
float r_proj_mat[4][4])
{
float scalemat[4][4], quat[4];
float temp[4][4];
perspective_m4_fov(r_proj_mat,
draw_view->fov.angle_left,
draw_view->fov.angle_right,
draw_view->fov.angle_up,
draw_view->fov.angle_down,
clip_start,
clip_end);
scale_m4_fl(scalemat, 1.0f);
invert_qt_qt_normalized(quat, draw_view->pose.orientation_quat);
quat_to_mat4(temp, quat);
translate_m4(temp,
-draw_view->pose.position[0],
-draw_view->pose.position[1],
-draw_view->pose.position[2]);
if (scene->camera) {
invert_m4_m4(scene->camera->imat, scene->camera->obmat);
mul_m4_m4m4(r_view_mat, temp, scene->camera->imat);
}
else {
copy_m4_m4(r_view_mat, temp);
}
}
/**
* \brief Draw a viewport for a single eye.
*
* This is the main viewport drawing function for VR sessions. It's assigned to Ghost-XR as a
* callback (see VAMR_DrawViewFunc()) and executed for each view (read: eye).
*/
void wm_xr_draw_view(const VAMR_DrawViewInfo *draw_view, void *customdata)
{
bContext *C = customdata;
wmXrSurfaceData *surface_data = g_xr_surface->customdata;
const float clip_start = 0.01f, clip_end = 500.0f;
const float lens = 50.0f;
const rcti rect = {
.xmin = 0, .ymin = 0, .xmax = draw_view->width - 1, .ymax = draw_view->height - 1};
GPUOffScreen *offscreen;
GPUViewport *viewport;
View3DShading shading;
float viewmat[4][4], winmat[4][4];
wm_xr_draw_matrices_create(CTX_data_scene(C), draw_view, clip_start, clip_end, viewmat, winmat);
if (!wm_xr_session_surface_offscreen_ensure(draw_view)) {
return;
}
offscreen = surface_data->offscreen;
viewport = surface_data->viewport;
wm_xr_surface_viewport_bind(surface_data, &rect);
glClear(GL_DEPTH_BUFFER_BIT);
BKE_screen_view3d_shading_init(&shading);
shading.flag |= V3D_SHADING_WORLD_ORIENTATION;
shading.flag &= ~V3D_SHADING_SPECULAR_HIGHLIGHT;
shading.background_type = V3D_SHADING_BACKGROUND_WORLD;
ED_view3d_draw_offscreen_simple(CTX_data_ensure_evaluated_depsgraph(C),
CTX_data_scene(C),
&shading,
OB_SOLID,
draw_view->width,
draw_view->height,
/* Draw floor for better orientation */
V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS | V3D_OFSDRAW_SHOW_GRIDFLOOR,
viewmat,
winmat,
clip_start,
clip_end,
lens,
true,
true,
NULL,
false,
offscreen,
viewport);
GPU_framebuffer_bind(surface_data->fbo);
GPUTexture *texture = GPU_offscreen_color_texture(offscreen);
wm_draw_offscreen_texture_parameters(offscreen);
wmViewport(&rect);
if (surface_data->secondary_ghost_ctx &&
GHOST_isUpsideDownContext(surface_data->secondary_ghost_ctx)) {
GPU_texture_bind(texture, 0);
wm_draw_upside_down(draw_view->width, draw_view->height, draw_view->expects_srgb_buffer);
GPU_texture_unbind(texture);
}
else {
GPU_viewport_draw_to_screen_ex(viewport, &rect, draw_view->expects_srgb_buffer);
}
/* Leave viewport bound so VAMR can use its context/framebuffer, its unbound in
* wm_xr_session_surface_draw(). */
// GPU_viewport_unbind(viewport);
}
/** \} */ /* XR Drawing */

View File

@@ -98,4 +98,16 @@ void wm_stereo3d_set_cancel(bContext *C, wmOperator *op);
void wm_open_init_load_ui(wmOperator *op, bool use_prefs);
void wm_open_init_use_scripts(wmOperator *op, bool use_prefs);
/* wm_draw.c */
struct GPUOffScreen;
void wm_draw_offscreen_texture_parameters(struct GPUOffScreen *offscreen);
void wm_draw_upside_down(int sizex, int sizey, bool to_srgb);
#ifdef WITH_OPENXR
/* wm_xr.c */
bool wm_xr_context_ensure(bContext *C, wmWindowManager *wm);
void wm_xr_context_destroy(wmWindowManager *wm);
void wm_xr_session_toggle(bContext *C, void *xr_context);
#endif
#endif /* __WM_H__ */

View File

@@ -0,0 +1,57 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup wm
*
* \name WM-Surface
*
* Container to manage painting in an offscreen context.
*/
#ifndef __WM_SURFACE_H__
#define __WM_SURFACE_H__
struct bContext;
typedef struct wmSurface {
struct wmSurface *next, *prev;
GHOST_ContextHandle ghost_ctx;
struct GPUContext *gpu_ctx;
void *customdata;
void (*draw)(struct bContext *);
/** Free customdata, not the surface itself (done by wm_surface API) */
void (*free_data)(struct wmSurface *);
} wmSurface;
/* Create/Free */
void wm_surface_add(wmSurface *surface);
void wm_surface_remove(wmSurface *surface);
void wm_surfaces_free(void);
/* Utils */
void wm_surfaces_iter(struct bContext *C, void (*cb)(bContext *, wmSurface *));
/* Drawing */
void wm_surface_make_drawable(wmSurface *surface);
void wm_surface_clear_drawable(void);
void wm_surface_set_drawable(wmSurface *surface, bool activate);
void wm_surface_reset_drawable(void);
#endif /* __WM_SURFACE_H__ */

View File

@@ -118,6 +118,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
if(WITH_OPENXR)
add_definitions(-DWITH_OPENXR)
endif()
# Setup the exe sources and buildinfo
set(SRC
creator.c
@@ -838,6 +842,8 @@ elseif(WIN32)
${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_gpu_glitchworkaround.cmd
${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_log.cmd
${CMAKE_SOURCE_DIR}/release/windows/batch/blender_factory_startup.cmd
${CMAKE_SOURCE_DIR}/release/windows/batch/blender_oculus.cmd
${CMAKE_SOURCE_DIR}/release/windows/batch/oculus.json
DESTINATION "."
)

View File

@@ -604,6 +604,10 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo
BLI_argsPrintArgDoc(ba, "--debug-gpu-shaders");
BLI_argsPrintArgDoc(ba, "--debug-gpu-force-workarounds");
BLI_argsPrintArgDoc(ba, "--debug-wm");
# ifdef WITH_OPENXR
BLI_argsPrintArgDoc(ba, "--debug-xr");
BLI_argsPrintArgDoc(ba, "--debug-xr-time");
# endif
BLI_argsPrintArgDoc(ba, "--debug-all");
BLI_argsPrintArgDoc(ba, "--debug-io");
@@ -941,6 +945,16 @@ static const char arg_handle_debug_mode_generic_set_doc_wm[] =
"\n\t"
"Enable debug messages for the window manager, shows all operators in search, shows "
"keymap errors.";
# ifdef WITH_OPENXR
static const char arg_handle_debug_mode_generic_set_doc_xr[] =
"\n\t"
"Enable debug messages for virtual reality contexts.\n"
"\tEnables the OpenXR API validation layer, (OpenXR) debug messages and general information "
"prints.";
static const char arg_handle_debug_mode_generic_set_doc_xr_time[] =
"\n\t"
"Enable debug messages for virtual reality frame rendering times.";
# endif
static const char arg_handle_debug_mode_generic_set_doc_jobs[] =
"\n\t"
"Enable time profiling for background jobs.";
@@ -2081,6 +2095,16 @@ void main_args_setup(bContext *C, bArgs *ba)
(void *)G_DEBUG_HANDLERS);
BLI_argsAdd(
ba, 1, NULL, "--debug-wm", CB_EX(arg_handle_debug_mode_generic_set, wm), (void *)G_DEBUG_WM);
# ifdef WITH_OPENXR
BLI_argsAdd(
ba, 1, NULL, "--debug-xr", CB_EX(arg_handle_debug_mode_generic_set, xr), (void *)G_DEBUG_XR);
BLI_argsAdd(ba,
1,
NULL,
"--debug-xr-time",
CB_EX(arg_handle_debug_mode_generic_set, xr_time),
(void *)G_DEBUG_XR_TIME);
# endif
BLI_argsAdd(ba, 1, NULL, "--debug-all", CB(arg_handle_debug_mode_all), NULL);
BLI_argsAdd(ba, 1, NULL, "--debug-io", CB(arg_handle_debug_mode_io), NULL);

View File

@@ -50,6 +50,9 @@ def _init_addon_blacklist():
# netrender has known problems re-registering
BLACKLIST_ADDONS.add("netrender")
if not bpy.app.build_options.openxr:
BLACKLIST_ADDONS.add("viewport_vr_preview")
for mod in addon_utils.modules():
if addon_utils.module_bl_info(mod)['blender'] < (2, 80, 0):
BLACKLIST_ADDONS.add(mod.__name__)

View File

@@ -54,6 +54,9 @@ MODULE_SYS_PATHS = {
if not bpy.app.build_options.freestyle:
BLACKLIST.add("render_freestyle_svg")
if not bpy.app.build_options.openxr:
BLACKLIST.add("viewport_vr_preview")
BLACKLIST_DIRS = (
os.path.join(bpy.utils.resource_path('USER'), "scripts"),
) + tuple(addon_utils.paths()[1:])