Skip to content

Commit 0a6a9b3

Browse files
Integrate VisionOS platform
1 parent 75a6593 commit 0a6a9b3

23 files changed

Lines changed: 684 additions & 55 deletions

BuildTools/CMake/BuildUtils.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,8 @@ endfunction()
323323
function(get_backend_libraries_type _LIB_TYPE)
324324
if(PLATFORM_WIN32 OR PLATFORM_LINUX OR PLATFORM_ANDROID OR PLATFORM_UNIVERSAL_WINDOWS OR PLATFORM_MACOS)
325325
set(LIB_TYPE "shared")
326-
elseif(PLATFORM_IOS OR PLATFORM_TVOS OR PLATFORM_WEB)
327-
# Statically link with the engine on iOS, tvOS and Emscripten.
326+
elseif(PLATFORM_IOS OR PLATFORM_TVOS OR PLATFORM_VISIONOS OR PLATFORM_WEB)
327+
# Statically link with the engine on iOS, tvOS, visionOS and Emscripten.
328328
# It is also possible to link dynamically by
329329
# putting the library into the framework.
330330
set(LIB_TYPE "static")

CMakeLists.txt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ set(PLATFORM_LINUX FALSE CACHE INTERNAL "")
4949
set(PLATFORM_MACOS FALSE CACHE INTERNAL "")
5050
set(PLATFORM_IOS FALSE CACHE INTERNAL "")
5151
set(PLATFORM_TVOS FALSE CACHE INTERNAL "")
52+
set(PLATFORM_VISIONOS FALSE CACHE INTERNAL "")
5253
set(PLATFORM_WEB FALSE CACHE INTERNAL "")
5354
set(PLATFORM_EMSCRIPTEN FALSE CACHE INTERNAL "")
5455
set(D3D11_SUPPORTED FALSE CACHE INTERNAL "D3D11 is not supported")
@@ -135,6 +136,9 @@ else()
135136
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "tvOS")
136137
set(PLATFORM_TVOS TRUE CACHE INTERNAL "Target platform: tvOS")
137138
message("Target platform: tvOS " ${ARCH})
139+
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "visionOS")
140+
set(PLATFORM_VISIONOS TRUE CACHE INTERNAL "Target platform: visionOS")
141+
message("Target platform: visionOS " ${ARCH})
138142
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
139143
set(PLATFORM_WEB TRUE CACHE INTERNAL "Target platform: Web")
140144
set(PLATFORM_EMSCRIPTEN TRUE CACHE INTERNAL "Build with Emscripten")
@@ -144,8 +148,8 @@ else()
144148
endif()
145149
endif(WIN32)
146150

147-
if(PLATFORM_MACOS OR PLATFORM_IOS OR PLATFORM_TVOS)
148-
set(PLATFORM_APPLE TRUE CACHE INTERNAL "Apple platform (macOS, iOS, or tvOS)")
151+
if(PLATFORM_MACOS OR PLATFORM_IOS OR PLATFORM_TVOS OR PLATFORM_VISIONOS)
152+
set(PLATFORM_APPLE TRUE CACHE INTERNAL "Apple platform (macOS, iOS, tvOS, or visionOS)")
149153
endif()
150154

151155
add_library(Diligent-PublicBuildSettings INTERFACE)
@@ -209,6 +213,9 @@ elseif(PLATFORM_IOS)
209213
target_compile_definitions(Diligent-PublicBuildSettings INTERFACE PLATFORM_IOS=1 PLATFORM_APPLE=1)
210214
elseif(PLATFORM_TVOS)
211215
target_compile_definitions(Diligent-PublicBuildSettings INTERFACE PLATFORM_TVOS=1 PLATFORM_APPLE=1)
216+
elseif(PLATFORM_VISIONOS)
217+
set(ARCHIVER_SUPPORTED TRUE CACHE INTERNAL "Archiver is supported on visionOS platform")
218+
target_compile_definitions(Diligent-PublicBuildSettings INTERFACE PLATFORM_VISIONOS=1 PLATFORM_APPLE=1)
212219
elseif(PLATFORM_WEB)
213220
set(GLES_SUPPORTED TRUE CACHE INTERNAL "OpenGLES is supported on Web platform")
214221
set(WEBGPU_SUPPORTED TRUE CACHE INTERNAL "WebGPU is supported on Web platform")
@@ -487,7 +494,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
487494
target_compile_options(Diligent-BuildSettings INTERFACE "$<$<CONFIG:DEBUG>:${DILIGENT_CLANG_DEBUG_COMPILE_OPTIONS}>")
488495

489496
set(CLANG_RELEASE_OPTIONS -Wno-unused-variable ${DILIGENT_CLANG_RELEASE_COMPILE_OPTIONS})
490-
target_compile_options(Diligent-BuildSettings INTERFACE $<$<NOT:$<CONFIG:Debug>>:${CLANG_RELEASE_OPTIONS}>)
497+
target_compile_options(Diligent-BuildSettings INTERFACE "$<$<NOT:$<CONFIG:Debug>>:${CLANG_RELEASE_OPTIONS}>")
491498

492499
if (PLATFORM_WIN32)
493500
target_compile_options(Diligent-BuildSettings
@@ -540,6 +547,16 @@ elseif(PLATFORM_TVOS)
540547
message(FATAL_ERROR "Failed to find CoreFoundation framework")
541548
endif()
542549

550+
find_library(FOUNDATION Foundation)
551+
if(NOT FOUNDATION)
552+
message(FATAL_ERROR "Failed to find Foundation framework")
553+
endif()
554+
elseif(PLATFORM_VISIONOS)
555+
find_library(CORE_FOUNDATION CoreFoundation)
556+
if(NOT CORE_FOUNDATION)
557+
message(FATAL_ERROR "Failed to find CoreFoundation framework")
558+
endif()
559+
543560
find_library(FOUNDATION Foundation)
544561
if(NOT FOUNDATION)
545562
message(FATAL_ERROR "Failed to find Foundation framework")

Common/interface/StringTools.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2025 Diligent Graphics LLC
2+
* Copyright 2019-2026 Diligent Graphics LLC
33
* Copyright 2015-2019 Egor Yusov
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -95,7 +95,7 @@ inline std::wstring WidenString(const std::string& Str)
9595

9696
inline int StrCmpNoCase(const char* Str1, const char* Str2, size_t NumChars)
9797
{
98-
#if PLATFORM_ANDROID || PLATFORM_LINUX || PLATFORM_MACOS || PLATFORM_IOS || PLATFORM_TVOS || PLATFORM_WEB
98+
#if PLATFORM_ANDROID || PLATFORM_LINUX || PLATFORM_MACOS || PLATFORM_IOS || PLATFORM_TVOS || PLATFORM_VISIONOS || PLATFORM_WEB
9999
# define _strnicmp strncasecmp
100100
#endif
101101

@@ -104,7 +104,7 @@ inline int StrCmpNoCase(const char* Str1, const char* Str2, size_t NumChars)
104104

105105
inline int StrCmpNoCase(const char* Str1, const char* Str2)
106106
{
107-
#if PLATFORM_ANDROID || PLATFORM_LINUX || PLATFORM_MACOS || PLATFORM_IOS || PLATFORM_TVOS || PLATFORM_WEB
107+
#if PLATFORM_ANDROID || PLATFORM_LINUX || PLATFORM_MACOS || PLATFORM_IOS || PLATFORM_TVOS || PLATFORM_VISIONOS || PLATFORM_WEB
108108
# define _stricmp strcasecmp
109109
#endif
110110

Graphics/Archiver/interface/ArchiverFactoryLoader.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2025 Diligent Graphics LLC
2+
* Copyright 2019-2026 Diligent Graphics LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@
2828

2929
#include "ArchiverFactory.h"
3030

31-
#if PLATFORM_ANDROID || PLATFORM_LINUX || PLATFORM_MACOS || PLATFORM_IOS || PLATFORM_TVOS || PLATFORM_WEB || (PLATFORM_WIN32 && !defined(_MSC_VER))
31+
#if PLATFORM_ANDROID || PLATFORM_LINUX || PLATFORM_MACOS || PLATFORM_IOS || PLATFORM_TVOS || PLATFORM_VISIONOS || PLATFORM_WEB || (PLATFORM_WIN32 && !defined(_MSC_VER))
3232
// https://gcc.gnu.org/wiki/Visibility
3333
# define API_QUALIFIER __attribute__((visibility("default")))
3434
#elif PLATFORM_WIN32 || PLATFORM_UNIVERSAL_WINDOWS

Graphics/GraphicsEngine/src/DeviceObjectArchive.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ DeviceObjectArchive::DeviceType RenderDeviceTypeToArchiveDeviceType(RENDER_DEVIC
5050
case RENDER_DEVICE_TYPE_VULKAN: return DeviceObjectArchive::DeviceType::Vulkan;
5151
#if PLATFORM_MACOS
5252
case RENDER_DEVICE_TYPE_METAL: return DeviceObjectArchive::DeviceType::Metal_MacOS;
53-
#elif PLATFORM_IOS || PLATFORM_TVOS
53+
#elif PLATFORM_IOS || PLATFORM_TVOS || PLATFORM_VISIONOS
5454
case RENDER_DEVICE_TYPE_METAL: return DeviceObjectArchive::DeviceType::Metal_iOS;
5555
#endif
5656
case RENDER_DEVICE_TYPE_WEBGPU:

Graphics/GraphicsEngine/src/FramebufferBase.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2025 Diligent Graphics LLC
2+
* Copyright 2019-2026 Diligent Graphics LLC
33
* Copyright 2015-2019 Egor Yusov
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -125,7 +125,7 @@ void ValidateFramebufferDesc(const FramebufferDesc& Desc, IRenderDevice* pDevice
125125
LOG_FRAMEBUFFER_ERROR_AND_THROW("memoryless attachment ", i, " is not compatible with ATTACHMENT_STORE_OP_STORE");
126126
}
127127

128-
#if PLATFORM_MACOS || PLATFORM_IOS || PLATFORM_TVOS
128+
#if PLATFORM_MACOS || PLATFORM_IOS || PLATFORM_TVOS || PLATFORM_VISIONOS
129129
{
130130
Uint32 NumSubpasses = 0;
131131
for (Uint32 s = 0; s < RPDesc.SubpassCount; ++s)

Graphics/GraphicsTools/CMakeLists.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ if(DILIGENT_USE_OPENXR)
7777
list(APPEND INTERFACE interface/OpenXRUtilities.h)
7878
endif()
7979

80+
if(PLATFORM_VISIONOS)
81+
list(APPEND SOURCE src/CompositorServicesSession.mm)
82+
list(APPEND INTERFACE interface/CompositorServicesSession.hpp)
83+
endif()
84+
8085
set(DEPENDENCIES)
8186

8287
if(D3D11_SUPPORTED)
@@ -164,6 +169,26 @@ if(DILIGENT_USE_OPENXR)
164169
target_compile_definitions(Diligent-GraphicsTools PUBLIC DILIGENT_USE_OPENXR=1)
165170
endif()
166171

172+
if(PLATFORM_VISIONOS)
173+
find_library(COMPOSITOR_SERVICES_FRAMEWORK CompositorServices)
174+
find_library(SPATIAL_FRAMEWORK Spatial)
175+
find_library(ARKIT_FRAMEWORK ARKit)
176+
if(COMPOSITOR_SERVICES_FRAMEWORK AND SPATIAL_FRAMEWORK AND ARKIT_FRAMEWORK)
177+
target_link_libraries(Diligent-GraphicsTools PUBLIC
178+
${COMPOSITOR_SERVICES_FRAMEWORK}
179+
${SPATIAL_FRAMEWORK}
180+
${ARKIT_FRAMEWORK}
181+
)
182+
endif()
183+
184+
# CompositorServicesSession.mm reuses ObjCWrapper.hpp from DiligentCorePro.
185+
# Metal is the only backend on visionOS, so the Pro Metal headers are
186+
# always available in this configuration.
187+
target_include_directories(Diligent-GraphicsTools PRIVATE
188+
../../../DiligentCorePro/Graphics/GraphicsEngineMetal/include
189+
)
190+
endif()
191+
167192
if(D3D11_SUPPORTED OR D3D12_SUPPORTED)
168193
target_link_libraries(Diligent-GraphicsTools
169194
PRIVATE
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Copyright 2026 Diligent Graphics LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* In no event and under no legal theory, whether in tort (including negligence),
17+
* contract, or otherwise, unless required by applicable law (such as deliberate
18+
* and grossly negligent acts) or agreed to in writing, shall any Contributor be
19+
* liable for any damages, including any direct, indirect, special, incidental,
20+
* or consequential damages of any character arising as a result of this License or
21+
* out of the use or inability to use the software (including but not limited to damages
22+
* for loss of goodwill, work stoppage, computer failure or malfunction, or any and
23+
* all other commercial damages or losses), even if such Contributor has been advised
24+
* of the possibility of such damages.
25+
*/
26+
27+
#pragma once
28+
29+
/// \file
30+
/// CompositorServices session wrapper for visionOS.
31+
32+
/// `CompositorServicesSession` encapsulates the visionOS frame protocol (frame query, update,
33+
/// timing wait, drawable iteration, present) and the optional ARKit world-tracking session,
34+
/// exposing a small, idiomatic C++ API on top of the C/Objective-C `CompositorServices`
35+
/// framework.
36+
37+
#include <functional>
38+
#include <memory>
39+
40+
#include "../../GraphicsEngine/interface/RenderDevice.h"
41+
#include "../../GraphicsEngine/interface/DeviceContext.h"
42+
#include "../../../Primitives/interface/BasicTypes.h"
43+
#include "../../../Common/interface/BasicMath.hpp"
44+
#include "../../../Common/interface/RefCntAutoPtr.hpp"
45+
46+
namespace Diligent
47+
{
48+
49+
/// High-level wrapper around the visionOS CompositorServices frame protocol.
50+
51+
/// The session owns:
52+
/// - a `cp_layer_renderer_t` received from the SwiftUI `CompositorLayer` entry point;
53+
/// - an optional ARKit world-tracking session that supplies device anchors for each drawable;
54+
/// - references to the Diligent device and immediate context used for wrapping drawable
55+
/// textures and presenting drawables.
56+
///
57+
/// `cp_layer_renderer_t` and `cp_drawable_t` are exposed as opaque `void*` pointers so that
58+
/// consumers do not need to include `<CompositorServices/CompositorServices.h>` and can be
59+
/// compiled as plain C++
60+
class CompositorServicesSession
61+
{
62+
public:
63+
/// Initializes the session and, optionally, starts the ARKit world-tracking session.
64+
65+
/// \param[in] pRenderer - Layer renderer (`cp_layer_renderer_t`) from the SwiftUI
66+
/// `CompositorLayer` entry point, passed as an opaque
67+
/// pointer. Must not be null.
68+
/// \param[in] pDevice - Diligent render device. Must not be null.
69+
/// \param[in] pContext - Diligent immediate context. Must not be null.
70+
/// \param[in] EnableWorldTracking - When `true`, an ARKit world-tracking session is started
71+
/// and used to attach device anchors to every drawable.
72+
/// When `false`, the scene is rigidly attached to the
73+
/// headset.
74+
CompositorServicesSession(void* pRenderer,
75+
IRenderDevice* pDevice,
76+
IDeviceContext* pContext,
77+
bool EnableWorldTracking = true);
78+
79+
~CompositorServicesSession();
80+
81+
// clang-format off
82+
CompositorServicesSession (const CompositorServicesSession&) = delete;
83+
CompositorServicesSession ( CompositorServicesSession&&) = delete;
84+
CompositorServicesSession& operator=(const CompositorServicesSession&) = delete;
85+
CompositorServicesSession& operator=( CompositorServicesSession&&) = delete;
86+
// clang-format on
87+
88+
/// Drives a single CompositorServices frame from start to finish.
89+
90+
/// Handles the full frame protocol mandated by visionOS:
91+
/// 1. Queries the next frame from the layer renderer.
92+
/// 2. Wraps app-side state updates in `cp_frame_start_update` / `cp_frame_end_update`
93+
/// by invoking `Update`.
94+
/// 3. Predicts timing after the update phase and waits until the compositor's optimal
95+
/// input time.
96+
/// 4. Enters the submission phase, iterates over all drawables, attaches a device anchor
97+
/// (when world tracking is enabled) and invokes `RenderDrawable` for each.
98+
/// 5. Ends the submission phase.
99+
///
100+
/// All null / cancelled-frame paths are handled internally.
101+
///
102+
/// `RenderDrawable` receives an opaque `cp_drawable_t` pointer and is responsible for
103+
/// setting up render targets, drawing the scene and calling `PresentDrawable` on the
104+
/// session.
105+
///
106+
/// Either callback may be empty.
107+
///
108+
/// \param[in] Update - Callback invoked during the update phase of the frame.
109+
/// \param[in] RenderDrawable - Callback invoked once per drawable during the submission
110+
/// phase of the frame.
111+
void RenderFrame(std::function<void()> Update,
112+
std::function<void(void* pDrawable)> RenderDrawable);
113+
114+
/// Returns the number of views (eyes) in a drawable (typically 2 for stereo rendering).
115+
116+
/// \param[in] pDrawable - Opaque `cp_drawable_t` pointer.
117+
Uint32 GetViewCount(void* pDrawable) const;
118+
119+
/// Wraps the Metal color texture of a drawable view as a Diligent texture.
120+
121+
/// \param[in] pDrawable - Opaque `cp_drawable_t` pointer.
122+
/// \param[in] ViewIndex - Zero-based view index.
123+
///
124+
/// \return A reference to the Diligent texture, or null on failure.
125+
RefCntAutoPtr<ITexture> GetColorSwapchainImage(void* pDrawable, Uint32 ViewIndex);
126+
127+
/// Wraps the Metal depth texture of a drawable view as a Diligent texture.
128+
129+
/// \param[in] pDrawable - Opaque `cp_drawable_t` pointer.
130+
/// \param[in] ViewIndex - Zero-based view index.
131+
///
132+
/// \return A reference to the Diligent texture, or null on failure.
133+
RefCntAutoPtr<ITexture> GetDepthSwapchainImage(void* pDrawable, Uint32 ViewIndex);
134+
135+
/// Returns the reverse-Z projection matrix for a view in a drawable.
136+
137+
/// The compositor mandates reverse-Z depth: the matrix maps the near plane to 1 and the
138+
/// far plane to 0. Clear the depth buffer to 0 and use `COMPARISON_FUNC_GREATER_EQUAL`
139+
/// in the PSO.
140+
///
141+
/// \param[in] pDrawable - Opaque `cp_drawable_t` pointer.
142+
/// \param[in] ViewIndex - Zero-based view index.
143+
/// \param[in] NearZ - Near clipping plane distance.
144+
/// \param[in] FarZ - Far clipping plane distance.
145+
float4x4 GetProjectionMatrix(void* pDrawable, Uint32 ViewIndex, float NearZ, float FarZ) const;
146+
147+
/// Returns the world-to-eye view matrix for a view in a drawable.
148+
149+
/// When world tracking is enabled and the ARKit session has produced at least one anchor,
150+
/// the matrix accounts for the device pose in the world. Otherwise it yields a drawable-
151+
/// local view matrix (scene rigidly attached to the headset).
152+
///
153+
/// \param[in] pDrawable - Opaque `cp_drawable_t` pointer.
154+
/// \param[in] ViewIndex - Zero-based view index.
155+
float4x4 GetViewMatrix(void* pDrawable, Uint32 ViewIndex) const;
156+
157+
/// Encodes a present command for the drawable into the immediate context's Metal command
158+
/// buffer.
159+
160+
/// Call after issuing all draw calls for the drawable, before `Flush`.
161+
///
162+
/// \param[in] pDrawable - Opaque `cp_drawable_t` pointer.
163+
void PresentDrawable(void* pDrawable);
164+
165+
private:
166+
struct Impl;
167+
std::unique_ptr<Impl> m_Impl;
168+
};
169+
170+
} // namespace Diligent

0 commit comments

Comments
 (0)