Cogs.Core
TexturesWebGPU.cpp
1#include "TexturesWebGPU.h"
2
3#include "FormatsWebGPU.h"
4#include "GraphicsDeviceWebGPU.h"
5#include "Foundation/Logging/Logger.h"
6
7#include "Foundation/StringView.h"
8
9#include <glm/gtc/color_space.hpp>
10#include <glm/detail/func_packing.inl>
11
12namespace{
13 Cogs::Logging::Log logger = Cogs::Logging::getLogger("TexturesWebGPU");
14
15 void textures_error_callback(WGPUPopErrorScopeStatus status, WGPUErrorType type, WGPUStringView message, void* userdata1, void* userdata2)
16 {
17 Cogs::TexturesWebGPU *textures = (Cogs::TexturesWebGPU*)userdata1;
18 (void)textures;
20 (void)ptr;
21 if(status == WGPUPopErrorScopeStatus_CallbackCancelled){
22 LOG_ERROR(logger, "WebGPU textures err status: Cancelled");
23 }
24 if(status == WGPUPopErrorScopeStatus_Error){
25 LOG_ERROR(logger, "WebGPU textures err status: Error");
26 }
27 if(message.data && message.length){
28 LOG_ERROR(logger, "WebGPU textures err (%d) %.*s", type, WGPUStringViewFormat(message));
29 }
30 }
31
32 WGPUCompareFunction wgpu(Cogs::SamplerState::ComparisonFunction cmp)
33 {
34 switch(cmp){
35 case Cogs::SamplerState::ComparisonFunction::Never:
36 return WGPUCompareFunction_Never;
37 case Cogs::SamplerState::ComparisonFunction::Less:
38 return WGPUCompareFunction_Less;
39 case Cogs::SamplerState::ComparisonFunction::Equal:
40 return WGPUCompareFunction_Equal;
41 case Cogs::SamplerState::ComparisonFunction::LessEqual:
42 return WGPUCompareFunction_LessEqual;
43 case Cogs::SamplerState::ComparisonFunction::Greater:
44 return WGPUCompareFunction_Greater;
45 case Cogs::SamplerState::ComparisonFunction::NotEqual:
46 return WGPUCompareFunction_NotEqual;
47 case Cogs::SamplerState::ComparisonFunction::GreaterEqual:
48 return WGPUCompareFunction_GreaterEqual;
49 case Cogs::SamplerState::ComparisonFunction::Always:
50 return WGPUCompareFunction_Always;
51 case Cogs::SamplerState::ComparisonFunction::ComparisonFunction_Size:
52 break;
53 }
54 assert(false);
55 return WGPUCompareFunction_Undefined;
56 }
57
58 WGPUAddressMode wgpu(Cogs::SamplerState::AddressMode address_mode)
59 {
60 switch(address_mode){
62 return WGPUAddressMode_ClampToEdge;
64 return WGPUAddressMode_Repeat;
66 return WGPUAddressMode_MirrorRepeat;
68 LOG_WARNING_ONCE(logger, "WebGPU backend does not support SamplerState::AddressMode::Border.");
69 return WGPUAddressMode_ClampToEdge;
70 case Cogs::SamplerState::AddressMode::AddressMode_Size:
71 break;
72 }
73 assert(false);
74 return WGPUAddressMode_Repeat;
75 }
76}
77
78namespace Cogs{
79
80 void TexturesWebGPU::initialize(class GraphicsDeviceWebGPU *device)
81 {
82 graphicsDevice = device;
83 }
84
86 {
87 TextureWebGPU &texture = textures[handle];
88 wgpuTextureSetLabel(texture.texture, {name.data(), name.length()});
89 }
90
91 static void writeMipLevelToGPU(GraphicsDeviceWebGPU *graphicsDevice, WGPUTexture *destinationTexture, void *data, uint32_t cubeFace, uint32_t arrayLayer, uint32_t mipLevel, Cogs::TextureExtent textureExtent, size_t levelSize, uint32_t bytesPerPixel)
92 {
93 WGPUTexelCopyBufferLayout layout = WGPU_TEXEL_COPY_BUFFER_LAYOUT_INIT;
94 layout.offset = 0;
95 layout.bytesPerRow = bytesPerPixel * textureExtent.width;
96 layout.rowsPerImage = textureExtent.height;
97
98 WGPUTexelCopyTextureInfo destination = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT;
99 destination.texture = *destinationTexture;
100 destination.aspect = WGPUTextureAspect_All;
101 destination.origin = { 0, 0, static_cast<uint32_t>(arrayLayer + cubeFace) };
102 destination.mipLevel = static_cast<uint32_t>(mipLevel);
103 WGPUExtent3D sizeExtent = { textureExtent.width, textureExtent.height, textureExtent.depth };
104 wgpuQueueWriteTexture(graphicsDevice->queue, &destination, data, levelSize, &layout, &sizeExtent);
105 }
106
107 template <typename T>
108 static void convertMiplevelDataToFloatData(float *out, T *data, size_t nPixels, uint32_t channels, bool isSigned, bool isFloat, bool isHalfFloat, bool isSRGB, bool hasAlpha)
109 {
110 float valRange = std::powf(2, 8 * sizeof(T)) - 1.0f;
111
112 memcpy(out, data, static_cast<size_t>(isFloat) * nPixels * channels * sizeof(float));
113
114 for(size_t pixel = 0; pixel < nPixels * static_cast<size_t>(!isFloat); pixel++) {
115 for(size_t channel = 0; channel < channels; channel++) {
116 T channelData = data[(pixel * channels) + channel];
117 size_t isSignBitSet = static_cast<size_t>(isSigned) & (static_cast<size_t>(channelData) >> ((sizeof(T) * 8) - 1));
118
119
120 T channelDataShifted = channelData + static_cast<T>((static_cast<size_t>(!isHalfFloat) * isSignBitSet * (static_cast<size_t>(valRange) >> 1)));
121
122 glm::vec2 halfFloatToFloatVec = glm::unpackHalf2x16(static_cast<uint32_t>(channelDataShifted));
123 float halfFloatToFloat = halfFloatToFloatVec.x;
124
125 float channelDataAsFloat = (static_cast<size_t>(!isHalfFloat) * static_cast<float>(channelDataShifted) / valRange) + (static_cast<float>(static_cast<size_t>(isHalfFloat)) * halfFloatToFloat);
126 channelDataAsFloat -= 1.0f * isSignBitSet * static_cast<size_t>(!isHalfFloat);
127
128 size_t isNotAlphaChannel = static_cast<size_t>(!hasAlpha) + (static_cast<size_t>(hasAlpha) * static_cast<size_t>((channel < (channels-1))));
129 size_t isAlphaChannel = static_cast<size_t>(hasAlpha) * static_cast<size_t>((channel == (channels-1)));
130
131 glm::vec3 channelDataVecGamma = glm::convertSRGBToLinear(glm::vec3(channelDataAsFloat, 0.0f, 0.0f));
132 float channelDataGamma = channelDataVecGamma.r;
133
134 float dataSRGBToLinear = static_cast<size_t>(isSRGB) * ((isNotAlphaChannel * channelDataGamma) + (isAlphaChannel * channelDataAsFloat));
135
136 channelDataAsFloat = (static_cast<size_t>(!isSRGB) * channelDataAsFloat) + (static_cast<size_t>(isSRGB) * dataSRGBToLinear);
137
138 out[(pixel * channels) + channel] = channelDataAsFloat;
139 }
140 }
141 }
142
143 template <typename T>
144 static void nextMiplevelFromFloatData(T *nativeTypeOut, float *data, Cogs::TextureExtent textureExtent, uint32_t channels, bool isSigned, bool isFloat, bool isHalfFloat, bool isSRGB, bool hasAlpha)
145 {
146 float valRange = std::powf(2, 8 * sizeof(T)) - 1.0f;
147
148 for (size_t y = 0; y < textureExtent.height; y++)
149 {
150 for (size_t x = 0; x < textureExtent.width; x++)
151 {
152 float *writeFloat = &data[channels * (y * textureExtent.width + x)];
153 T *write = &nativeTypeOut[channels * (y * textureExtent.width + x)];
154
155 float *s0 = &data[channels * ((2 * y + 0) * (2 * textureExtent.width) + (2 * x + 0))];
156 float *s1 = &data[channels * ((2 * y + 0) * (2 * textureExtent.width) + (2 * x + 1))];
157 float *s2 = &data[channels * ((2 * y + 1) * (2 * textureExtent.width) + (2 * x + 0))];
158 float *s3 = &data[channels * ((2 * y + 1) * (2 * textureExtent.width) + (2 * x + 1))];
159
160 for (size_t channel = 0; channel < channels; channel++) {
161 float channelDataFloat = (s0[channel] + s1[channel] + s2[channel] + s3[channel]) / 4.0f;
162
163 writeFloat[channel] = channelDataFloat;
164
165 size_t isSignBitSet = static_cast<size_t>(isSigned) & (static_cast<size_t>(channelDataFloat) >> ((sizeof(T) * 8) - 1));
166
167 float channelDataFloatShifted = channelDataFloat + 1.0f * static_cast<float>(static_cast<size_t>(!isHalfFloat)) * static_cast<float>(isSignBitSet) * static_cast<float>(static_cast<size_t>(!isFloat));
168
169 size_t isNotAlphaChannel = static_cast<size_t>(!hasAlpha) + (static_cast<size_t>(hasAlpha) * static_cast<size_t>((channel < (channels-1))));
170 size_t isAlphaChannel = static_cast<size_t>(hasAlpha) * static_cast<size_t>((channel == (channels-1)));
171
172 glm::vec3 channelDataVecGamma = glm::convertLinearToSRGB(glm::vec3(channelDataFloatShifted, 0.0f, 0.0f));
173 float channelDataGamma = channelDataVecGamma.r;
174
175 float channelDataLinearToSRGB = static_cast<size_t>(isSRGB) * ((isNotAlphaChannel * channelDataGamma) + (isAlphaChannel * channelDataFloatShifted));
176
177 channelDataFloatShifted = (static_cast<size_t>(!isSRGB) * channelDataFloatShifted) + (static_cast<size_t>(isSRGB) * channelDataLinearToSRGB);
178
179 uint16_t floatToHalfFloat = static_cast<uint16_t>(glm::packHalf2x16(glm::vec2(channelDataFloat, 0.0f)));
180
181 T channelData = static_cast<T>((static_cast<size_t>(!isFloat) * static_cast<size_t>(!isHalfFloat) * channelDataFloatShifted * valRange) + (static_cast<size_t>(isFloat) * channelDataFloat) + (static_cast<size_t>(isHalfFloat) * floatToHalfFloat));
182
183 write[channel] = channelData;
184 }
185 }
186 }
187 }
188
189 template <typename T>
190 static void generateMipmapsInternal(GraphicsDeviceWebGPU *graphicsDevice, WGPUTexture *destinationTexture, const TextureData *data, uint32_t faces, uint32_t layers, uint32_t levels, uint32_t bytesPerChannel, uint32_t channels, bool isSigned, bool isFloat, bool isHalfFloat, bool isSRGB, bool hasAlpha)
191 {
192 uint32_t bytesPerPixel = bytesPerChannel * channels;
193 size_t nPixels = data->getLevelSize(0) / bytesPerPixel;
194
195 size_t levelSize = data->getLevelSize(0);
196
197 std::vector<T> currLevelData(levelSize);
198 std::vector<T> prevLevelData(nPixels * channels);
199 std::vector<float> prevLevelDataFloat(nPixels * channels);
200
201 Cogs::TextureExtent textureExtent = {};
202
203 for (uint32_t f = 0; f < faces; ++f) { // Faces
204 for (uint32_t a = 0; a < layers; ++a) { // Array layers
205 textureExtent = data->getExtent(0);
206
207 T* tmpLevelData = reinterpret_cast<T*>(const_cast<void*>(data->getData(a, f, 0)));
208 memcpy(prevLevelData.data(), tmpLevelData, levelSize);
209
210 writeMipLevelToGPU(graphicsDevice, destinationTexture, prevLevelData.data(), f, a, 0, textureExtent, levelSize, bytesPerPixel);
211
212 convertMiplevelDataToFloatData<T>(prevLevelDataFloat.data(), prevLevelData.data(), nPixels, channels, isSigned, isFloat, isHalfFloat, isSRGB, hasAlpha);
213
214 for (uint32_t m = 1; m < levels; ++m) { // Miplevels
215 textureExtent = data->getExtent(m);
216 size_t currentLevelSize = data->getLevelSize(m);
217
218 nextMiplevelFromFloatData(currLevelData.data(), prevLevelDataFloat.data(), textureExtent, channels, isSigned, isFloat, isHalfFloat, isSRGB, hasAlpha);
219
220 writeMipLevelToGPU(graphicsDevice, destinationTexture, currLevelData.data(), f, a, m, textureExtent, currentLevelSize, bytesPerPixel);
221 }
222 }
223 }
224 }
225
227 {
228 const bool generateMips = (td.flags & TextureFlags::GenerateMipMaps) != 0;
229 ResourceCountersWebGPU &counters = graphicsDevice->counters;
230 WGPUDevice device = graphicsDevice->device;
231
232 if(graphicsDevice->use_error_scope){
233 wgpuDevicePushErrorScope(graphicsDevice->device, graphicsDevice->filter);
234 }
235
236 TextureWebGPU texture = {};
237 texture.estimatedByteSize = uint32_t(td.estimateMemorySize());
238
239 WGPUTextureFormat format = TextureFormatsWebGPU[(size_t)td.format];
240 // if (data && format.isCompressed) {
241 // texture.estimatedByteSize = uint32_t(data->getSize());
242 // }
243 WGPUTextureFormat view_formats[] = {format};
244
245 uint32_t mipLevelCount = td.levels;
246
247 // Create texture
248 WGPUTextureDescriptor desc = WGPU_TEXTURE_DESCRIPTOR_INIT;
249 desc.label; // TODO
250 desc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding;
251 desc.usage |= WGPUTextureUsage_CopySrc; // TODO add to td.flags???
252 if((td.flags&TextureFlags::RenderTarget) != 0 ||
253 (td.flags&TextureFlags::DepthBuffer) != 0){
254 desc.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst | WGPUTextureUsage_CopySrc;
255 }
256 if((td.flags&TextureFlags::ReadWriteTexture) != 0){
257 desc.usage |= WGPUTextureUsage_StorageBinding;
258 }
259 if(td.target == ResourceDimensions::Texture1D ||
260 td.target == ResourceDimensions::Texture1DArray){
261 desc.dimension = WGPUTextureDimension_1D;
262 }
263 else if(td.target == ResourceDimensions::Texture3D ||
264 td.target == ResourceDimensions::Texture3DArray){
265 desc.dimension = WGPUTextureDimension_3D;
266 }
267 else{
268 desc.dimension = WGPUTextureDimension_2D;
269 }
270
271 const Cogs::FormatInfo *textureFormat = Cogs::getFormatInfo(td.format);
272 bool compressedFormat =
273 ((textureFormat->blockExtent.width != 1) ||
274 (textureFormat->blockExtent.height != 1) ||
275 (textureFormat->blockExtent.depth != 1));
276
277 if(generateMips) {
278 if(compressedFormat) {
279 LOG_WARNING_ONCE(logger, "Generating mips for compressed textures is not supported.");
280 } else {
281 size_t max = glm::max(td.width, td.height);
282 if (max != 0) {
283 mipLevelCount = 1;
284 while (max >>= 1) {
285 ++mipLevelCount;
286 }
287 }
288 }
289 }
290
291 desc.size = {td.width, td.height, td.depth*td.faces*td.layers };
292 desc.format = format;
293 desc.mipLevelCount = mipLevelCount;
294 // WebGPU only supports sample counts 1 and 4.
295 desc.sampleCount = td.samples > 1 ? 4 : 1;
296 desc.viewFormatCount = sizeof(view_formats)/sizeof(view_formats[0]);
297 desc.viewFormats = view_formats;
298
299#ifndef __EMSCRIPTEN__
300 wgpuDeviceValidateTextureDescriptor(device, &desc);
301#endif
302
303 if (td.flags & TextureFlags::ExternalTexture) {
304 assert(data != nullptr && data->externalHandle != 0);
305 if (mipLevelCount != 1) {
306 LOG_WARNING_ONCE(logger, "External WebGPU textures currently only support 1 mip level; mipmap generation is deferred.");
307 mipLevelCount = 1;
308 desc.mipLevelCount = 1;
309 }
310 texture.texture = reinterpret_cast<WGPUTexture>(data->externalHandle);
311 } else {
312 texture.texture = wgpuDeviceCreateTexture(device, &desc);
313 }
314 counters.texture++;
315
316 // Create default texture view
317 texture.view_format = desc.format;
318 texture.format = td.format;
319 if(td.target == ResourceDimensions::Texture1D ||
320 td.target == ResourceDimensions::Texture1DArray){
321 texture.view_dimension = WGPUTextureViewDimension_1D;
322 }
323 else if(td.target == ResourceDimensions::Texture2D ||
324 td.target == ResourceDimensions::Texture2DMS){
325 texture.view_dimension = WGPUTextureViewDimension_2D;
326 }
327 else if(td.target == ResourceDimensions::Texture2DArray ||
328 td.target == ResourceDimensions::Texture2DMSArray){
329 texture.view_dimension = WGPUTextureViewDimension_2DArray;
330 }
331 else if(td.target == ResourceDimensions::Texture3D ||
332 td.target == ResourceDimensions::Texture3DArray){
333 texture.view_dimension = WGPUTextureViewDimension_3D;
334 }
335 else if(td.target == ResourceDimensions::TextureCube){
336 texture.view_dimension = WGPUTextureViewDimension_Cube;
337 }
338 else if(td.target == ResourceDimensions::TextureCubeArray){
339 texture.view_dimension = WGPUTextureViewDimension_CubeArray;
340 }
341 else{
342 assert(false);
343 }
344
345 WGPUTextureViewDescriptor view_desc = WGPU_TEXTURE_VIEW_DESCRIPTOR_INIT;
346 view_desc.label = {"DefaultTextureView", WGPU_STRLEN};
347 view_desc.format = format;
348 view_desc.dimension = texture.view_dimension;
349 view_desc.baseMipLevel = 0;
350 view_desc.mipLevelCount = mipLevelCount;
351 view_desc.baseArrayLayer = 0;
352 view_desc.arrayLayerCount = td.faces*td.layers;
353 view_desc.aspect = WGPUTextureAspect_All;
354 texture.texture_view = wgpuTextureCreateView(texture.texture, &view_desc);
355 counters.texture_view++;
356
357 texture.width = td.width;
358 texture.height = td.height;
359 texture.samples = desc.sampleCount;
360 texture.flags = td.flags;
361
362 // Upload Data
363 if(data){
364 uint32_t faces = std::min(td.faces, static_cast<uint32_t>(data->faces));
365 uint32_t layers = std::min(td.layers, static_cast<uint32_t>(data->layers));
366 uint32_t levels = mipLevelCount;
367
368 if(generateMips && !compressedFormat) {
369 uint32_t bytesPerChannel = textureFormat->blockSize / textureFormat->elements;
370 Cogs::DataFormat formatType = parseDataFormat(textureFormat->name);
371
372 switch (formatType) {
373 case(Cogs::DataFormat::R8_UNORM):
374 case(Cogs::DataFormat::R8G8_UNORM):
375 case(Cogs::DataFormat::R8G8B8_UNORM):
376 case(Cogs::DataFormat::R8G8B8A8_UNORM):
377 {
378 generateMipmapsInternal<uint8_t>(
379 graphicsDevice,
380 &texture.texture,
381 data,
382 faces,
383 layers,
384 levels,
385 bytesPerChannel,
386 textureFormat->elements, // number of channels
387 false, // isSigned
388 false, // isFloat
389 false, // isHalfFloat
390 false, // isSRGB
391 false); // hasAlpha
392 break;
393 }
394 case(Cogs::DataFormat::R8_SNORM):
395 case(Cogs::DataFormat::R8G8_SNORM):
396 case(Cogs::DataFormat::R8G8B8_SNORM):
397 case(Cogs::DataFormat::R8G8B8A8_SNORM):
398 {
399 generateMipmapsInternal<uint8_t>(
400 graphicsDevice,
401 &texture.texture,
402 data,
403 faces,
404 layers,
405 levels,
406 bytesPerChannel,
407 textureFormat->elements, // number of channels
408 true, // isSigned
409 false, // isFloat
410 false, // isHalfFloat
411 false, // isSRGB
412 false); // hasAlpha
413 break;
414 }
415 case(Cogs::DataFormat::R16_UNORM):
416 case(Cogs::DataFormat::R16G16_UNORM):
417 case(Cogs::DataFormat::R16G16B16_UNORM):
418 case(Cogs::DataFormat::R16G16B16A16_UNORM):
419 {
420 generateMipmapsInternal<uint16_t>(
421 graphicsDevice,
422 &texture.texture,
423 data,
424 faces,
425 layers,
426 levels,
427 bytesPerChannel,
428 textureFormat->elements, // number of channels
429 false, // isSigned
430 false, // isFloat
431 false, // isHalfFloat
432 false, // isSRGB
433 false); // hasAlpha
434 break;
435 }
436 case(Cogs::DataFormat::R16_SNORM):
437 case(Cogs::DataFormat::R16G16_SNORM):
438 case(Cogs::DataFormat::R16G16B16_SNORM):
439 case(Cogs::DataFormat::R16G16B16A16_SNORM):
440 {
441 generateMipmapsInternal<uint16_t>(
442 graphicsDevice,
443 &texture.texture,
444 data,
445 faces,
446 layers,
447 levels,
448 bytesPerChannel,
449 textureFormat->elements, // number of channels
450 true, // isSigned
451 false, // isFloat
452 false, // isHalfFloat
453 false, // isSRGB
454 false); // hasAlpha
455 break;
456 }
457 case(Cogs::DataFormat::R16_FLOAT):
458 case(Cogs::DataFormat::R16G16_FLOAT):
459 case(Cogs::DataFormat::R16G16B16_FLOAT):
460 case(Cogs::DataFormat::R16G16B16A16_FLOAT):
461 {
462 generateMipmapsInternal<uint16_t>(
463 graphicsDevice,
464 &texture.texture,
465 data,
466 faces,
467 layers,
468 levels,
469 bytesPerChannel,
470 textureFormat->elements, // number of channels
471 false, // isSigned
472 false, // isFloat
473 true, // isHalfFloat
474 false, // isSRGB
475 false); // hasAlpha
476 break;
477 }
478 case(Cogs::DataFormat::R32_FLOAT):
479 case(Cogs::DataFormat::R32G32_FLOAT):
480 case(Cogs::DataFormat::R32G32B32_FLOAT):
481 case(Cogs::DataFormat::R32G32B32A32_FLOAT):
482 {
483 generateMipmapsInternal<float>(
484 graphicsDevice,
485 &texture.texture,
486 data,
487 faces,
488 layers,
489 levels,
490 bytesPerChannel,
491 textureFormat->elements, // number of channels
492 false, // isSigned
493 true, // isFloat
494 false, // isHalfFloat
495 false, // isSRGB
496 false); // hasAlpha
497 break;
498 }
499 case(Cogs::DataFormat::R8G8B8_UNORM_SRGB):
500 {
501 generateMipmapsInternal<uint8_t>(
502 graphicsDevice,
503 &texture.texture,
504 data,
505 faces,
506 layers,
507 levels,
508 bytesPerChannel,
509 textureFormat->elements, // number of channels
510 false, // isSigned
511 false, // isFloat
512 false, // isHalfFloat
513 true, // isSRGB
514 false); // hasAlpha
515 break;
516 }
517 case(Cogs::DataFormat::R8G8B8A8_UNORM_SRGB):
518 {
519 generateMipmapsInternal<uint8_t>(
520 graphicsDevice,
521 &texture.texture,
522 data,
523 faces,
524 layers,
525 levels,
526 bytesPerChannel,
527 textureFormat->elements, // number of channels
528 false, // isSigned
529 false, // isFloat
530 false, // isHalfFloat
531 true, // isSRGB
532 true); // hasAlpha
533 break;
534 }
535 default: {
536 break;
537 }
538 }
539 } else {
540 for (size_t f = 0; f < faces; ++f) { // Faces
541 for (size_t a = 0; a < layers; ++a) { // Array layer
542 for (size_t m = 0; m < mipLevelCount; ++m) { // Array layer
543 Cogs::TextureExtent extent = data->getExtent(m);
544 const void* ptr = data->getData(a, f, m);
545 const size_t ptr_size = data->getLevelSize(m);
546
547 if ((extent.width % data->blockExtent.width) != 0) {
548 extent.width = (extent.width / data->blockExtent.width + 1) * data->blockExtent.width;
549 }
550 if ((extent.height % data->blockExtent.height) != 0) {
551 extent.height = (extent.height / data->blockExtent.height + 1) * data->blockExtent.height;
552 }
553
554 WGPUTexelCopyTextureInfo dst = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT;
555 dst.texture = texture.texture;
556 dst.mipLevel = (uint32_t)m;
557 dst.origin = { 0, 0, (uint32_t)(a + f) };
558 dst.aspect = WGPUTextureAspect_All;
559
560 WGPUTexelCopyBufferLayout layout = WGPU_TEXEL_COPY_BUFFER_LAYOUT_INIT;
561 layout.offset = 0;
562 layout.bytesPerRow = (uint32_t)data->getPitch(m);
563 layout.rowsPerImage = extent.height;
564 WGPUExtent3D size = { extent.width, extent.height, extent.depth };
565 wgpuQueueWriteTexture(graphicsDevice->queue, &dst, ptr, ptr_size, &layout, &size);
566 }
567 }
568 }
569 }
570 }
571
572 textureMemoryConsumption += texture.estimatedByteSize;
573
574 if(graphicsDevice->use_error_scope){
575 WGPUPopErrorScopeCallbackInfo info = WGPU_POP_ERROR_SCOPE_CALLBACK_INFO_INIT;
576 info.mode = WGPUCallbackMode_AllowSpontaneous;
577 info.callback = textures_error_callback;
578 info.userdata1 = this;
579 info.userdata2 = graphicsDevice;
580 WGPUFuture future = wgpuDevicePopErrorScope(graphicsDevice->device, info);
581 // WGPUWaitStatus wgpuInstanceWaitAny(WGPUInstance instance, size_t futureCount, WGPUFutureWaitInfo * futures, uint64_t timeoutNS);
582 }
583
584 return textures.addResource(std::move(texture));
585 }
586
587
588 void TexturesWebGPU::releaseTexture(TextureHandle handle)
589 {
590 if(!HandleIsValid(handle)) return;
591 ResourceCountersWebGPU &counters = graphicsDevice->counters;
592
593 TextureWebGPU &texture = textures[handle];
594 wgpuTextureViewRelease(texture.texture_view);
595 counters.texture_view--;
596 if ((texture.flags & TextureFlags::NoDelete) == 0 &&
597 (texture.flags & TextureFlags::ExternalTexture) == 0) {
598 wgpuTextureDestroy(texture.texture);
599 wgpuTextureRelease(texture.texture);
600 }
601 counters.texture--;
602 textureMemoryConsumption -= texture.estimatedByteSize;
603
604 textures.removeResource(handle);
605 }
606
607 void TexturesWebGPU::releaseNativeTexture(TextureNativeHandle nativeHandle)
608 {
609 WGPUTexture texture = reinterpret_cast<WGPUTexture>(nativeHandle);
610 wgpuTextureDestroy(texture);
611 wgpuTextureRelease(texture);
612 }
613
614 TextureViewHandle TexturesWebGPU::createTextureView(TextureViewDescription &view)
615 {
616 ResourceCountersWebGPU &counters = graphicsDevice->counters;
617 const TextureWebGPU &texture = textures[view.texture];
618
619 if(graphicsDevice->use_error_scope){
620 wgpuDevicePushErrorScope(graphicsDevice->device, graphicsDevice->filter);
621 }
622
623 TextureViewWebGPU texture_view = {};
624
625 WGPUTextureViewDescriptor desc = WGPU_TEXTURE_VIEW_DESCRIPTOR_INIT;
626 desc.label; // TODO
627 desc.format = texture.view_format;
628 desc.dimension = texture.view_dimension;
629 if(view.numLayers == 1){
630 switch(desc.dimension){
631 case WGPUTextureViewDimension_2DArray:
632 desc.dimension = WGPUTextureViewDimension_2D;
633 break;
634 case WGPUTextureViewDimension_CubeArray:
635 desc.dimension = WGPUTextureViewDimension_Cube;
636 break;
637 default:
638 break;
639 }
640 }
641 desc.baseMipLevel = view.levelIndex;
642 desc.mipLevelCount = view.numLevels;
643 desc.baseArrayLayer = view.layerIndex;
644 desc.arrayLayerCount = view.numLayers;
645 desc.aspect = WGPUTextureAspect_All;
646 texture_view.texture_view = wgpuTextureCreateView(texture.texture, &desc);
647 counters.texture_view++;
648
649 if(graphicsDevice->use_error_scope){
650 WGPUPopErrorScopeCallbackInfo info = WGPU_POP_ERROR_SCOPE_CALLBACK_INFO_INIT;
651 info.mode = WGPUCallbackMode_AllowSpontaneous;
652 info.callback = textures_error_callback;
653 info.userdata1 = this;
654 info.userdata2 = graphicsDevice;
655 WGPUFuture future = wgpuDevicePopErrorScope(graphicsDevice->device, info);
656 // WGPUWaitStatus wgpuInstanceWaitAny(WGPUInstance instance, size_t futureCount, WGPUFutureWaitInfo * futures, uint64_t timeoutNS);
657 }
658
659 return textureViews.addResource(std::move(texture_view));
660 }
661
662 void TexturesWebGPU::releaseTextureView(const TextureViewHandle &handle)
663 {
664 if(!HandleIsValid(handle)) return;
665 ResourceCountersWebGPU &counters = graphicsDevice->counters;
666
667 TextureViewWebGPU &texture_view = textureViews[handle];
668 wgpuTextureViewRelease(texture_view.texture_view);
669 counters.texture_view--;
670
671 textureViews.removeResource(handle);
672 }
673
674 SamplerStateHandle TexturesWebGPU::loadSamplerState(const SamplerState &state)
675 {
676 ResourceCountersWebGPU &counters = graphicsDevice->counters;
677 WGPUDevice device = graphicsDevice->device;
678
679 if(graphicsDevice->use_error_scope){
680 wgpuDevicePushErrorScope(graphicsDevice->device, graphicsDevice->filter);
681 }
682
683 SamplerStateWebGPU sampler = {};
684
685 WGPUSamplerDescriptor desc = WGPU_SAMPLER_DESCRIPTOR_INIT;
686 desc.label; // TODO
687 desc.addressModeU = wgpu(state.addressModeS);
688 desc.addressModeV = wgpu(state.addressModeT);
689 desc.addressModeW = wgpu(state.addressModeW);
690 switch(state.filter){
691 case SamplerState::FilterMode::MinMagMipPoint:
692 desc.magFilter = WGPUFilterMode_Nearest;
693 desc.minFilter = WGPUFilterMode_Nearest;
694 desc.mipmapFilter = WGPUMipmapFilterMode_Nearest;
695 desc.compare = WGPUCompareFunction_Undefined;
696 break;
697 case SamplerState::FilterMode::MinMagMipLinear:
698 desc.magFilter = WGPUFilterMode_Linear;
699 desc.minFilter = WGPUFilterMode_Linear;
700 desc.mipmapFilter = WGPUMipmapFilterMode_Linear;
701 desc.compare = WGPUCompareFunction_Undefined;
702 break;
703 case SamplerState::FilterMode::ComparisonMinMagMipPoint:
704 desc.magFilter = WGPUFilterMode_Nearest;
705 desc.minFilter = WGPUFilterMode_Nearest;
706 desc.mipmapFilter = WGPUMipmapFilterMode_Nearest;
707 desc.compare = wgpu(state.comparisonFunction);
708 break;
709 case SamplerState::FilterMode::ComparisonMinMagMipLinear:
710 desc.magFilter = WGPUFilterMode_Linear;
711 desc.minFilter = WGPUFilterMode_Linear;
712 desc.mipmapFilter = WGPUMipmapFilterMode_Linear;
713 desc.compare = wgpu(state.comparisonFunction);
714 break;
715 default:
716 assert(false);
717 break;
718 }
719 desc.lodMinClamp = 0;
720 desc.lodMaxClamp = 32;
721 if(state.maxAnisotropy < 1.0f)
722 desc.maxAnisotropy = 1;
723 else
724 desc.maxAnisotropy = (uint16_t)state.maxAnisotropy;
725 if ((desc.minFilter == WGPUFilterMode_Nearest || desc.magFilter == WGPUFilterMode_Nearest) && desc.maxAnisotropy > 1) {
726 LOG_WARNING(logger, "Sampler with nearest filter must set maxAnisotropy = 1");
727 desc.maxAnisotropy = 1;
728 }
729 sampler.sampler = wgpuDeviceCreateSampler(device, &desc);
730 counters.sampler++;
731
732 if(graphicsDevice->use_error_scope){
733 WGPUPopErrorScopeCallbackInfo info = WGPU_POP_ERROR_SCOPE_CALLBACK_INFO_INIT;
734 info.mode = WGPUCallbackMode_AllowSpontaneous;
735 info.callback = textures_error_callback;
736 info.userdata1 = this;
737 info.userdata2 = graphicsDevice;
738 WGPUFuture future = wgpuDevicePopErrorScope(graphicsDevice->device, info);
739 // WGPUWaitStatus wgpuInstanceWaitAny(WGPUInstance instance, size_t futureCount, WGPUFutureWaitInfo * futures, uint64_t timeoutNS);
740 }
741
742 return samplerStates.addResource(std::move(sampler));
743 }
744
745 void TexturesWebGPU::releaseSamplerState(SamplerStateHandle handle)
746 {
747 if(!HandleIsValid(handle)) return;
748 ResourceCountersWebGPU &counters = graphicsDevice->counters;
749
750 SamplerStateWebGPU &sampler = samplerStates[handle];
751 wgpuSamplerRelease(sampler.sampler);
752 counters.sampler--;
753
754 samplerStates.removeResource(handle);
755 }
756
757 void TexturesWebGPU::generateMipmaps(TextureHandle /*handle*/)
758 {
759 // TODO
760 }
761
762 void TexturesWebGPU::releaseResources()
763 {
764 // TODO
765 }
766
767}
Log implementation class.
Definition: LogManager.h:140
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:50
virtual void annotate(TextureHandle handle, const StringView &name) override
Associate a name with an object for use in graphics debugging.
virtual TextureHandle loadTexture(const TextureDescription &desc, const TextureData *data) override
Load a texture from the given description.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
uint16_t elements
Number of channels in a data item.
Definition: DataFormat.h:259
uint8_t blockSize
Bytesize of one block of data.
Definition: DataFormat.h:257
TextureExtent blockExtent
Number of data items in a block.
Definition: DataFormat.h:258
const char * name
Name as a color format (using RGBA as channels).
Definition: DataFormat.h:260
Encapsulates state for texture sampling in a state object.
Definition: SamplerState.h:12
ComparisonFunction comparisonFunction
Specifies the comparison function to use when applying a comparison sampler.
Definition: SamplerState.h:73
unsigned int maxAnisotropy
Specifies the maximum number of anisotropic samples to use when sampling a texture.
Definition: SamplerState.h:76
AddressMode addressModeW
Specifies the addressing mode along the W axis in texture coordinate space.
Definition: SamplerState.h:67
ComparisonFunction
Comparison functions applied when sampling depth buffers using comparison filters.
Definition: SamplerState.h:48
AddressMode addressModeS
Specifies the addressing mode along the S axis in texture coordinate space.
Definition: SamplerState.h:63
AddressMode
Addressing modes to use when sampling textures.
Definition: SamplerState.h:15
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
Definition: SamplerState.h:17
@ Border
Texture color is set to the border color when outside [0, 1] range.
Definition: SamplerState.h:23
@ Wrap
Texture coordinates automatically wrap around to [0, 1] range.
Definition: SamplerState.h:19
@ Mirror
Texture coordinates are mirrored when outside [0, 1] range.
Definition: SamplerState.h:21
FilterMode filter
Specifies the filter to use for texture sampling.
Definition: SamplerState.h:70
AddressMode addressModeT
Specifies the addressing mode along the T axis in texture coordinate space.
Definition: SamplerState.h:65
@ DepthBuffer
The texture can be used as a depth target and have depth buffer values written into.
Definition: Flags.h:122
@ RenderTarget
The texture can be used as a render target and drawn into.
Definition: Flags.h:120
@ GenerateMipMaps
The texture supports automatic mipmap generation performed by the graphics device.
Definition: Flags.h:124
@ ReadWriteTexture
The texture can be used as a read/write texture. Can be used to output data from compute shaders.
Definition: Flags.h:128
Describes how to fetch data from a texture in shaders.
Definition: ITextures.h:13
uint32_t layerIndex
Index of the first layer (if array) to fetch from.
Definition: ITextures.h:17
uint32_t numLayers
Number of array layers available.
Definition: ITextures.h:19
uint32_t numLevels
Number of mipmap levels available.
Definition: ITextures.h:23
uint32_t levelIndex
First mipmap level to fetch data from.
Definition: ITextures.h:21
TextureHandle texture
Texture.
Definition: ITextures.h:15