VisuTwin Canvas
C++ 3D Engine — Metal Backend
Loading...
Searching...
No Matches
texture.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 19.09.2025.
5//
6#include "texture.h"
7
8#include <assert.h>
9#include <algorithm>
10#include <spdlog/spdlog.h>
11
12#include "graphicsDevice.h"
13#include "textureUtils.h"
14
15namespace visutwin::canvas
16{
17 uint32_t Texture::_nextId = 0;
18
19 Texture::Texture(GraphicsDevice* graphicsDevice, const TextureOptions& options) :
20 _device(graphicsDevice), _width(options.width), _height(options.height), _depth(options.depth),
21 _format(options.format), _id(_nextId++), _minFilter(options.minFilter), _magFilter(options.magFilter),
22 _cubemap(options.cubemap), _volume(options.volume), _arrayLength(options.arrayLength), _numLevelsRequested(options.numLevels),
23 _mipmaps(options.mipmaps), _storage(options.storage), _name(options.name), _profilerHint(options.profilerHint)
24 {
25 assert(options.width > 0 && "Texture width must be greater than 0");
26 assert(options.height > 0 && "Texture height must be greater than 0");
27 assert(options.depth > 0 && "Texture depth must be greater than 0");
28
29 _compressed = isCompressedPixelFormat(_format);
30 _integerFormat = isIntegerPixelFormat(_format);
31
32 if (_integerFormat) {
33 _minFilter = FilterMode::FILTER_NEAREST;
34 _magFilter = FilterMode::FILTER_NEAREST;
35 }
36
37 if (_cubemap) {
39 } else {
40 _projection = options.projection;
41 }
42
43 // Set the number of levels
44 if (options.numLevels > 0) {
45 _numLevels = options.numLevels;
46 }
47 updateNumLevels();
48
49 // Initialize level data
50 if (options.levels) {
51 // Handle provided level data
52 // Note: In actual implementation, would need to properly handle different data types
53 } else {
54 clearLevels();
55 }
56
57 // Create implementation
58 recreateImpl(options.levels != nullptr);
59
60 spdlog::trace("Alloc: Id %u %s: %ux%u [Format:%u]%s%s%s[MipLevels:%u]",
61 _id, _name, _width, _height, static_cast<uint32_t>(_format),
62 _cubemap ? "[Cubemap]" : "",
63 _volume ? "[Volume]" : "",
64 isArray() ? "[Array]" : "",
65 _numLevels);
66 }
67
69 {
70
71 }
72
73 void Texture::updateNumLevels() {
74 uint32_t maxLevels = _mipmaps ? TextureUtils::calcMipLevelsCount(_width, _height) : 1;
75
76 if (_numLevelsRequested > 0 && _numLevelsRequested > maxLevels) {
77 spdlog::warn("Texture#numLevels: requested mip level count %u is greater than maximum %u, clamping",
78 _numLevelsRequested, maxLevels);
79 }
80
81 _numLevels = std::min(_numLevelsRequested > 0 ? _numLevelsRequested : maxLevels, maxLevels);
82 _mipmaps = _numLevels > 1;
83 }
84
85 void Texture::clearLevels() {
86 if (_cubemap) {
87 _levels.resize(1);
88 _levels[0].resize(6, nullptr);
89 _levelDataSizes.resize(1);
90 _levelDataSizes[0].resize(6, 0);
91 _levelStorage.resize(1);
92 _levelStorage[0].resize(6);
93 _levelsUpdated.resize(1);
94 _levelsUpdated[0].resize(6, true);
95 } else {
96 _levels.resize(1);
97 _levels[0].resize(1, nullptr);
98 _levelDataSizes.resize(1);
99 _levelDataSizes[0].resize(1, 0);
100 _levelStorage.resize(1);
101 _levelStorage[0].resize(1);
102 _levelsUpdated.resize(1);
103 _levelsUpdated[0].resize(1, true);
104 }
105 }
106
107 void Texture::recreateImpl(bool enableUpload)
108 {
109 // destroy existing
110 _impl.reset();
111
112 // create new
113 _impl = _device->createGPUTexture(this);
114 dirtyAll();
115
116 if (enableUpload) {
117 upload();
118 }
119 }
120
121 void Texture::dirtyAll() {
122 if (_cubemap) {
123 _levelsUpdated.resize(1);
124 _levelsUpdated[0].resize(6, true);
125 } else {
126 _levelsUpdated.resize(1);
127 _levelsUpdated[0].resize(1, true);
128 }
129
130 _needsUpload = true;
131 _needsMipmapsUpload = _mipmaps;
132 _mipmapsUploaded = false;
133
134 //propertyChanged(TEXPROPERTY_ALL);
135 }
136
138 {
139 _needsUpload = true;
140 _needsMipmapsUpload = _mipmaps;
141
142 if (_impl) {
143 _impl->uploadImmediate(_device);
144 }
145 }
146
147 void* Texture::getLevel(uint32_t mipLevel) const
148 {
149 if (_levels.empty() || mipLevel >= _levels[0].size()) {
150 return nullptr;
151 }
152 return _levels[0][mipLevel];
153 }
154
155 size_t Texture::getLevelDataSize(uint32_t mipLevel, uint32_t face) const
156 {
157 if (face >= _levelDataSizes.size() || mipLevel >= _levelDataSizes[face].size()) {
158 return 0;
159 }
160 return _levelDataSizes[face][mipLevel];
161 }
162
163 void Texture::setLevelData(uint32_t mipLevel, const uint8_t* data, size_t dataSize, uint32_t face)
164 {
165 if (!data || dataSize == 0) {
166 return;
167 }
168
169 if (_cubemap) {
170 if (face >= 6) {
171 spdlog::warn("Texture::setLevelData face index {} out of range for cubemap", face);
172 return;
173 }
174 } else {
175 face = 0;
176 }
177
178 if (face >= _levels.size()) {
179 _levels.resize(face + 1);
180 _levelDataSizes.resize(face + 1);
181 _levelStorage.resize(face + 1);
182 _levelsUpdated.resize(face + 1);
183 }
184
185 const auto requiredLevels = mipLevel + 1;
186 if (_levels[face].size() < requiredLevels) {
187 _levels[face].resize(requiredLevels, nullptr);
188 _levelDataSizes[face].resize(requiredLevels, 0);
189 _levelStorage[face].resize(requiredLevels);
190 _levelsUpdated[face].resize(requiredLevels, true);
191 }
192
193 auto& storage = _levelStorage[face][mipLevel];
194 storage.assign(data, data + dataSize);
195 _levels[face][mipLevel] = storage.data();
196 _levelDataSizes[face][mipLevel] = dataSize;
197 _levelsUpdated[face][mipLevel] = true;
198 _needsUpload = true;
199 }
200
201 void* Texture::getFaceData(uint32_t mipLevel, uint32_t face) const {
202 if (!_cubemap || face >= 6 || _levels.size() <= face) {
203 return nullptr;
204 }
205 if (mipLevel >= _levels[face].size()) {
206 return nullptr;
207 }
208 return _levels[face][mipLevel];
209 }
210
211 void* Texture::getArrayData(uint32_t mipLevel, uint32_t index) const {
212 if (!isArray() || index >= _arrayLength) {
213 return nullptr;
214 }
215 // Note: Array texture implementation would be more complex
216 return getLevel(mipLevel);
217 }
218
219 void Texture::resize(uint32_t width, uint32_t height, uint32_t depth)
220 {
221 // Update VRAM tracking
222 if (_gpuSize > 0) {
223 adjustVramSizeTracking(_device->_vram, -static_cast<int64_t>(_gpuSize));
224 }
225
226 // Clear levels
227 clearLevels();
228
229 // Update dimensions
230 _width = width;
231 _height = height;
232 _depth = depth;
233 updateNumLevels();
234
235 dirtyAll();
236 }
237
238 void Texture::adjustVramSizeTracking(DeviceVRAM& vram, int64_t size)
239 {
240 spdlog::trace("%u %s size: %lld vram.tex: %zu => %zu", _id, _name.c_str(), size, vram.tex, vram.tex + size);
241
242 vram.tex += size;
243
244 // Update profiler-specific VRAM tracking
245 switch (_profilerHint) {
247 vram.texShadow += size;
248 break;
250 vram.texAsset += size;
251 break;
253 vram.texLightmap += size;
254 break;
255 default:
256 break;
257 }
258 }
259
261 {
262 if (_mipmaps != mipmaps) {
263 _mipmaps = mipmaps;
264
265 if (mipmaps) {
266 _needsMipmapsUpload = true;
267 }
268 }
269 }
270
272 {
273 if (_minFilter != filter) {
274 if (_integerFormat) {
275 spdlog::warn("minFilter property cannot be changed on integer texture, will remain FILTER_NEAREST");
276 } else {
277 _minFilter = filter;
279 }
280 }
281 }
282
284 {
285 if (_magFilter != filter) {
286 if (_integerFormat) {
287 spdlog::warn("magFilter property cannot be changed on integer texture, will remain FILTER_NEAREST");
288 } else {
289 _magFilter = filter;
291 }
292 }
293 }
294
296 {
297 if (_addressU != address) {
298 _addressU = address;
300 }
301 }
302
304 {
305 if (_addressV != address) {
306 _addressV = address;
308 }
309 }
310
312 {
313 if (!_volume) {
314 spdlog::warn("Cannot set W addressing mode for a non-3D texture");
315 return;
316 }
317 if (_addressW != address) {
318 _addressW = address;
320 }
321 }
322
324 {
325 if (_impl)
326 {
327 _impl->propertyChanged(flag);
328 }
329 _renderVersionDirty = _device->renderVersion();
330 }
331}
Abstract GPU interface for resource creation, state management, and draw submission.
size_t getLevelDataSize(uint32_t mipLevel, uint32_t face=0) const
Definition texture.cpp:155
Texture(GraphicsDevice *graphicsDevice, const TextureOptions &options=TextureOptions{})
Definition texture.cpp:19
uint32_t width() const
Definition texture.h:63
PixelFormat format() const
Definition texture.h:97
void setLevelData(uint32_t mipLevel, const uint8_t *data, size_t dataSize, uint32_t face=0)
Definition texture.cpp:163
void * getLevel(uint32_t mipLevel) const
Definition texture.cpp:147
void setMinFilter(FilterMode filter)
Definition texture.cpp:271
void setAddressW(AddressMode address)
Definition texture.cpp:311
const std::string & name() const
Definition texture.h:103
virtual void propertyChanged(TextureProperty flag)
Definition texture.cpp:323
uint32_t height() const
Definition texture.h:65
bool isArray() const
Definition texture.h:69
void setMipmaps(bool mipmaps)
Definition texture.cpp:260
uint32_t depth() const
Definition texture.h:67
void resize(uint32_t width, uint32_t height, uint32_t depth=1)
Definition texture.cpp:219
void setAddressU(AddressMode address)
Definition texture.cpp:295
void * getFaceData(uint32_t mipLevel, uint32_t face) const
Definition texture.cpp:201
bool storage() const
Definition texture.h:98
bool mipmaps() const
Definition texture.h:94
void setMagFilter(FilterMode filter)
Definition texture.cpp:283
void setAddressV(AddressMode address)
Definition texture.cpp:303
void * getArrayData(uint32_t mipLevel, uint32_t index) const
Definition texture.cpp:211
static uint32_t calcMipLevelsCount(uint32_t width, uint32_t height, uint32_t depth=1)
bool isCompressedPixelFormat(const PixelFormat format)
Definition constants.cpp:33
bool isIntegerPixelFormat(const PixelFormat format)
Definition constants.cpp:38
TextureProjection projection
Definition texture.h:38