• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Razakhel / RaZ / 18059902851

27 Sep 2025 12:32PM UTC coverage: 74.093% (+0.04%) from 74.05%
18059902851

push

github

Razakhel
[Utils/Logger] Added formatted logging overloads

- Formatted calls to logging functions and made use of std::format() in several other places

100 of 170 new or added lines in 36 files covered. (58.82%)

4 existing lines in 2 files now uncovered.

8334 of 11248 relevant lines covered (74.09%)

1757.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/RaZ/XR/XrSession.cpp
1
#include "RaZ/Math/Quaternion.hpp"
2
#include "RaZ/Render/Renderer.hpp"
3
#include "RaZ/Render/RenderPass.hpp"
4
#include "RaZ/Utils/Logger.hpp"
5
#include "RaZ/XR/XrContext.hpp"
6
#include "RaZ/XR/XrSession.hpp"
7

8
#if defined(XR_OS_WINDOWS)
9
#define WIN32_LEAN_AND_MEAN
10
#include <Windows.h>
11
#include <Unknwn.h>
12
#endif
13

14
#include "GL/glew.h"
15
#if defined(XR_OS_WINDOWS)
16
#define GLFW_EXPOSE_NATIVE_WIN32
17
#define GLFW_EXPOSE_NATIVE_WGL
18
#elif defined(XR_OS_LINUX)
19
#define GLFW_EXPOSE_NATIVE_X11
20
#define GLFW_EXPOSE_NATIVE_GLX
21
#endif
22
#include "GLFW/glfw3.h"
23
#include "GLFW/glfw3native.h"
24

25
#include "openxr/openxr.h"
26
#include "openxr/openxr_platform.h"
27

28
#include "tracy/Tracy.hpp"
29
#include "tracy/TracyOpenGL.hpp"
30

31
#include <algorithm>
32
#include <array>
33
#include <stdexcept>
34

35
namespace Raz {
36

37
namespace {
38

39
constexpr std::string_view swapchainCopySource = R"(
40
  in vec2 fragTexcoords;
41

42
  uniform sampler2D uniFinalColorBuffer;
43
  uniform sampler2D uniFinalDepthBuffer;
44

45
  layout(location = 0) out vec4 fragColor;
46

47
  void main() {
48
    fragColor     = texture(uniFinalColorBuffer, fragTexcoords).rgba;
49
    // Gamma uncorrection, as the swapchain seems to apply it itself
50
    fragColor.rgb = pow(fragColor.rgb, vec3(2.2));
51
    gl_FragDepth  = texture(uniFinalDepthBuffer, fragTexcoords).r;
52
  }
53
)";
54

55
const char* getResultStr(XrInstance instance, XrResult result) {
×
56
  static std::array<char, XR_MAX_RESULT_STRING_SIZE> errorStr {};
57
  xrResultToString(instance, result, errorStr.data());
×
58
  return errorStr.data();
×
59
}
60

61
std::string getErrorStr(const std::string& errorMsg, XrResult result, XrInstance instance) {
×
62
  return "[XrSession] " + errorMsg + ": " + getResultStr(instance, result) + " (" + std::to_string(result) + ')';
×
63
}
64

65
void checkLog(XrResult result, const std::string& errorMsg, XrInstance instance) {
×
66
  if (XR_SUCCEEDED(result))
×
67
    return;
×
68

69
  Logger::error(getErrorStr(errorMsg, result, instance));
×
70
}
71

72
void checkThrow(XrResult result, const std::string& errorMsg, XrInstance instance) {
×
73
  if (XR_SUCCEEDED(result))
×
74
    return;
×
75

76
  throw std::runtime_error(getErrorStr(errorMsg, result, instance));
×
77
}
78

79
#if defined(XR_USE_PLATFORM_WIN32)
80
using GraphicsBinding = XrGraphicsBindingOpenGLWin32KHR;
81
#elif defined(XR_USE_PLATFORM_XLIB)
82
using GraphicsBinding = XrGraphicsBindingOpenGLXlibKHR;
83
#endif
84

85
GraphicsBinding getGraphicsBinding() {
×
86
  GLFWwindow* window = glfwGetCurrentContext();
×
87
  GraphicsBinding graphicsBinding {};
×
88

89
#if defined(XR_USE_PLATFORM_WIN32)
90
  graphicsBinding.type  = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
91
  graphicsBinding.hDC   = GetDC(glfwGetWin32Window(window));
92
  graphicsBinding.hGLRC = glfwGetWGLContext(window);
93
#elif defined(XR_USE_PLATFORM_XLIB)
94
  Display* x11Display = glfwGetX11Display();
×
95

96
  // TODO: some fields can't be directly filled with what GLFW exposes; see https://github.com/glfw/glfw/issues/2129
97

98
  // TODO: unless there's a way to easily recover the FBConfig from a GLXContext (related GLFW PR: https://github.com/glfw/glfw/pull/1925), it has to be done
99
  //  manually; see https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/dfe3ad67f11ab71a64b2c75d6b7a97034b9766fd/src/common/gfxwrapper_opengl.c#L1016-L1077
100
  GLXFBConfig fbConfig {};
×
101
  graphicsBinding.type        = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
×
102
  graphicsBinding.xDisplay    = x11Display;
×
103
  graphicsBinding.visualid    = static_cast<uint32_t>(glXGetVisualFromFBConfig(x11Display, fbConfig)->visualid);
×
104
  graphicsBinding.glxFBConfig = fbConfig;
×
105
  graphicsBinding.glxDrawable = glXGetCurrentDrawable();
×
106
  graphicsBinding.glxContext  = glfwGetGLXContext(window);
×
107
#endif
108

109
  return graphicsBinding;
×
110
}
111

112
int64_t selectColorSwapchainFormat(const std::vector<int64_t>& formats) {
×
113
  constexpr std::array<int64_t, 4> supportedColorSwapchainFormats = {
×
114
    GL_RGB10_A2,
115
    GL_RGBA16F,
116
    // The following values should only be used as a fallback, as they are linear color formats without enough bits for color depth, thus leading to banding
117
    GL_RGBA8,
118
    GL_RGBA8_SNORM
119
  };
120

121
  const auto formatIter = std::find_first_of(formats.cbegin(), formats.cend(),
×
122
                                             supportedColorSwapchainFormats.cbegin(), supportedColorSwapchainFormats.cend());
123

124
  if (formatIter == formats.cend())
×
125
    return 0;
×
126

127
  return *formatIter;
×
128
}
129

130
int64_t selectDepthSwapchainFormat(const std::vector<int64_t>& formats) {
×
131
  constexpr std::array<int64_t, 4> supportedDepthSwapchainFormats = {
×
132
    GL_DEPTH_COMPONENT32F,
133
    GL_DEPTH_COMPONENT32,
134
    GL_DEPTH_COMPONENT24,
135
    GL_DEPTH_COMPONENT16
136
  };
137

138
  const auto formatIter = std::find_first_of(formats.cbegin(), formats.cend(),
×
139
                                             supportedDepthSwapchainFormats.cbegin(), supportedDepthSwapchainFormats.cend());
140

141
  if (formatIter == formats.cend())
×
142
    return 0;
×
143

144
  return *formatIter;
×
145
}
146

147
} // namespace
148

149
struct XrSession::RenderLayerInfo {
150
  XrTime predictedDisplayTime {};
151
  std::vector<XrCompositionLayerBaseHeader*> layers;
152
  XrCompositionLayerProjection layerProjection{ XR_TYPE_COMPOSITION_LAYER_PROJECTION };
153
  std::vector<XrCompositionLayerProjectionView> layerProjectionViews;
154
};
155

156
enum class XrSession::SwapchainType : uint8_t {
157
  COLOR,
158
  DEPTH
159
};
160

161
XrSession::XrSession(const XrContext& context) : m_instance{ context.m_instance } {
×
162
  if (m_instance == XR_NULL_HANDLE)
×
163
    throw std::runtime_error("[XrSession] The XR instance must be valid");
×
164
}
×
165

166
void XrSession::begin(unsigned int viewConfigType) {
×
167
  Logger::debug("[XrSession] Beginning session...");
×
168

169
  XrSessionBeginInfo sessionBeginInfo {};
×
170
  sessionBeginInfo.type                         = XR_TYPE_SESSION_BEGIN_INFO;
×
171
  sessionBeginInfo.primaryViewConfigurationType = static_cast<XrViewConfigurationType>(viewConfigType);
×
172
  checkLog(xrBeginSession(m_handle, &sessionBeginInfo), "Failed to begin session", m_instance);
×
173

174
  Logger::debug("[XrSession] Began session");
×
175
}
×
176

177
void XrSession::end() {
×
178
  Logger::debug("[XrSession] Ending session...");
×
179
  checkLog(xrEndSession(m_handle), "Failed to end session", m_instance);
×
180
  Logger::debug("[XrSession] Ended session");
×
181
}
×
182

183
bool XrSession::renderFrame(const std::vector<XrViewConfigurationView>& viewConfigViews,
×
184
                            unsigned int viewConfigType,
185
                            unsigned int environmentBlendMode,
186
                            const ViewRenderFunc& viewRenderFunc) const {
187
  ZoneScopedN("XrSession::renderFrame");
188

189
  if (!m_isRunning)
×
190
    return false;
×
191

192
  XrFrameWaitInfo frameWaitInfo {};
×
193
  frameWaitInfo.type = XR_TYPE_FRAME_WAIT_INFO;
×
194
  XrFrameState frameState {};
×
195
  frameState.type = XR_TYPE_FRAME_STATE;
×
196
  checkLog(xrWaitFrame(m_handle, &frameWaitInfo, &frameState), "Failed to wait for the XR frame", m_instance);
×
197

198
  XrFrameBeginInfo frameBeginInfo {};
×
199
  frameBeginInfo.type = XR_TYPE_FRAME_BEGIN_INFO;
×
200
  checkLog(xrBeginFrame(m_handle, &frameBeginInfo), "Failed to begin the XR frame", m_instance);
×
201

202
  RenderLayerInfo renderLayerInfo;
×
203
  // TODO: either the application should use this display time, or the application's global & delta times should be used here somehow
204
  //  See:
205
  //  - https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrWaitFrame.html#_description
206
  //  - https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrEndFrame.html#_description
207
  //  - https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrTime.html
208
  //  - https://registry.khronos.org/OpenXR/specs/1.0/man/html/XR_KHR_convert_timespec_time.html
209
  renderLayerInfo.predictedDisplayTime = frameState.predictedDisplayTime;
×
210

211
  const bool isSessionActive = (m_state == XR_SESSION_STATE_SYNCHRONIZED || m_state == XR_SESSION_STATE_VISIBLE || m_state == XR_SESSION_STATE_FOCUSED);
×
212
  if (isSessionActive && frameState.shouldRender && renderLayer(renderLayerInfo, viewConfigViews, viewConfigType, viewRenderFunc))
×
213
    renderLayerInfo.layers.emplace_back(reinterpret_cast<XrCompositionLayerBaseHeader*>(&renderLayerInfo.layerProjection));
×
214

215
  {
216
    ZoneNamedN(endFrameZone, "xrEndFrame", true);
217
    TracyGpuZone("xrEndFrame")
218

219
    XrFrameEndInfo frameEndInfo {};
×
220
    frameEndInfo.type                 = XR_TYPE_FRAME_END_INFO;
×
221
    frameEndInfo.displayTime          = frameState.predictedDisplayTime;
×
222
    frameEndInfo.environmentBlendMode = static_cast<XrEnvironmentBlendMode>(environmentBlendMode);
×
223
    frameEndInfo.layerCount           = static_cast<uint32_t>(renderLayerInfo.layers.size());
×
224
    frameEndInfo.layers               = renderLayerInfo.layers.data();
×
225
    checkLog(xrEndFrame(m_handle, &frameEndInfo), "Failed to end the XR frame", m_instance);
×
226
  }
227

228
  return !renderLayerInfo.layers.empty();
×
229
}
×
230

231
XrSession::~XrSession() {
×
232
  if (m_handle == XR_NULL_HANDLE)
×
233
    return;
×
234

235
  Logger::debug("[XrSession] Destroying session...");
×
236

237
  destroySwapchains();
×
238
  destroyReferenceSpace();
×
239
  checkLog(xrDestroySession(m_handle), "Failed to destroy session", m_instance);
×
240

241
  Logger::debug("[XrSession] Destroyed session");
×
242
}
×
243

244
void XrSession::initialize(uint64_t systemId) {
×
245
  ZoneScopedN("XrSession::initialize");
246

247
  Logger::debug("[XrSession] Initializing...");
×
248

249
  if (!Renderer::isInitialized())
×
250
    throw std::runtime_error("[XrSession] The renderer must be initialized");
×
251

252
  PFN_xrGetOpenGLGraphicsRequirementsKHR xrGetOpenGLGraphicsRequirementsKHR {};
×
253
  checkLog(xrGetInstanceProcAddr(m_instance,
×
254
                                 "xrGetOpenGLGraphicsRequirementsKHR",
255
                                 reinterpret_cast<PFN_xrVoidFunction*>(&xrGetOpenGLGraphicsRequirementsKHR)),
256
           "Failed to get OpenGL graphics requirements get function",
257
           m_instance);
258
  XrGraphicsRequirementsOpenGLKHR graphicsRequirements {};
×
259
  graphicsRequirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR;
×
260
  checkLog(xrGetOpenGLGraphicsRequirementsKHR(m_instance, systemId, &graphicsRequirements),
×
261
           "Failed to get graphics requirements for OpenGL", m_instance);
262

263
  const XrVersion graphicsApiVersion = XR_MAKE_VERSION(Renderer::getMajorVersion(), Renderer::getMinorVersion(), 0);
×
264
  if (graphicsRequirements.minApiVersionSupported > graphicsApiVersion) {
×
265
    const uint16_t requiredMajorVersion = XR_VERSION_MAJOR(graphicsRequirements.minApiVersionSupported);
×
266
    const uint16_t requiredMinorVersion = XR_VERSION_MINOR(graphicsRequirements.minApiVersionSupported);
×
267
    throw std::runtime_error("[XrSession] The current OpenGL version "
268
                             + std::to_string(Renderer::getMajorVersion()) + '.' + std::to_string(Renderer::getMinorVersion())
×
269
                             + " does not meet the minimum required version "
×
270
                             + std::to_string(requiredMajorVersion) + '.' + std::to_string(requiredMinorVersion)
×
271
                             + " for OpenXR");
×
272
  }
273

274
  const GraphicsBinding graphicsBinding = getGraphicsBinding();
×
275
  XrSessionCreateInfo sessionCreateInfo {};
×
276
  sessionCreateInfo.type        = XR_TYPE_SESSION_CREATE_INFO;
×
277
  sessionCreateInfo.next        = &graphicsBinding;
×
278
  sessionCreateInfo.createFlags = 0;
×
279
  sessionCreateInfo.systemId    = systemId;
×
280
  checkThrow(xrCreateSession(m_instance, &sessionCreateInfo, &m_handle), "Failed to create session", m_instance);
×
281

282
  createReferenceSpace();
×
283

284
  Logger::debug("[XrSession] Initialized");
×
285
}
×
286

287
void XrSession::createReferenceSpace() {
×
288
  ZoneScopedN("XrSession::createReferenceSpace");
289

290
  Logger::debug("[XrSession] Creating reference space...");
×
291

292
  XrReferenceSpaceCreateInfo referenceSpaceCreateInfo {};
×
293
  referenceSpaceCreateInfo.type                 = XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
×
294
  referenceSpaceCreateInfo.referenceSpaceType   = XR_REFERENCE_SPACE_TYPE_LOCAL;
×
295
  referenceSpaceCreateInfo.poseInReferenceSpace = { XrQuaternionf{ 0.f, 0.f, 0.f, 1.f }, XrVector3f{ 0.f, 0.f, 0.f }};
×
296
  checkLog(xrCreateReferenceSpace(m_handle, &referenceSpaceCreateInfo, &m_localSpace), "Failed to create reference space", m_instance);
×
297

298
  Logger::debug("[XrSession] Created reference space");
×
299
}
×
300

301
void XrSession::destroyReferenceSpace() {
×
302
  Logger::debug("[XrSession] Destroying reference space...");
×
303
  checkLog(xrDestroySpace(m_localSpace), "Failed to destroy space", m_instance);
×
304
  Logger::debug("[XrSession] Destroyed reference space");
×
305
}
×
306

307
void XrSession::createSwapchains(const std::vector<XrViewConfigurationView>& viewConfigViews) {
×
308
  ZoneScopedN("XrSession::createSwapchains");
309

310
  Logger::debug("[XrSession] Creating swapchains...");
×
311

312
  if (m_handle == nullptr)
×
313
    throw std::runtime_error("[XrSession] The session has not been initialized");
×
314

315
  uint32_t formatCount {};
×
316
  checkLog(xrEnumerateSwapchainFormats(m_handle, 0, &formatCount, nullptr), "Failed to get swapchain format count", m_instance);
×
317
  std::vector<int64_t> formats(formatCount);
×
318
  checkLog(xrEnumerateSwapchainFormats(m_handle, formatCount, &formatCount, formats.data()), "Failed to enumerate swapchain formats", m_instance);
×
319

320
  if (selectDepthSwapchainFormat(formats) == 0)
×
321
    Logger::error("[XrSession] Failed to find a supported depth swapchain format");
×
322

323
  m_colorSwapchains.resize(viewConfigViews.size());
×
324
  m_depthSwapchains.resize(viewConfigViews.size());
×
325
  m_swapchainImages.reserve(viewConfigViews.size());
×
326

327
  for (std::size_t viewIndex = 0; viewIndex < viewConfigViews.size(); ++viewIndex) {
×
328
    XrSwapchain& colorSwapchain = m_colorSwapchains[viewIndex];
×
329
    XrSwapchain& depthSwapchain = m_depthSwapchains[viewIndex];
×
330

331
    XrSwapchainCreateInfo swapchainCreateInfo {};
×
332
    swapchainCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO;
×
333

334
    swapchainCreateInfo.createFlags = 0;
×
335
    swapchainCreateInfo.usageFlags  = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; // Technically ignored with OpenGL
×
336
    swapchainCreateInfo.format      = selectColorSwapchainFormat(formats);
×
337
    swapchainCreateInfo.sampleCount = viewConfigViews[viewIndex].recommendedSwapchainSampleCount;
×
338
    swapchainCreateInfo.width       = viewConfigViews[viewIndex].recommendedImageRectWidth;
×
339
    swapchainCreateInfo.height      = viewConfigViews[viewIndex].recommendedImageRectHeight;
×
340
    swapchainCreateInfo.faceCount   = 1;
×
341
    swapchainCreateInfo.arraySize   = 1;
×
342
    swapchainCreateInfo.mipCount    = 1;
×
343
    checkLog(xrCreateSwapchain(m_handle, &swapchainCreateInfo, &colorSwapchain), "Failed to create color swapchain", m_instance);
×
344

345
    swapchainCreateInfo.createFlags = 0;
×
346
    swapchainCreateInfo.usageFlags  = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; // Technically ignored with OpenGL
×
347
    swapchainCreateInfo.format      = selectDepthSwapchainFormat(formats);
×
348
    swapchainCreateInfo.sampleCount = viewConfigViews[viewIndex].recommendedSwapchainSampleCount;
×
349
    swapchainCreateInfo.width       = viewConfigViews[viewIndex].recommendedImageRectWidth;
×
350
    swapchainCreateInfo.height      = viewConfigViews[viewIndex].recommendedImageRectHeight;
×
351
    swapchainCreateInfo.faceCount   = 1;
×
352
    swapchainCreateInfo.arraySize   = 1;
×
353
    swapchainCreateInfo.mipCount    = 1;
×
354
    checkLog(xrCreateSwapchain(m_handle, &swapchainCreateInfo, &depthSwapchain), "Failed to create depth swapchain", m_instance);
×
355

356
    createSwapchainImages(colorSwapchain, SwapchainType::COLOR);
×
357
    createSwapchainImages(depthSwapchain, SwapchainType::DEPTH);
×
358
  }
359

360
  Logger::debug("[XrSession] Created swapchains");
×
361
}
×
362

363
void XrSession::destroySwapchains() {
×
364
  ZoneScopedN("XrSession::destroySwapchains");
365

366
  Logger::debug("[XrSession] Destroying swapchains...");
×
367

368
  for (std::size_t swapchainIndex = 0; swapchainIndex < m_colorSwapchains.size(); ++swapchainIndex) {
×
369
    checkLog(xrDestroySwapchain(m_colorSwapchains[swapchainIndex]), "Failed to destroy color swapchain", m_instance);
×
370
    checkLog(xrDestroySwapchain(m_depthSwapchains[swapchainIndex]), "Failed to destroy depth swapchain", m_instance);
×
371
  }
372

373
  m_swapchainImages.clear();
×
374

375
  Logger::debug("[XrSession] Destroyed swapchains");
×
376
}
×
377

378
void XrSession::createSwapchainImages(XrSwapchain swapchain, SwapchainType swapchainType) {
×
379
  ZoneScopedN("XrSession::createSwapchainImages");
380

NEW
381
  const std::string_view typeStr = (swapchainType == SwapchainType::DEPTH ? "depth" : "color");
×
382

NEW
383
  Logger::debug("[XrSession] Creating {} swapchain images...", typeStr);
×
384

385
  uint32_t swapchainImageCount {};
×
386
  checkLog(xrEnumerateSwapchainImages(swapchain, 0, &swapchainImageCount, nullptr),
×
NEW
387
           std::format("Failed to get {} swapchain image count", typeStr),
×
388
           m_instance);
389

390
  std::vector<XrSwapchainImageOpenGLKHR>& images = m_swapchainImages[swapchain];
×
391
  images.resize(swapchainImageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR });
×
392
  checkLog(xrEnumerateSwapchainImages(swapchain, swapchainImageCount, &swapchainImageCount,
×
393
                                      reinterpret_cast<XrSwapchainImageBaseHeader*>(images.data())),
×
NEW
394
           std::format("Failed to enumerate {} swapchain images", typeStr),
×
395
           m_instance);
396

NEW
397
  Logger::debug("[XrSession] Created {} swapchain images", typeStr);
×
398
}
×
399

400
bool XrSession::renderLayer(RenderLayerInfo& layerInfo,
×
401
                            const std::vector<XrViewConfigurationView>& viewConfigViews,
402
                            unsigned int viewConfigType,
403
                            const ViewRenderFunc& viewRenderFunc) const {
404
  ZoneScopedN("XrSession::renderLayer");
405

406
  std::vector<XrView> views(m_swapchainImages.size(), { XR_TYPE_VIEW });
×
407

408
  XrViewLocateInfo viewLocateInfo {};
×
409
  viewLocateInfo.type                  = XR_TYPE_VIEW_LOCATE_INFO;
×
410
  viewLocateInfo.viewConfigurationType = static_cast<XrViewConfigurationType>(viewConfigType);
×
411
  viewLocateInfo.displayTime           = layerInfo.predictedDisplayTime;
×
412
  viewLocateInfo.space                 = m_localSpace;
×
413
  XrViewState viewState {};
×
414
  viewState.type = XR_TYPE_VIEW_STATE;
×
415
  uint32_t viewCount {};
×
416
  if (xrLocateViews(m_handle, &viewLocateInfo, &viewState, static_cast<uint32_t>(views.size()), &viewCount, views.data()) != XR_SUCCESS) {
×
417
    Logger::error("[XrSession] Failed to locate views");
×
418
    return false;
×
419
  }
420

421
  // TODO: view state flags must be checked; see: https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrViewStateFlagBits.html#_description
422

423
  layerInfo.layerProjectionViews.resize(viewCount, { XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW });
×
424

425
  for (uint32_t viewIndex = 0; viewIndex < viewCount; ++viewIndex) {
×
426
    [[maybe_unused]] const char* eyeStr = (viewCount == 1 ? "Single view"
×
427
                                        : (viewIndex == 0 ? "Left eye"
×
428
                                                          : "Right eye"));
429

430
    ZoneTransientN(cpuEyeZone, eyeStr, true);
431

432
    const XrSwapchain colorSwapchain = m_colorSwapchains[viewIndex];
×
433
    const XrSwapchain depthSwapchain = m_depthSwapchains[viewIndex];
×
434

435
    XrSwapchainImageAcquireInfo acquireInfo {};
×
436
    acquireInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO;
×
437
    uint32_t colorImageIndex {};
×
438
    uint32_t depthImageIndex {};
×
439
    checkLog(xrAcquireSwapchainImage(colorSwapchain, &acquireInfo, &colorImageIndex),
×
440
             "Failed to acquire image from the color swapchain",
441
             m_instance);
×
442
    checkLog(xrAcquireSwapchainImage(depthSwapchain, &acquireInfo, &depthImageIndex),
×
443
             "Failed to acquire image from the depth swapchain",
444
             m_instance);
×
445

446
    XrSwapchainImageWaitInfo waitInfo {};
×
447
    waitInfo.type    = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO;
×
448
    waitInfo.timeout = XR_INFINITE_DURATION;
×
449
    checkLog(xrWaitSwapchainImage(colorSwapchain, &waitInfo), "Failed to wait for image from the color swapchain", m_instance);
×
450
    checkLog(xrWaitSwapchainImage(depthSwapchain, &waitInfo), "Failed to wait for image from the depth swapchain", m_instance);
×
451

452
    const uint32_t width  = viewConfigViews[viewIndex].recommendedImageRectWidth;
×
453
    const uint32_t height = viewConfigViews[viewIndex].recommendedImageRectHeight;
×
454

455
    const XrView& currentView = views[viewIndex];
×
456

457
    XrCompositionLayerProjectionView& layerProjectionView = layerInfo.layerProjectionViews[viewIndex];
×
458
    layerProjectionView.type                      = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
×
459
    layerProjectionView.pose                      = currentView.pose;
×
460
    layerProjectionView.fov                       = currentView.fov;
×
461
    layerProjectionView.subImage.swapchain        = colorSwapchain;
×
462
    layerProjectionView.subImage.imageRect.offset = { 0, 0 };
×
463
    layerProjectionView.subImage.imageRect.extent = { static_cast<int32_t>(width), static_cast<int32_t>(height) };
×
464
    layerProjectionView.subImage.imageArrayIndex  = 0;  // Useful for multiview rendering
×
465

466
    TracyGpuZoneTransient(gpuEyeZone, eyeStr, true)
467

468
#if defined(RAZ_CONFIG_DEBUG)
469
    if (Renderer::checkVersion(4, 3))
×
470
      Renderer::pushDebugGroup(eyeStr);
×
471
#endif
472

473
    const auto& [colorBuffer, depthBuffer] = viewRenderFunc(Vec3f(currentView.pose.position.x, currentView.pose.position.y, currentView.pose.position.z),
×
474
                                                            Quaternionf(currentView.pose.orientation.w, currentView.pose.orientation.x,
×
475
                                                                        currentView.pose.orientation.y, currentView.pose.orientation.z),
×
476
                                                            ViewFov{ Radiansf(currentView.fov.angleRight), Radiansf(currentView.fov.angleLeft),
×
477
                                                                     Radiansf(currentView.fov.angleUp), Radiansf(currentView.fov.angleDown) });
×
478

479
    const uint32_t colorSwapchainImage = m_swapchainImages.find(colorSwapchain)->second[colorImageIndex].image;
×
480
    const uint32_t depthSwapchainImage = m_swapchainImages.find(depthSwapchain)->second[depthImageIndex].image;
×
481
    copyToSwapchains(colorBuffer, depthBuffer, colorSwapchainImage, depthSwapchainImage);
×
482

483
#if defined(RAZ_CONFIG_DEBUG)
484
    if (Renderer::checkVersion(4, 3))
×
485
      Renderer::popDebugGroup();
×
486
#endif
487

488
    XrSwapchainImageReleaseInfo releaseInfo {};
×
489
    releaseInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO;
×
490
    checkLog(xrReleaseSwapchainImage(colorSwapchain, &releaseInfo), "Failed to release image back to the color swapchain", m_instance);
×
491
    checkLog(xrReleaseSwapchainImage(depthSwapchain, &releaseInfo), "Failed to release image back to the depth swapchain", m_instance);
×
492
  }
493

494
  layerInfo.layerProjection.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT
×
495
                                       | XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; // Should be deprecated and ignored
496
  layerInfo.layerProjection.space      = m_localSpace;
×
497
  layerInfo.layerProjection.viewCount  = static_cast<uint32_t>(layerInfo.layerProjectionViews.size());
×
498
  layerInfo.layerProjection.views      = layerInfo.layerProjectionViews.data();
×
499

500
  return true;
×
501
}
×
502

503
void XrSession::copyToSwapchains(const Texture2D& colorBuffer, const Texture2D& depthBuffer, uint32_t colorSwapchainImage, uint32_t depthSwapchainImage) const {
×
504
  // https://docs.gl/gl4/glCopyImageSubData *could* be a viable and more direct solution, but expects both textures to have compatible internal formats
505
  //  (https://registry.khronos.org/OpenGL/specs/gl/glspec46.core.pdf#page=295), which we simply cannot have any guarantee of
506

507
  ZoneScopedN("XrSession::copyToSwapchains");
508
  TracyGpuZone("XrSession::copyToSwapchains")
509

510
  static RenderPass swapchainCopyPass = [] () {
×
511
    RenderPass copyPass(FragmentShader::loadFromSource(swapchainCopySource), "Swapchain copy pass");
×
512

513
    RenderShaderProgram& copyProgram = copyPass.getProgram();
×
514
    copyProgram.setAttribute(0, "uniFinalColorBuffer");
×
515
    copyProgram.setAttribute(1, "uniFinalDepthBuffer");
×
516

517
    constexpr DrawBuffer drawBuffer = DrawBuffer::COLOR_ATTACHMENT0;
×
518
    Renderer::bindFramebuffer(copyPass.getFramebuffer().getIndex(), FramebufferType::DRAW_FRAMEBUFFER);
×
519
    Renderer::setDrawBuffers(1, &drawBuffer);
×
520
    Renderer::bindFramebuffer(0);
×
521

522
    return copyPass;
×
523
  }();
×
524

525
  swapchainCopyPass.getProgram().use();
×
526
  Renderer::setActiveTexture(0);
×
527
  colorBuffer.bind();
×
528
  Renderer::setActiveTexture(1);
×
529
  depthBuffer.bind();
×
530

531
  Renderer::bindFramebuffer(swapchainCopyPass.getFramebuffer().getIndex(), FramebufferType::DRAW_FRAMEBUFFER);
×
532
  Renderer::setFramebufferTexture2D(FramebufferAttachment::COLOR0, colorSwapchainImage, 0);
×
533
  Renderer::setFramebufferTexture2D(FramebufferAttachment::DEPTH, depthSwapchainImage, 0);
×
534
  Renderer::clear(MaskType::COLOR | MaskType::DEPTH | MaskType::STENCIL);
×
535

536
  Renderer::setDepthFunction(DepthStencilFunction::ALWAYS);
×
537
  swapchainCopyPass.execute();
×
538
  Renderer::setDepthFunction(DepthStencilFunction::LESS);
×
539
}
×
540

541
} // namespace Raz
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc