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

Razakhel / RaZ / 11805424802

12 Nov 2024 07:55PM UTC coverage: 74.798% (-4.7%) from 79.538%
11805424802

push

github

Razakhel
[Render/RenderSystem] Enabled XR multi-view rendering

- XR can be enabled in the RenderSystem; if it has been, the scene is rendered once per view each frame

15 of 65 new or added lines in 2 files covered. (23.08%)

1 existing line in 1 file now uncovered.

8055 of 10769 relevant lines covered (74.8%)

1826.46 hits per line

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

71.73
/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)
NEW
26
void RenderSystem::enableXr(XrSystem& xrSystem) {
×
NEW
27
  m_xrSystem = &xrSystem;
×
28

NEW
29
  const Vec2u optimalViewSize = xrSystem.recoverOptimalViewSize();
×
NEW
30
  resizeViewport(optimalViewSize.x(), optimalViewSize.y());
×
NEW
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✔
NEW
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)
NEW
296
void RenderSystem::renderXrFrame() {
×
NEW
297
  m_xrSystem->renderFrame([this] (const Vec3f& position, const Quaternionf& rotation, const ViewFov& viewFov) {
×
NEW
298
    Mat4f invViewMat = rotation.computeMatrix();
×
NEW
299
    invViewMat.getElement(3, 0) = position.x();
×
NEW
300
    invViewMat.getElement(3, 1) = position.y();
×
NEW
301
    invViewMat.getElement(3, 2) = position.z();
×
NEW
302
    const Mat4f viewMat = invViewMat.inverse();
×
303

NEW
304
    const float tanAngleRight    = std::tan(viewFov.angleRight.value);
×
NEW
305
    const float tanAngleLeft     = std::tan(viewFov.angleLeft.value);
×
NEW
306
    const float tanAngleUp       = std::tan(viewFov.angleUp.value);
×
NEW
307
    const float tanAngleDown     = std::tan(viewFov.angleDown.value);
×
NEW
308
    const float invAngleWidth    = 1.f / (tanAngleRight - tanAngleLeft);
×
NEW
309
    const float invAngleHeight   = 1.f / (tanAngleUp - tanAngleDown);
×
NEW
310
    const float angleWidthDiff   = tanAngleRight + tanAngleLeft;
×
NEW
311
    const float angleHeightDiff  = tanAngleUp + tanAngleDown;
×
NEW
312
    constexpr float nearZ        = 0.1f;
×
NEW
313
    constexpr float farZ         = 1000.f;
×
NEW
314
    constexpr float invDepthDiff = 1.f / (farZ - nearZ);
×
NEW
315
    const Mat4f projMatrix(2.f * invAngleWidth, 0.f,                  angleWidthDiff * invAngleWidth,   0.f,
×
NEW
316
                           0.f,                 2.f * invAngleHeight, angleHeightDiff * invAngleHeight, 0.f,
×
NEW
317
                           0.f,                 0.f,                  -(farZ + nearZ) * invDepthDiff,   -(farZ * (nearZ + nearZ)) * invDepthDiff,
×
NEW
318
                           0.f,                 0.f,                  -1.f,                             0.f);
×
319

NEW
320
    m_cameraUbo.bind();
×
NEW
321
    sendViewMatrix(viewMat);
×
NEW
322
    sendInverseViewMatrix(invViewMat);
×
NEW
323
    sendProjectionMatrix(projMatrix);
×
NEW
324
    sendInverseProjectionMatrix(projMatrix.inverse());
×
NEW
325
    sendViewProjectionMatrix(projMatrix * viewMat);
×
NEW
326
    sendCameraPosition(position);
×
327

NEW
328
    m_renderGraph.execute(*this);
×
329

330
    // TODO: the returned color buffer must be the one from the render pass executed last
NEW
331
    const Framebuffer& geomFramebuffer = m_renderGraph.m_geometryPass.getFramebuffer();
×
NEW
332
    return std::make_pair(std::cref(geomFramebuffer.getColorBuffer(0)), std::cref(geomFramebuffer.getDepthBuffer()));
×
333
  });
NEW
334
}
×
335
#endif
336

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