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

Razakhel / RaZ / 16969175463

14 Aug 2025 08:25AM UTC coverage: 74.293% (-0.3%) from 74.615%
16969175463

push

github

Razakhel
[Render/Renderer] Added sampler functions

- Renamed activateTexture() to setActiveTexture(), and TextureParam* enums to TextureParameter*

- Added several noexcept specifications

- Removed single dots at the end of message strings

66 of 170 new or added lines in 28 files covered. (38.82%)

8326 of 11207 relevant lines covered (74.29%)

1746.35 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

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

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

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

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

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

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

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

52
  m_cameraUbo.bindBase(0);
22✔
53
  m_lightsUbo.bindBase(1);
22✔
54
  m_timeUbo.bindBase(2);
22✔
55
  m_modelUbo.bindBase(3);
22✔
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) {
35✔
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();
22✔
67
  m_timeUbo.sendData(timeInfo.deltaTime, 0);
22✔
68
  m_timeUbo.sendData(timeInfo.globalTime, sizeof(float));
22✔
69

70
#if defined(RAZ_USE_XR)
71
  if (m_xrSystem) {
22✔
72
    renderXrFrame();
×
73
  } else
74
#endif
75
  {
76
    sendCameraInfo();
22✔
77
    m_renderGraph.execute(*this);
22✔
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)
22✔
86
    return m_window->run(timeInfo.deltaTime);
×
87
#endif
88

89
  return true;
22✔
90
}
91

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

95
  unsigned int lightCount = 0;
11✔
96

97
  m_lightsUbo.bind();
11✔
98

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

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

107
  m_lightsUbo.sendData(lightCount, sizeof(Vec4f) * 4 * 100);
11✔
108
}
11✔
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 {
5✔
136
  ZoneScopedN("RenderSystem::updateMaterials(MeshRenderer)");
137

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

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

147
    m_cameraUbo.bindUniformBlock(materialProgram, "uboCameraInfo", 0);
3✔
148
    m_lightsUbo.bindUniformBlock(materialProgram, "uboLightsInfo", 1);
3✔
149
    m_timeUbo.bindUniformBlock(materialProgram, "uboTimeInfo", 2);
3✔
150
    m_modelUbo.bindUniformBlock(materialProgram, "uboModelInfo", 3);
3✔
151
  }
152
}
5✔
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
  ZoneTextF("Path: %s", filePath.toUtf8().c_str());
166

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

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

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

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

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

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

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

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

200
  System::linkEntity(entity);
26✔
201

202
  if (entity->hasComponent<Camera>())
26✔
203
    m_cameraEntity = entity.get();
12✔
204

205
  if (entity->hasComponent<Light>())
26✔
206
    updateLights();
10✔
207

208
  if (entity->hasComponent<MeshRenderer>())
26✔
209
    updateMaterials(entity->getComponent<MeshRenderer>());
4✔
210
}
26✔
211

212
void RenderSystem::initialize() {
24✔
213
  ZoneScopedN("RenderSystem::initialize");
214

215
  registerComponents<Camera, Light, MeshRenderer>();
24✔
216

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

229
#if !defined(USE_OPENGL_ES)
230
  // 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
231
  if (Renderer::checkVersion(4, 5) || Renderer::isExtensionSupported("GL_ARB_clip_control"))
24✔
232
    Renderer::setClipControl(ClipOrigin::LOWER_LEFT, ClipDepth::ZERO_TO_ONE);
24✔
233

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

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

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

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

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

257
  m_cameraUbo.bind();
22✔
258

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

265
    camera.computeInverseViewMatrix();
13✔
266

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

271
    camTransform.setUpdated(false);
13✔
272
  }
273

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

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

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

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

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

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

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

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

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

338
    m_renderGraph.execute(*this);
×
339

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

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

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

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

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

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

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

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

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

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

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

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

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

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

403
  windowCopyProgram.use();
×
NEW
404
  Renderer::setActiveTexture(0);
×
405
  colorBuffer.bind();
×
NEW
406
  Renderer::setActiveTexture(1);
×
407
  depthBuffer.bind();
×
408

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

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

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

© 2026 Coveralls, Inc