VisuTwin Canvas
C++ 3D Engine — Metal Backend
Loading...
Searching...
No Matches
renderTarget.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 "renderTarget.h"
7
8#include <spdlog/spdlog.h>
9
10#include "graphicsDevice.h"
11#include "textureUtils.h"
12
13namespace visutwin::canvas
14{
15 static int globalRenderTargetId = 0;
16
18 {
19 _id = globalRenderTargetId++;
20
21 // Get device from one of the buffers
22 _device = options.colorBuffer ? options.colorBuffer->device() :
23 (!options.colorBuffers.empty() ? options.colorBuffers[0]->device() :
24 (options.depthBuffer ? options.depthBuffer->device() :
25 options.graphicsDevice));
26
27 assert(_device != nullptr && "Failed to obtain the device, colorBuffer nor depthBuffer store it.");
28
29 // Samples
30 int maxSamples = _device->maxSamples();
31 _samples = std::min(options.samples, maxSamples);
32
33 // Use the single colorBuffer in the colorBuffers array
34 _colorBuffer = options.colorBuffer;
35 if (options.colorBuffer) {
36 _colorBuffers = {options.colorBuffer};
37 }
38
39 // Process optional arguments
40 _depthBuffer = options.depthBuffer;
41 _face = options.face;
42
43 if (_depthBuffer) {
44 auto format = _depthBuffer->format();
46 _depth = true;
47 _stencil = false;
48 } else if (format == PixelFormat::PIXELFORMAT_DEPTHSTENCIL) {
49 _depth = true;
50 _stencil = true;
51 } else if (format == PixelFormat::PIXELFORMAT_R32F && _samples > 1) {
52 // on WebGPU, when multisampling is enabled, we use R32F format for the specified buffer,
53 // which we can resolve depth to using a shader
54 _depth = true;
55 _stencil = false;
56 } else {
57 spdlog::warn("Incorrect depthBuffer format. Must be pc.PIXELFORMAT_DEPTH or pc.PIXELFORMAT_DEPTHSTENCIL");
58 _depth = false;
59 _stencil = false;
60 }
61 } else {
62 _depth = options.depth;
63 _stencil = options.stencil;
64 }
65
66 // MRT
67 if (!options.colorBuffers.empty()) {
68 assert((_colorBuffers.empty() || _colorBuffers.size() == 1) &&
69 "When constructing RenderTarget and options.colorBuffers is used, options.colorBuffer must not be used.");
70
71 if (_colorBuffers.empty() || _colorBuffers.size() == 1) {
72 _colorBuffers = options.colorBuffers;
73 // set the main color buffer to point to 0 index
74 _colorBuffer = options.colorBuffers[0];
75 }
76 }
77
78 _autoResolve = options.autoResolve;
79
80 // Use specified name, otherwise get one from color or depth buffer
81 _name = options.name;
82 if (_name.empty() && _colorBuffer) {
83 _name = _colorBuffer->name();
84 }
85 if (_name.empty() && _depthBuffer) {
86 _name = _depthBuffer->name();
87 }
88 if (_name.empty()) {
89 _name = "Untitled";
90 }
91
92 // Render image flipped in Y
93 _flipY = options.flipY;
94
95 _mipLevel = options.mipLevel;
96 if (_mipLevel > 0 && _depth) {
97 spdlog::error("Rendering to a mipLevel is not supported when render target uses a depth buffer. Ignoring mipLevel " +
98 std::to_string(_mipLevel) + " for render target " + _name);
99 _mipLevel = 0;
100 }
101
102 // If we render to a specific mipmap (even 0), do not generate mipmaps
103 _mipmaps = (options.mipLevel == 0 && !options.colorBuffer);
104
105 validateMrt();
106
107 spdlog::trace("Alloc: Id " + std::to_string(_id) + " " + _name + ": " +
108 std::to_string(width()) + "x" + std::to_string(height()) +
109 " [samples: " + std::to_string(_samples) + "]" +
110 (!_colorBuffers.empty() ? "[MRT: " + std::to_string(_colorBuffers.size()) + "]" : "") +
111 (_colorBuffer ? "[Color]" : "") +
112 (_depth ? "[Depth]" : "") +
113 (_stencil ? "[Stencil]" : "") +
114 "[Face:" + std::to_string(_face) + "]");
115 }
116
118 {
119 spdlog::trace("DeAlloc: Id " + std::to_string(_id) + " " + _name);
120
121 if (_device) {
122 _device->removeTarget(this);
123
124 if (_device->renderTarget().get() == this) {
125 _device->setRenderTarget(nullptr);
126 }
127 }
128 }
129
131 {
132 auto width = _colorBuffer != nullptr ? _colorBuffer->width() : _depthBuffer != nullptr ? _depthBuffer->width() : _device->size().first;
133 if (_mipLevel > 0) {
135 }
136 return width;
137 }
138
140 {
141 int height = _colorBuffer ? _colorBuffer->height() : (_depthBuffer ? _depthBuffer->height() : _device->size().second);
142 if (_mipLevel > 0) {
144 }
145 return height;
146 }
147
149 {
150 if (this->width() != width || this->height() != height) {
151 if (_mipLevel > 0) {
152 spdlog::warn("Only a render target rendering to mipLevel 0 can be resized, ignoring.");
153 return;
154 }
155
156 // Release existing
158 if (_device->renderTarget().get() == this) {
159 _device->setRenderTarget(nullptr);
160 }
161
162 // Resize textures
163 if (_depthBuffer) {
164 _depthBuffer->resize(width, height);
165 }
166 for (auto* colorBuffer : _colorBuffers) {
167 if (colorBuffer) {
168 colorBuffer->resize(width, height);
169 }
170 }
171
172 // Initialize again
173 validateMrt();
175 }
176 }
177
179 {
180 if (index < _colorBuffers.size()) {
181 return _colorBuffers[index];
182 }
183 return nullptr;
184 }
185
187 {
188 if (!_colorBuffers.empty()) {
189 int width = _colorBuffers[0]->width();
190 int height = _colorBuffers[0]->height();
191 bool cubemap = _colorBuffers[0]->isCubemap();
192 bool volume = _colorBuffers[0]->isVolume();
193
194 for (size_t i = 1; i < _colorBuffers.size(); i++) {
195 Texture* colorBuffer = _colorBuffers[i];
196 assert(colorBuffer->width() == width && "All render target color buffers must have the same width");
197 assert(colorBuffer->height() == height &&
198 "All render target color buffers must have the same height");
199 assert(colorBuffer->isCubemap() == cubemap &&
200 "All render target color buffers must have the same cubemap setting");
201 assert(colorBuffer->isVolume() == volume &&
202 "All render target color buffers must have the same volume setting");
203 }
204 }
205 }
206}
Texture * getColorBuffer(size_t index) const
virtual void destroyFrameBuffers()=0
void resize(int width, int height)
RenderTarget(const RenderTargetOptions &options={})
virtual void createFrameBuffers()=0
Texture * colorBuffer() const
GPU texture resource supporting 2D, cubemap, volume, and array formats with mipmap management.
Definition texture.h:57
GraphicsDevice * device() const
Definition texture.h:100
static uint32_t calcLevelDimension(uint32_t dimension, uint32_t mipLevel)
std::vector< Texture * > colorBuffers