VisuTwin Canvas
C++ 3D Engine — Metal Backend
Loading...
Searching...
No Matches
renderPass.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025-2026 Arnis Lektauers
3//
4// Created by Arnis Lektauers on 12.09.2025.
5//
6#include <cmath>
7
8#include "graphicsDevice.h"
9#include "renderPass.h"
10
11namespace visutwin::canvas
12{
14 {
15 if (_options != nullptr && _renderTarget != nullptr) {
16 if (const auto* resizeSource = _options->resizeSource != nullptr ? _options->resizeSource.get() : _device->backBuffer()->getColorBuffer(0))
17 {
18 const auto width = std::floor(resizeSource->width() * scaleX());
19 const auto height = std::floor(resizeSource->height() * scaleY());
20 _renderTarget->resize(width, height);
21 }
22 }
23 }
24
26 if (_enabled) {
27 // null means backbuffer (real pass), "uninitialized" means no pass.
28 const bool realPass = _renderTargetInitialized;
29
30 log(_device, _device->_renderPassIndex);
31
32 before();
33
34 if (_executeEnabled) {
35 bool passReady = true;
36
37 if (realPass && !_skipStart) {
38 _device->startRenderPass(this);
39 passReady = _device->insideRenderPass();
40 }
41
42 if (passReady) {
43 execute();
44 }
45
46 if (realPass && !_skipEnd && passReady) {
47 _device->endRenderPass(this);
48 }
49 }
50
51 after();
52
53 _device->_renderPassIndex++;
54 }
55 }
56
57 void RenderPass::setEnabled(bool value) {
58 if (_enabled != value) {
59 _enabled = value;
60 if (value) {
61 onEnable();
62 } else {
63 onDisable();
64 }
65 }
66 }
67
68 void RenderPass::init(const std::shared_ptr<RenderTarget>& renderTarget, const std::shared_ptr<RenderPassOptions>& options)
69 {
70 setOptions(options);
71
72 // null represents the default framebuffer
73 _renderTarget = renderTarget;
74 _renderTargetInitialized = true;
75
76 // defaults depend on multisampling
77 _samples = std::max(renderTarget ? renderTarget->samples() : _device->samples(), 1);
78
79 // allocate ops only when render target is used (when this function was called)
81
82 // allow for post-init setup
83 postInit();
84 }
85
86 void RenderPass::setOptions(const std::shared_ptr<RenderPassOptions>& value)
87 {
88 _options = value;
89
90 // Sanitize options
91 if (value) {
92 if (_options->scaleX == 0.0f)
93 {
94 _options->scaleX = 1.0f;
95 }
96 if (_options->scaleY == 0.0f)
97 {
98 _options->scaleY = 1.0f;
99 }
100 }
101 }
102
104 {
105 // depth
106 _depthStencilOps = std::make_shared<DepthStencilAttachmentOps>();
107
108 // if a RT is used (so not a backbuffer) that was created with a user supplied depth buffer,
109 // assume the user wants to use its content, and so store it by default
110 if (_renderTarget && _renderTarget->hasDepthBuffer()) {
111 _depthStencilOps->storeDepth = true;
112 }
113
114 // color
115 int numColorOps = _renderTarget ? _renderTarget->colorBufferCount() : 1;
116 _colorArrayOps.clear();
117 _colorArrayOps.resize(numColorOps);
118
119 for (int i = 0; i < numColorOps; i++) {
120 auto colorOps = std::make_shared<ColorAttachmentOps>();
121 _colorArrayOps[i] = colorOps;
122
123 // if rendering to a single-sampled buffer, this buffer needs to be stored
124 if (_samples == 1) {
125 colorOps->store = true;
126 colorOps->resolve = false;
127 }
128
129 // if the render target needs mipmaps
130 if (_renderTarget && _renderTarget->hasMipmaps() && i < _renderTarget->colorBufferCount()) {
131 auto colorBuffer = _renderTarget->getColorBuffer(i);
132 if (colorBuffer && colorBuffer->mipmaps()) {
133 bool intFormat = isIntegerPixelFormat(colorBuffer->format());
134 colorOps->genMipmaps = !intFormat; // no automatic mipmap generation for integer formats
135 }
136 }
137 }
138 }
139
141 {
142 // In case of MRT, clear all color attachments.
143 for (const auto& colorOps : _colorArrayOps) {
144 if (!colorOps) {
145 continue;
146 }
147
148 if (color) {
149 colorOps->clearValue = *color;
150 colorOps->clearValueLinear = *color;
151 }
152
153 colorOps->clear = color != nullptr;
154 }
155 }
156
157 void RenderPass::setClearDepth(const float* depthValue)
158 {
159 if (!_depthStencilOps) {
160 return;
161 }
162
163 if (depthValue) {
164 _depthStencilOps->clearDepthValue = *depthValue;
165 }
166 _depthStencilOps->clearDepth = depthValue != nullptr;
167 }
168
169 void RenderPass::setClearStencil(const int* stencilValue)
170 {
171 if (!_depthStencilOps) {
172 return;
173 }
174
175 if (stencilValue) {
176 _depthStencilOps->clearStencilValue = *stencilValue;
177 }
178 _depthStencilOps->clearStencil = stencilValue != nullptr;
179 }
180
181 void RenderPass::log(std::shared_ptr<GraphicsDevice> device, int index) const
182 {
183 const auto& rt = _renderTarget == nullptr ? nullptr : device->backBuffer();
184 bool isBackBuffer = false; // Simplified check
185 int numColor = 0;
186 bool hasDepth = false;
187 bool hasStencil = false;
188 int mipLevel = 0;
189
190 if (rt) {
191 numColor = rt->colorBufferCount();
192 hasDepth = rt->hasDepthBuffer();
193 hasStencil = rt->hasStencil();
194 mipLevel = rt->mipLevel();
195 }
196
197 // This is a simplified version of the debug output
198 // In a real implementation, you'd use a proper logging system
199 std::string rtInfo;
200 if (rt) {
201 rtInfo = " RT: " + rt->name();
202 if (numColor > 0) {
203 rtInfo += " [Color";
204 if (numColor > 1) {
205 rtInfo += " x " + std::to_string(numColor);
206 }
207 rtInfo += "]";
208 }
209 if (hasDepth)
210 {
211 rtInfo += "[Depth]";
212 }
213 if (hasStencil)
214 {
215 rtInfo += "[Stencil]";
216 }
217 rtInfo += " " + std::to_string(rt->width()) + " x " + std::to_string(rt->height());
218 if (_samples > 0) {
219 rtInfo += " samples: " + std::to_string(_samples);
220 }
221 if (mipLevel > 0) {
222 rtInfo += " mipLevel: " + std::to_string(mipLevel);
223 }
224 }
225
226 std::string indexString = _skipStart ? "++" : std::to_string(index);
227 }
228
229 std::shared_ptr<ColorAttachmentOps> RenderPass::colorOps() const
230 {
231 if (!_colorArrayOps.empty()) {
232 return _colorArrayOps[0];
233 }
234 return nullptr;
235 }
236
237 void RenderPass::addBeforePass(const std::shared_ptr<RenderPass>& renderPass)
238 {
239 if (renderPass) {
240 _beforePasses.push_back(renderPass);
241 }
242 }
243
244 void RenderPass::addAfterPass(const std::shared_ptr<RenderPass>& renderPass)
245 {
246 if (renderPass) {
247 _afterPasses.push_back(renderPass);
248 }
249 }
250
252 {
253 _beforePasses.clear();
254 }
255
257 {
258 _afterPasses.clear();
259 }
260}
virtual void init(const std::shared_ptr< RenderTarget > &renderTarget=nullptr, const std::shared_ptr< RenderPassOptions > &options=nullptr)
void setClearColor(const Color *color=nullptr)
void setClearDepth(const float *depthValue=nullptr)
void setClearStencil(const int *stencilValue=nullptr)
std::shared_ptr< RenderTarget > renderTarget() const
Definition renderPass.h:98
virtual void frameUpdate() const
std::shared_ptr< GraphicsDevice > device() const
Definition renderPass.h:124
void addBeforePass(const std::shared_ptr< RenderPass > &renderPass)
std::shared_ptr< ColorAttachmentOps > colorOps() const
void addAfterPass(const std::shared_ptr< RenderPass > &renderPass)
void log(std::shared_ptr< GraphicsDevice > device, int index=0) const
void setEnabled(bool value)
void setOptions(const std::shared_ptr< RenderPassOptions > &value)
bool isIntegerPixelFormat(const PixelFormat format)
Definition constants.cpp:38
RGBA color with floating-point components in [0, 1].
Definition color.h:18