1#include "TexturesWebGPU.h"
3#include "FormatsWebGPU.h"
4#include "GraphicsDeviceWebGPU.h"
5#include "Foundation/Logging/Logger.h"
7#include "Foundation/StringView.h"
9#include <glm/gtc/color_space.hpp>
10#include <glm/detail/func_packing.inl>
17 case Cogs::SamplerState::ComparisonFunction::Never:
18 return WGPUCompareFunction_Never;
19 case Cogs::SamplerState::ComparisonFunction::Less:
20 return WGPUCompareFunction_Less;
21 case Cogs::SamplerState::ComparisonFunction::Equal:
22 return WGPUCompareFunction_Equal;
23 case Cogs::SamplerState::ComparisonFunction::LessEqual:
24 return WGPUCompareFunction_LessEqual;
25 case Cogs::SamplerState::ComparisonFunction::Greater:
26 return WGPUCompareFunction_Greater;
27 case Cogs::SamplerState::ComparisonFunction::NotEqual:
28 return WGPUCompareFunction_NotEqual;
29 case Cogs::SamplerState::ComparisonFunction::GreaterEqual:
30 return WGPUCompareFunction_GreaterEqual;
31 case Cogs::SamplerState::ComparisonFunction::Always:
32 return WGPUCompareFunction_Always;
33 case Cogs::SamplerState::ComparisonFunction::ComparisonFunction_Size:
37 return WGPUCompareFunction_Undefined;
44 return WGPUAddressMode_ClampToEdge;
46 return WGPUAddressMode_Repeat;
48 return WGPUAddressMode_MirrorRepeat;
50 LOG_WARNING_ONCE(logger,
"WebGPU backend does not support SamplerState::AddressMode::Border.");
51 return WGPUAddressMode_ClampToEdge;
52 case Cogs::SamplerState::AddressMode::AddressMode_Size:
56 return WGPUAddressMode_Repeat;
62 void TexturesWebGPU::initialize(
class GraphicsDeviceWebGPU *device)
64 graphicsDevice = device;
70 wgpuTextureSetLabel(texture.texture, {name.data(), WGPU_STRLEN});
73 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)
75 WGPUTexelCopyBufferLayout layout = {};
77 layout.bytesPerRow = bytesPerPixel * textureExtent.width;
78 layout.rowsPerImage = textureExtent.height;
80 WGPUTexelCopyTextureInfo destination = {};
81 destination.texture = *destinationTexture;
82 destination.aspect = WGPUTextureAspect_All;
83 destination.origin = { 0, 0,
static_cast<uint32_t
>(arrayLayer + cubeFace) };
84 destination.mipLevel =
static_cast<uint32_t
>(mipLevel);
85 WGPUExtent3D sizeExtent = { textureExtent.width, textureExtent.height, textureExtent.depth };
86 wgpuQueueWriteTexture(graphicsDevice->queue, &destination, data, levelSize, &layout, &sizeExtent);
90 static void convertMiplevelDataToFloatData(
float *out, T *data,
size_t nPixels, uint32_t channels,
bool isSigned,
bool isFloat,
bool isHalfFloat,
bool isSRGB,
bool hasAlpha)
92 float valRange = std::powf(2, 8 *
sizeof(T)) - 1.0f;
94 memcpy(out, data,
static_cast<size_t>(isFloat) * nPixels * channels *
sizeof(
float));
96 for(
size_t pixel = 0; pixel < nPixels * static_cast<size_t>(!isFloat); pixel++) {
97 for(
size_t channel = 0; channel < channels; channel++) {
98 T channelData = data[(pixel * channels) + channel];
99 size_t isSignBitSet =
static_cast<size_t>(isSigned) & (
static_cast<size_t>(channelData) >> ((
sizeof(T) * 8) - 1));
102 T channelDataShifted = channelData +
static_cast<T
>((
static_cast<size_t>(!isHalfFloat) * isSignBitSet * (
static_cast<size_t>(valRange) >> 1)));
104 glm::vec2 halfFloatToFloatVec = glm::unpackHalf2x16(
static_cast<uint32_t
>(channelDataShifted));
105 float halfFloatToFloat = halfFloatToFloatVec.x;
107 float channelDataAsFloat = (
static_cast<size_t>(!isHalfFloat) *
static_cast<float>(channelDataShifted) / valRange) + (
static_cast<float>(
static_cast<size_t>(isHalfFloat)) * halfFloatToFloat);
108 channelDataAsFloat -= 1.0f * isSignBitSet *
static_cast<size_t>(!isHalfFloat);
110 size_t isNotAlphaChannel =
static_cast<size_t>(!hasAlpha) + (
static_cast<size_t>(hasAlpha) *
static_cast<size_t>((channel < (channels-1))));
111 size_t isAlphaChannel =
static_cast<size_t>(hasAlpha) *
static_cast<size_t>((channel == (channels-1)));
113 glm::vec3 channelDataVecGamma = glm::convertSRGBToLinear(glm::vec3(channelDataAsFloat, 0.0f, 0.0f));
114 float channelDataGamma = channelDataVecGamma.r;
116 float dataSRGBToLinear =
static_cast<size_t>(isSRGB) * ((isNotAlphaChannel * channelDataGamma) + (isAlphaChannel * channelDataAsFloat));
118 channelDataAsFloat = (
static_cast<size_t>(!isSRGB) * channelDataAsFloat) + (
static_cast<size_t>(isSRGB) * dataSRGBToLinear);
120 out[(pixel * channels) + channel] = channelDataAsFloat;
125 template <
typename T>
126 static void nextMiplevelFromFloatData(T *nativeTypeOut,
float *data,
Cogs::TextureExtent textureExtent, uint32_t channels,
bool isSigned,
bool isFloat,
bool isHalfFloat,
bool isSRGB,
bool hasAlpha)
128 float valRange = std::powf(2, 8 *
sizeof(T)) - 1.0f;
130 for (
size_t y = 0; y < textureExtent.height; y++)
132 for (
size_t x = 0; x < textureExtent.width; x++)
134 float *writeFloat = &data[channels * (y * textureExtent.width + x)];
135 T *write = &nativeTypeOut[channels * (y * textureExtent.width + x)];
137 float *s0 = &data[channels * ((2 * y + 0) * (2 * textureExtent.width) + (2 * x + 0))];
138 float *s1 = &data[channels * ((2 * y + 0) * (2 * textureExtent.width) + (2 * x + 1))];
139 float *s2 = &data[channels * ((2 * y + 1) * (2 * textureExtent.width) + (2 * x + 0))];
140 float *s3 = &data[channels * ((2 * y + 1) * (2 * textureExtent.width) + (2 * x + 1))];
142 for (
size_t channel = 0; channel < channels; channel++) {
143 float channelDataFloat = (s0[channel] + s1[channel] + s2[channel] + s3[channel]) / 4.0f;
145 writeFloat[channel] = channelDataFloat;
147 size_t isSignBitSet =
static_cast<size_t>(isSigned) & (
static_cast<size_t>(channelDataFloat) >> ((
sizeof(T) * 8) - 1));
149 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));
151 size_t isNotAlphaChannel =
static_cast<size_t>(!hasAlpha) + (
static_cast<size_t>(hasAlpha) *
static_cast<size_t>((channel < (channels-1))));
152 size_t isAlphaChannel =
static_cast<size_t>(hasAlpha) *
static_cast<size_t>((channel == (channels-1)));
154 glm::vec3 channelDataVecGamma = glm::convertLinearToSRGB(glm::vec3(channelDataFloatShifted, 0.0f, 0.0f));
155 float channelDataGamma = channelDataVecGamma.r;
157 float channelDataLinearToSRGB =
static_cast<size_t>(isSRGB) * ((isNotAlphaChannel * channelDataGamma) + (isAlphaChannel * channelDataFloatShifted));
159 channelDataFloatShifted = (
static_cast<size_t>(!isSRGB) * channelDataFloatShifted) + (
static_cast<size_t>(isSRGB) * channelDataLinearToSRGB);
161 uint16_t floatToHalfFloat =
static_cast<uint16_t
>(glm::packHalf2x16(glm::vec2(channelDataFloat, 0.0f)));
163 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));
165 write[channel] = channelData;
171 template <
typename T>
172 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)
174 uint32_t bytesPerPixel = bytesPerChannel * channels;
175 size_t nPixels = data->getLevelSize(0) / bytesPerPixel;
177 size_t levelSize = data->getLevelSize(0);
179 std::vector<T> currLevelData(levelSize);
180 std::vector<T> prevLevelData(nPixels * channels);
181 std::vector<float> prevLevelDataFloat(nPixels * channels);
185 for (uint32_t f = 0; f < faces; ++f) {
186 for (uint32_t a = 0; a < layers; ++a) {
187 textureExtent = data->getExtent(0);
189 T* tmpLevelData =
reinterpret_cast<T*
>(
const_cast<void*
>(data->getData(a, f, 0)));
190 memcpy(prevLevelData.data(), tmpLevelData, levelSize);
192 writeMipLevelToGPU(graphicsDevice, destinationTexture, prevLevelData.data(), f, a, 0, textureExtent, levelSize, bytesPerPixel);
194 convertMiplevelDataToFloatData<T>(prevLevelDataFloat.data(), prevLevelData.data(), nPixels, channels, isSigned, isFloat, isHalfFloat, isSRGB, hasAlpha);
196 for (uint32_t m = 1; m < levels; ++m) {
197 textureExtent = data->getExtent(m);
198 size_t currentLevelSize = data->getLevelSize(m);
200 nextMiplevelFromFloatData(currLevelData.data(), prevLevelDataFloat.data(), textureExtent, channels, isSigned, isFloat, isHalfFloat, isSRGB, hasAlpha);
202 writeMipLevelToGPU(graphicsDevice, destinationTexture, currLevelData.data(), f, a, m, textureExtent, currentLevelSize, bytesPerPixel);
211 WGPUDevice device = graphicsDevice->device;
215 WGPUTextureFormat format = TextureFormatsWebGPU[(size_t)td.format];
216 WGPUTextureFormat view_formats[] = {format};
218 uint32_t mipLevelCount = td.levels;
221 WGPUTextureDescriptor desc = {};
222 desc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding;
223 desc.usage |= WGPUTextureUsage_CopySrc;
226 desc.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding;
229 desc.usage |= WGPUTextureUsage_StorageBinding;
231 if(td.target == ResourceDimensions::Texture1D ||
232 td.target == ResourceDimensions::Texture1DArray){
233 desc.dimension = WGPUTextureDimension_1D;
235 else if(td.target == ResourceDimensions::Texture3D ||
236 td.target == ResourceDimensions::Texture3DArray){
237 desc.dimension = WGPUTextureDimension_3D;
240 desc.dimension = WGPUTextureDimension_2D;
244 bool compressedFormat =
250 if(compressedFormat) {
251 LOG_WARNING_ONCE(logger,
"Generating mips for compressed textures is not supported.");
253 size_t max = glm::max(td.width, td.height);
263 desc.size = {td.width, td.height, td.depth*td.faces*td.layers };
264 desc.format = format;
265 desc.mipLevelCount = mipLevelCount;
266 desc.sampleCount = td.samples;
267 desc.viewFormatCount =
sizeof(view_formats)/
sizeof(view_formats[0]);
268 desc.viewFormats = view_formats;
271 wgpuDeviceValidateTextureDescriptor(device, &desc);
273 texture.texture = wgpuDeviceCreateTexture(device, &desc);
276 texture.view_format = desc.format;
277 texture.format = td.format;
278 if(td.target == ResourceDimensions::Texture1D ||
279 td.target == ResourceDimensions::Texture1DArray){
280 texture.view_dimension = WGPUTextureViewDimension_1D;
282 else if(td.target == ResourceDimensions::Texture2D ||
283 td.target == ResourceDimensions::Texture2DMS){
284 texture.view_dimension = WGPUTextureViewDimension_2D;
286 else if(td.target == ResourceDimensions::Texture2DArray ||
287 td.target == ResourceDimensions::Texture2DMSArray){
288 texture.view_dimension = WGPUTextureViewDimension_2DArray;
290 else if(td.target == ResourceDimensions::Texture3D ||
291 td.target == ResourceDimensions::Texture3DArray){
292 texture.view_dimension = WGPUTextureViewDimension_3D;
294 else if(td.target == ResourceDimensions::TextureCube){
295 texture.view_dimension = WGPUTextureViewDimension_Cube;
297 else if(td.target == ResourceDimensions::TextureCubeArray){
298 texture.view_dimension = WGPUTextureViewDimension_CubeArray;
304 WGPUTextureViewDescriptor view_desc = {};
305 view_desc.label = {
"DefaultTextureView", WGPU_STRLEN};
306 view_desc.format = format;
307 view_desc.dimension = texture.view_dimension;
308 view_desc.baseMipLevel = 0;
309 view_desc.mipLevelCount = mipLevelCount;
310 view_desc.baseArrayLayer = 0;
311 view_desc.arrayLayerCount = td.faces*td.layers;
312 view_desc.aspect = WGPUTextureAspect_All;
313 texture.texture_view = wgpuTextureCreateView(texture.texture, &view_desc);
315 texture.width = td.width;
316 texture.height = td.height;
317 texture.samples = td.samples;
321 uint32_t faces = std::min(td.faces,
static_cast<uint32_t
>(data->faces));
322 uint32_t layers = std::min(td.layers,
static_cast<uint32_t
>(data->layers));
323 uint32_t levels = mipLevelCount;
325 if(generateMips && !compressedFormat) {
327 Cogs::DataFormat formatType = parseDataFormat(textureFormat->
name);
329 switch (formatType) {
330 case(Cogs::DataFormat::R8_UNORM):
331 case(Cogs::DataFormat::R8G8_UNORM):
332 case(Cogs::DataFormat::R8G8B8_UNORM):
333 case(Cogs::DataFormat::R8G8B8A8_UNORM):
335 generateMipmapsInternal<uint8_t>(
351 case(Cogs::DataFormat::R8_SNORM):
352 case(Cogs::DataFormat::R8G8_SNORM):
353 case(Cogs::DataFormat::R8G8B8_SNORM):
354 case(Cogs::DataFormat::R8G8B8A8_SNORM):
356 generateMipmapsInternal<uint8_t>(
372 case(Cogs::DataFormat::R16_UNORM):
373 case(Cogs::DataFormat::R16G16_UNORM):
374 case(Cogs::DataFormat::R16G16B16_UNORM):
375 case(Cogs::DataFormat::R16G16B16A16_UNORM):
377 generateMipmapsInternal<uint16_t>(
393 case(Cogs::DataFormat::R16_SNORM):
394 case(Cogs::DataFormat::R16G16_SNORM):
395 case(Cogs::DataFormat::R16G16B16_SNORM):
396 case(Cogs::DataFormat::R16G16B16A16_SNORM):
398 generateMipmapsInternal<uint16_t>(
414 case(Cogs::DataFormat::R16_FLOAT):
415 case(Cogs::DataFormat::R16G16_FLOAT):
416 case(Cogs::DataFormat::R16G16B16_FLOAT):
417 case(Cogs::DataFormat::R16G16B16A16_FLOAT):
419 generateMipmapsInternal<uint16_t>(
435 case(Cogs::DataFormat::R32_FLOAT):
436 case(Cogs::DataFormat::R32G32_FLOAT):
437 case(Cogs::DataFormat::R32G32B32_FLOAT):
438 case(Cogs::DataFormat::R32G32B32A32_FLOAT):
440 generateMipmapsInternal<float>(
456 case(Cogs::DataFormat::R8G8B8_UNORM_SRGB):
458 generateMipmapsInternal<uint8_t>(
474 case(Cogs::DataFormat::R8G8B8A8_UNORM_SRGB):
476 generateMipmapsInternal<uint8_t>(
497 for (
size_t f = 0; f < faces; ++f) {
498 for (
size_t a = 0; a < layers; ++a) {
499 for (
size_t m = 0; m < mipLevelCount; ++m) {
501 const void* ptr = data->getData(a, f, m);
502 const size_t ptr_size = data->getLevelSize(m);
504 if ((extent.width % data->blockExtent.width) != 0) {
505 extent.width = (extent.width / data->blockExtent.width + 1) * data->blockExtent.width;
507 if ((extent.height % data->blockExtent.height) != 0) {
508 extent.height = (extent.height / data->blockExtent.height + 1) * data->blockExtent.height;
511 WGPUTexelCopyTextureInfo dst = {};
512 dst.texture = texture.texture;
513 dst.mipLevel = (uint32_t)m;
514 dst.origin = { 0, 0, (uint32_t)(a + f) };
515 dst.aspect = WGPUTextureAspect_All;
517 WGPUTexelCopyBufferLayout layout = {};
519 layout.bytesPerRow = (uint32_t)data->getPitch(m);
520 layout.rowsPerImage = extent.height;
521 WGPUExtent3D size = { extent.width, extent.height, extent.depth };
522 wgpuQueueWriteTexture(graphicsDevice->queue, &dst, ptr, ptr_size, &layout, &size);
529 return textures.addResource(std::move(texture));
535 if(!HandleIsValid(handle))
return;
538 wgpuTextureDestroy(texture.texture);
539 wgpuTextureRelease(texture.texture);
541 textures.removeResource(handle);
550 WGPUTextureViewDescriptor desc = {};
551 desc.format = texture.view_format;
552 desc.dimension = texture.view_dimension;
554 switch(desc.dimension){
555 case WGPUTextureViewDimension_2DArray:
556 desc.dimension = WGPUTextureViewDimension_2D;
558 case WGPUTextureViewDimension_CubeArray:
559 desc.dimension = WGPUTextureViewDimension_Cube;
569 desc.aspect = WGPUTextureAspect_All;
570 texture_view.texture_view = wgpuTextureCreateView(texture.texture, &desc);
572 return textureViews.addResource(std::move(texture_view));
577 if(!HandleIsValid(handle))
return;
580 wgpuTextureViewRelease(texture_view.texture_view);
582 textureViews.removeResource(handle);
587 WGPUDevice device = graphicsDevice->device;
591 WGPUSamplerDescriptor desc = {};
596 case SamplerState::FilterMode::MinMagMipPoint:
597 desc.magFilter = WGPUFilterMode_Nearest;
598 desc.minFilter = WGPUFilterMode_Nearest;
599 desc.mipmapFilter = WGPUMipmapFilterMode_Nearest;
600 desc.compare = WGPUCompareFunction_Undefined;
602 case SamplerState::FilterMode::MinMagMipLinear:
603 desc.magFilter = WGPUFilterMode_Linear;
604 desc.minFilter = WGPUFilterMode_Linear;
605 desc.mipmapFilter = WGPUMipmapFilterMode_Linear;
606 desc.compare = WGPUCompareFunction_Undefined;
608 case SamplerState::FilterMode::ComparisonMinMagMipPoint:
609 desc.magFilter = WGPUFilterMode_Nearest;
610 desc.minFilter = WGPUFilterMode_Nearest;
611 desc.mipmapFilter = WGPUMipmapFilterMode_Nearest;
614 case SamplerState::FilterMode::ComparisonMinMagMipLinear:
615 desc.magFilter = WGPUFilterMode_Linear;
616 desc.minFilter = WGPUFilterMode_Linear;
617 desc.mipmapFilter = WGPUMipmapFilterMode_Linear;
624 desc.lodMinClamp = 0;
625 desc.lodMaxClamp = 32;
627 desc.maxAnisotropy = 1;
630 if ((desc.minFilter == WGPUFilterMode_Nearest || desc.magFilter == WGPUFilterMode_Nearest) && desc.maxAnisotropy > 1) {
631 LOG_WARNING(logger,
"Sampler with nearest filter must set maxAnisotropy = 1");
632 desc.maxAnisotropy = 1;
634 sampler.sampler = wgpuDeviceCreateSampler(device, &desc);
636 return samplerStates.addResource(std::move(sampler));
641 if(!HandleIsValid(handle))
return;
644 wgpuSamplerRelease(sampler.sampler);
646 samplerStates.removeResource(handle);
654 void TexturesWebGPU::releaseResources()
Log implementation class.
Provides a weakly referenced view over the contents of a string.
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
Contains all Cogs related functionality.
Encapsulates state for texture sampling in a state object.
ComparisonFunction comparisonFunction
Specifies the comparison function to use when applying a comparison sampler.
unsigned int maxAnisotropy
Specifies the maximum number of anisotropic samples to use when sampling a texture.
AddressMode addressModeW
Specifies the addressing mode along the W axis in texture coordinate space.
ComparisonFunction
Comparison functions applied when sampling depth buffers using comparison filters.
AddressMode addressModeS
Specifies the addressing mode along the S axis in texture coordinate space.
AddressMode
Addressing modes to use when sampling textures.
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
@ Border
Texture color is set to the border color when outside [0, 1] range.
@ Wrap
Texture coordinates automatically wrap around to [0, 1] range.
@ Mirror
Texture coordinates are mirrored when outside [0, 1] range.
FilterMode filter
Specifies the filter to use for texture sampling.
AddressMode addressModeT
Specifies the addressing mode along the T axis in texture coordinate space.
@ DepthBuffer
The texture can be used as a depth target and have depth buffer values written into.
@ RenderTarget
The texture can be used as a render target and drawn into.
@ GenerateMipMaps
The texture supports automatic mipmap generation performed by the graphics device.
@ ReadWriteTexture
The texture can be used as a read/write texture. Can be used to output data from compute shaders.
Describes how to fetch data from a texture in shaders.
uint32_t layerIndex
Index of the first layer (if array) to fetch from.
uint32_t numLayers
Number of array layers available.
uint32_t numLevels
Number of mipmap levels available.
uint32_t levelIndex
First mipmap level to fetch data from.
TextureHandle texture
Texture.