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

Razakhel / RaZ / 11823221355

13 Nov 2024 06:08PM UTC coverage: 74.338% (-0.06%) from 74.4%
11823221355

push

github

Razakhel
[XR/XrSession] Delayed the session's actual creation

- This allows creating an XR system & context, therefore allowing getting the XR device's info like its views dimensions, before initializing anything related to rendering
  - The optimal view width & height (the recommended image rect. size for the device's views) can be recovered from the XrSystem
  - These dimensions are used to create the window in the XR demo

- Due to the necessary delayed rendering initialization, the swapchain copy pass is now a static local variable in the corresponding function, just like the window copy pass

0 of 76 new or added lines in 4 files covered. (0.0%)

3 existing lines in 2 files now uncovered.

8059 of 10841 relevant lines covered (74.34%)

1816.94 hits per line

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

59.05
/src/RaZ/Render/RenderSystem.cpp
1
#include "RaZ/Application.hpp"
2
#include "RaZ/Data/Image.hpp"
3
#include "RaZ/Data/ImageFormat.hpp"
4
#include "RaZ/Math/Transform.hpp"
5
#include "RaZ/Render/Camera.hpp"
6
#include "RaZ/Render/Light.hpp"
7
#include "RaZ/Render/MeshRenderer.hpp"
8
#include "RaZ/Render/Renderer.hpp"
9
#include "RaZ/Render/RenderSystem.hpp"
10
#if defined(RAZ_USE_XR)
11
#include "RaZ/XR/XrSystem.hpp"
12
#endif
13

14
#include "tracy/Tracy.hpp"
15
#include "GL/glew.h" // Needed by TracyOpenGL.hpp
16
#include "tracy/TracyOpenGL.hpp"
17

18
namespace Raz {
19

20
void RenderSystem::setCubemap(Cubemap&& cubemap) {
2✔
21
  m_cubemap = std::move(cubemap);
2✔
22
  m_cameraUbo.bindUniformBlock(m_cubemap->getProgram(), "uboCameraInfo", 0);
2✔
23
}
2✔
24

25
#if defined(RAZ_USE_XR)
26
void RenderSystem::enableXr(XrSystem& xrSystem) {
×
27
  m_xrSystem = &xrSystem;
×
28

NEW
29
  xrSystem.initializeSession();
×
NEW
30
  resizeViewport(xrSystem.getOptimalViewWidth(), xrSystem.getOptimalViewHeight());
×
UNCOV
31
}
×
32
#endif
33

34
void RenderSystem::resizeViewport(unsigned int width, unsigned int height) {
21✔
35
  ZoneScopedN("RenderSystem::resizeViewport");
36

37
  m_sceneWidth  = width;
21✔
38
  m_sceneHeight = height;
21✔
39

40
  Renderer::resizeViewport(0, 0, m_sceneWidth, m_sceneHeight);
21✔
41

42
  if (m_cameraEntity)
21✔
43
    m_cameraEntity->getComponent<Camera>().resizeViewport(m_sceneWidth, m_sceneHeight);
×
44

45
  m_renderGraph.resizeViewport(m_sceneWidth, m_sceneHeight);
21✔
46
}
21✔
47

48
bool RenderSystem::update(const FrameTimeInfo& timeInfo) {
21✔
49
  ZoneScopedN("RenderSystem::update");
50
  TracyGpuZone("RenderSystem::update")
51

52
  m_cameraUbo.bindBase(0);
21✔
53
  m_lightsUbo.bindBase(1);
21✔
54
  m_timeUbo.bindBase(2);
21✔
55
  m_modelUbo.bindBase(3);
21✔
56

57
  // TODO: this should be made only once at the passes' shader programs' initialization (as is done when updating shaders), not every frame
58
  //   Forcing to update shaders when adding a new pass would not be ideal either, as it implies many operations. Find a better & user-friendly way
59
  for (std::size_t i = 0; i < m_renderGraph.getNodeCount(); ++i) {
34✔
60
    const RenderShaderProgram& passProgram = m_renderGraph.getNode(i).getProgram();
13✔
61
    m_cameraUbo.bindUniformBlock(passProgram, "uboCameraInfo", 0);
13✔
62
    m_lightsUbo.bindUniformBlock(passProgram, "uboLightsInfo", 1);
13✔
63
    m_timeUbo.bindUniformBlock(passProgram, "uboTimeInfo", 2);
13✔
64
  }
65

66
  m_timeUbo.bind();
21✔
67
  m_timeUbo.sendData(timeInfo.deltaTime, 0);
21✔
68
  m_timeUbo.sendData(timeInfo.globalTime, sizeof(float));
21✔
69

70
#if defined(RAZ_USE_XR)
71
  if (m_xrSystem) {
21✔
72
    renderXrFrame();
×
73
  } else
74
#endif
75
  {
76
    sendCameraInfo();
21✔
77
    m_renderGraph.execute(*this);
21✔
78
  }
79

80
#if defined(RAZ_CONFIG_DEBUG) && !defined(SKIP_RENDERER_ERRORS)
81
  Renderer::printErrors();
82
#endif
83

84
#if !defined(RAZ_NO_WINDOW)
85
  if (m_window)
21✔
86
    return m_window->run(timeInfo.deltaTime);
×
87
#endif
88

89
  return true;
21✔
90
}
91

92
void RenderSystem::updateLights() const {
7✔
93
  ZoneScopedN("RenderSystem::updateLights");
94

95
  unsigned int lightCount = 0;
7✔
96

97
  m_lightsUbo.bind();
7✔
98

99
  for (const Entity* entity : m_entities) {
35✔
100
    if (!entity->isEnabled() || !entity->hasComponent<Light>())
28✔
101
      continue;
12✔
102

103
    updateLight(*entity, lightCount);
16✔
104
    ++lightCount;
16✔
105
  }
106

107
  m_lightsUbo.sendData(lightCount, sizeof(Vec4f) * 4 * 100);
7✔
108
}
7✔
109

110
void RenderSystem::updateShaders() const {
1✔
111
  ZoneScopedN("RenderSystem::updateShaders");
112

113
  m_renderGraph.updateShaders();
1✔
114

115
  for (std::size_t i = 0; i < m_renderGraph.getNodeCount(); ++i) {
1✔
116
    const RenderShaderProgram& passProgram = m_renderGraph.getNode(i).getProgram();
×
117
    m_cameraUbo.bindUniformBlock(passProgram, "uboCameraInfo", 0);
×
118
    m_lightsUbo.bindUniformBlock(passProgram, "uboLightsInfo", 1);
×
119
    m_timeUbo.bindUniformBlock(passProgram, "uboTimeInfo", 2);
×
120
  }
121

122
  for (Entity* entity : m_entities) {
1✔
123
    if (!entity->hasComponent<MeshRenderer>())
×
124
      continue;
×
125

126
    auto& meshRenderer = entity->getComponent<MeshRenderer>();
×
127

128
    for (Material& material : meshRenderer.getMaterials())
×
129
      material.getProgram().updateShaders();
×
130

131
    updateMaterials(meshRenderer);
×
132
  }
133
}
1✔
134

135
void RenderSystem::updateMaterials(const MeshRenderer& meshRenderer) const {
3✔
136
  ZoneScopedN("RenderSystem::updateMaterials(MeshRenderer)");
137

138
  for (const Material& material : meshRenderer.getMaterials()) {
5✔
139
    const RenderShaderProgram& materialProgram = material.getProgram();
2✔
140

141
    materialProgram.sendAttributes();
2✔
142
    materialProgram.initTextures();
2✔
143
#if !defined(USE_WEBGL)
144
    materialProgram.initImageTextures();
2✔
145
#endif
146

147
    m_cameraUbo.bindUniformBlock(materialProgram, "uboCameraInfo", 0);
2✔
148
    m_lightsUbo.bindUniformBlock(materialProgram, "uboLightsInfo", 1);
2✔
149
    m_timeUbo.bindUniformBlock(materialProgram, "uboTimeInfo", 2);
2✔
150
    m_modelUbo.bindUniformBlock(materialProgram, "uboModelInfo", 3);
2✔
151
  }
152
}
3✔
153

154
void RenderSystem::updateMaterials() const {
1✔
155
  ZoneScopedN("RenderSystem::updateMaterials");
156

157
  for (const Entity* entity : m_entities) {
1✔
158
    if (entity->hasComponent<MeshRenderer>())
×
159
      updateMaterials(entity->getComponent<MeshRenderer>());
×
160
  }
161
}
1✔
162

163
void RenderSystem::saveToImage(const FilePath& filePath, TextureFormat format, PixelDataType dataType) const {
3✔
164
  ZoneScopedN("RenderSystem::saveToImage");
165

166
  ImageColorspace colorspace = ImageColorspace::RGB;
3✔
167

168
  switch (format) {
3✔
169
    case TextureFormat::DEPTH:
1✔
170
      colorspace = ImageColorspace::GRAY;
1✔
171
      dataType   = PixelDataType::FLOAT;
1✔
172
      break;
1✔
173

174
    case TextureFormat::RGBA:
1✔
175
    case TextureFormat::BGRA:
176
      colorspace = ImageColorspace::RGBA;
1✔
177
      break;
1✔
178

179
    default:
1✔
180
      break;
1✔
181
  }
182

183
  Image img(m_sceneWidth, m_sceneHeight, colorspace, (dataType == PixelDataType::FLOAT ? ImageDataType::FLOAT : ImageDataType::BYTE));
6✔
184
  Renderer::recoverFrame(m_sceneWidth, m_sceneHeight, format, dataType, img.getDataPtr());
3✔
185

186
  ImageFormat::save(filePath, img, true);
3✔
187
}
3✔
188

189
void RenderSystem::destroy() {
1✔
190
#if !defined(RAZ_NO_WINDOW)
191
  if (m_window)
1✔
192
    m_window->setShouldClose();
×
193
#endif
194
}
1✔
195

196
void RenderSystem::linkEntity(const EntityPtr& entity) {
20✔
197
  ZoneScopedN("RenderSystem::linkEntity");
198

199
  System::linkEntity(entity);
20✔
200

201
  if (entity->hasComponent<Camera>())
20✔
202
    m_cameraEntity = entity.get();
11✔
203

204
  if (entity->hasComponent<Light>())
20✔
205
    updateLights();
6✔
206

207
  if (entity->hasComponent<MeshRenderer>())
20✔
208
    updateMaterials(entity->getComponent<MeshRenderer>());
3✔
209
}
20✔
210

211
void RenderSystem::initialize() {
23✔
212
  ZoneScopedN("RenderSystem::initialize");
213

214
  registerComponents<Camera, Light, MeshRenderer>();
23✔
215

216
  // TODO: this Renderer initialization is technically useless; the RenderSystem needs to have it initialized before construction
217
  //  (either manually or through the Window's initialization), since it constructs the RenderGraph's rendering objects
218
  //  As such, if reaching here, the Renderer is necessarily already functional. Ideally, this call below should be the only one in the whole program
219
  Renderer::initialize();
23✔
220
  Renderer::enable(Capability::CULL);
23✔
221
  Renderer::enable(Capability::BLEND);
23✔
222
  Renderer::enable(Capability::DEPTH_TEST);
23✔
223
  Renderer::enable(Capability::STENCIL_TEST);
23✔
224
#if !defined(USE_OPENGL_ES)
225
  Renderer::enable(Capability::CUBEMAP_SEAMLESS);
23✔
226
#endif
227

228
#if !defined(USE_OPENGL_ES)
229
  // Setting the depth to a [0; 1] range instead of a [-1; 1] one is always a good thing, since the [-1; 0] subrange is never used anyway
230
  if (Renderer::checkVersion(4, 5) || Renderer::isExtensionSupported("GL_ARB_clip_control"))
23✔
231
    Renderer::setClipControl(ClipOrigin::LOWER_LEFT, ClipDepth::ZERO_TO_ONE);
23✔
232

233
  if (Renderer::checkVersion(4, 3)) {
23✔
234
    Renderer::setLabel(RenderObjectType::BUFFER, m_cameraUbo.getIndex(), "Camera uniform buffer");
23✔
235
    Renderer::setLabel(RenderObjectType::BUFFER, m_lightsUbo.getIndex(), "Lights uniform buffer");
23✔
236
    Renderer::setLabel(RenderObjectType::BUFFER, m_timeUbo.getIndex(), "Time uniform buffer");
23✔
237
    Renderer::setLabel(RenderObjectType::BUFFER, m_modelUbo.getIndex(), "Model uniform buffer");
23✔
238
  }
239
#endif
240
}
23✔
241

242
void RenderSystem::initialize(unsigned int sceneWidth, unsigned int sceneHeight) {
6✔
243
  initialize();
6✔
244
  resizeViewport(sceneWidth, sceneHeight);
6✔
245
}
6✔
246

247
void RenderSystem::sendCameraInfo() const {
21✔
248
  assert("Error: The render system needs a camera to send its info." && (m_cameraEntity != nullptr));
21✔
249
  assert("Error: The camera must have a transform component to send its info." && m_cameraEntity->hasComponent<Transform>());
21✔
250

251
  ZoneScopedN("RenderSystem::sendCameraInfo");
252

253
  auto& camera       = m_cameraEntity->getComponent<Camera>();
21✔
254
  auto& camTransform = m_cameraEntity->getComponent<Transform>();
21✔
255

256
  m_cameraUbo.bind();
21✔
257

258
  if (camTransform.hasUpdated()) {
21✔
259
    if (camera.getCameraType() == CameraType::LOOK_AT)
12✔
260
      camera.computeLookAt(camTransform.getPosition());
1✔
261
    else
262
      camera.computeViewMatrix(camTransform);
11✔
263

264
    camera.computeInverseViewMatrix();
12✔
265

266
    sendViewMatrix(camera.getViewMatrix());
12✔
267
    sendInverseViewMatrix(camera.getInverseViewMatrix());
12✔
268
    sendCameraPosition(camTransform.getPosition());
12✔
269

270
    camTransform.setUpdated(false);
12✔
271
  }
272

273
  sendProjectionMatrix(camera.getProjectionMatrix());
21✔
274
  sendInverseProjectionMatrix(camera.getInverseProjectionMatrix());
21✔
275
  sendViewProjectionMatrix(camera.getProjectionMatrix() * camera.getViewMatrix());
21✔
276
}
21✔
277

278
void RenderSystem::updateLight(const Entity& entity, unsigned int lightIndex) const {
16✔
279
  const auto& light = entity.getComponent<Light>();
16✔
280
  const std::size_t dataStride = sizeof(Vec4f) * 4 * lightIndex;
16✔
281

282
  if (light.getType() == LightType::DIRECTIONAL) {
16✔
283
    m_lightsUbo.sendData(Vec4f(0.f), static_cast<unsigned int>(dataStride));
11✔
284
  } else {
285
    assert("Error: A non-directional light needs to have a Transform component." && entity.hasComponent<Transform>());
5✔
286
    m_lightsUbo.sendData(Vec4f(entity.getComponent<Transform>().getPosition(), 1.f), static_cast<unsigned int>(dataStride));
5✔
287
  }
288

289
  m_lightsUbo.sendData(light.getDirection(), static_cast<unsigned int>(dataStride + sizeof(Vec4f)));
16✔
290
  m_lightsUbo.sendData(light.getColor(), static_cast<unsigned int>(dataStride + sizeof(Vec4f) * 2));
16✔
291
  m_lightsUbo.sendData(light.getEnergy(), static_cast<unsigned int>(dataStride + sizeof(Vec4f) * 3));
16✔
292
  m_lightsUbo.sendData(light.getAngle().value, static_cast<unsigned int>(dataStride + sizeof(Vec4f) * 3 + sizeof(float)));
16✔
293
}
16✔
294

295
#if defined(RAZ_USE_XR)
296
void RenderSystem::renderXrFrame() {
×
297
  ZoneScopedN("RenderSystem::renderXrFrame");
298
  TracyGpuZone("RenderSystem::renderXrFrame")
299

300
  const bool hasRendered = m_xrSystem->renderFrame([this] (Vec3f position, Quaternionf rotation, ViewFov viewFov) {
×
301
    if (m_cameraEntity) {
×
302
      const auto& camTransform = m_cameraEntity->getComponent<Transform>();
×
303
      position = camTransform.getRotation() * position + camTransform.getPosition();
×
304
      rotation = camTransform.getRotation() * rotation;
×
305
    }
306

307
    Mat4f invViewMat = rotation.computeMatrix();
×
308
    invViewMat.getElement(3, 0) = position.x();
×
309
    invViewMat.getElement(3, 1) = position.y();
×
310
    invViewMat.getElement(3, 2) = position.z();
×
311
    const Mat4f viewMat = invViewMat.inverse();
×
312

313
    const float tanAngleRight    = std::tan(viewFov.angleRight.value);
×
314
    const float tanAngleLeft     = std::tan(viewFov.angleLeft.value);
×
315
    const float tanAngleUp       = std::tan(viewFov.angleUp.value);
×
316
    const float tanAngleDown     = std::tan(viewFov.angleDown.value);
×
317
    const float invAngleWidth    = 1.f / (tanAngleRight - tanAngleLeft);
×
318
    const float invAngleHeight   = 1.f / (tanAngleUp - tanAngleDown);
×
319
    const float angleWidthDiff   = tanAngleRight + tanAngleLeft;
×
320
    const float angleHeightDiff  = tanAngleUp + tanAngleDown;
×
321
    constexpr float nearZ        = 0.1f;
×
322
    constexpr float farZ         = 1000.f;
×
323
    constexpr float invDepthDiff = 1.f / (farZ - nearZ);
×
324
    const Mat4f projMat(2.f * invAngleWidth, 0.f,                  angleWidthDiff * invAngleWidth,   0.f,
×
325
                        0.f,                 2.f * invAngleHeight, angleHeightDiff * invAngleHeight, 0.f,
×
326
                        0.f,                 0.f,                  -(farZ + nearZ) * invDepthDiff,   -(farZ * (nearZ + nearZ)) * invDepthDiff,
×
327
                        0.f,                 0.f,                  -1.f,                             0.f);
×
328

329
    m_cameraUbo.bind();
×
330
    sendViewMatrix(viewMat);
×
331
    sendInverseViewMatrix(invViewMat);
×
332
    sendProjectionMatrix(projMat);
×
333
    sendInverseProjectionMatrix(projMat.inverse());
×
334
    sendViewProjectionMatrix(projMat * viewMat);
×
335
    sendCameraPosition(position);
×
336

337
    m_renderGraph.execute(*this);
×
338

339
    assert("Error: There is no valid last executed pass." && m_renderGraph.m_lastExecutedPass);
×
340
    const Framebuffer& finalFramebuffer = m_renderGraph.m_lastExecutedPass->getFramebuffer();
×
341
    assert("Error: The last executed pass must have at least one write color buffer." && finalFramebuffer.getColorBufferCount() >= 1);
×
342
    assert("Error: Either the last executed pass or the geometry pass must have a write depth buffer."
×
343
      && (finalFramebuffer.hasDepthBuffer() || m_renderGraph.m_geometryPass.getFramebuffer().hasDepthBuffer()));
344

345
    const Texture2D& depthBuffer = (finalFramebuffer.hasDepthBuffer() ? finalFramebuffer.getDepthBuffer()
×
346
                                                                      : m_renderGraph.m_geometryPass.getFramebuffer().getDepthBuffer());
×
347
    return std::make_pair(std::cref(finalFramebuffer.getColorBuffer(0)), std::cref(depthBuffer));
×
348
  });
349

350
#if !defined(RAZ_NO_WINDOW)
351
  if (!hasRendered)
×
352
    return;
×
353

354
  const Framebuffer& finalFramebuffer = m_renderGraph.m_lastExecutedPass->getFramebuffer();
×
355
  const Texture2D& depthBuffer        = (finalFramebuffer.hasDepthBuffer() ? finalFramebuffer.getDepthBuffer()
×
356
                                                                           : m_renderGraph.m_geometryPass.getFramebuffer().getDepthBuffer());
×
357
  copyToWindow(finalFramebuffer.getColorBuffer(0), depthBuffer, m_window->getWidth(), m_window->getHeight());
×
358
#endif
359
}
360
#endif
361

362
void RenderSystem::copyToWindow(const Texture2D& colorBuffer, const Texture2D& depthBuffer, unsigned int windowWidth, unsigned int windowHeight) const {
×
363
  assert("Error: The given color buffer must have a valid & non-depth colorspace to be copied to the window."
×
364
      && colorBuffer.getColorspace() != TextureColorspace::DEPTH && colorBuffer.getColorspace() != TextureColorspace::INVALID);
365
  assert("Error: The given depth buffer must have a depth colorspace to be copied to the window."
×
366
      && depthBuffer.getColorspace() == TextureColorspace::DEPTH);
367

368
  ZoneScopedN("RenderSystem::copyToWindow");
369
  TracyGpuZone("RenderSystem::copyToWindow")
370

371
  static RenderPass windowCopyPass = [] () {
×
372
    RenderPass copyPass(FragmentShader::loadFromSource(R"(
×
373
      in vec2 fragTexcoords;
374

375
      uniform sampler2D uniFinalColorBuffer;
376
      uniform sampler2D uniFinalDepthBuffer;
377
      uniform vec2 uniSizeFactor;
378

379
      layout(location = 0) out vec4 fragColor;
380

381
      void main() {
382
        vec2 scaledUv = fragTexcoords * uniSizeFactor;
383
        fragColor     = texture(uniFinalColorBuffer, scaledUv).rgba;
384
        gl_FragDepth  = texture(uniFinalDepthBuffer, scaledUv).r;
385
      }
386
    )"), "Window copy pass");
×
387

388
    RenderShaderProgram& copyProgram = copyPass.getProgram();
×
389
    copyProgram.setAttribute(0, "uniFinalColorBuffer");
×
390
    copyProgram.setAttribute(1, "uniFinalDepthBuffer");
×
391

392
    return copyPass;
×
393
  }();
×
394

395
  RenderShaderProgram& windowCopyProgram = windowCopyPass.getProgram();
×
396

397
  const Vec2f sizeFactor(static_cast<float>(m_sceneWidth) / static_cast<float>(windowWidth),
×
398
                         static_cast<float>(m_sceneHeight) / static_cast<float>(windowHeight));
×
399
  windowCopyProgram.setAttribute(sizeFactor, "uniSizeFactor");
×
400
  windowCopyProgram.sendAttributes();
×
401

402
  windowCopyProgram.use();
×
403
  Renderer::activateTexture(0);
×
404
  colorBuffer.bind();
×
405
  Renderer::activateTexture(1);
×
406
  depthBuffer.bind();
×
407

408
  Renderer::bindFramebuffer(0);
×
409
  Renderer::clear(MaskType::COLOR | MaskType::DEPTH | MaskType::STENCIL);
×
410

411
  Renderer::setDepthFunction(DepthStencilFunction::ALWAYS);
×
412
  windowCopyPass.execute();
×
413
  Renderer::setDepthFunction(DepthStencilFunction::LESS);
×
414
}
×
415

416
} // 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

© 2026 Coveralls, Inc