thorvg/src/renderer/wg_engine/tvgWgCommon.cpp

269 lines
No EOL
9.5 KiB
C++
Executable file

/*
* Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#include "tvgWgCommon.h"
#include "tvgArray.h"
void WgContext::initialize(WGPUInstance instance, WGPUDevice device)
{
assert(instance);
assert(device);
// store global instance and surface
this->instance = instance;
this->device = device;
this->preferredFormat = WGPUTextureFormat_BGRA8Unorm;
// get current queue
queue = wgpuDeviceGetQueue(device);
assert(queue);
// create shared webgpu assets
allocateBufferIndexFan(32768);
samplerNearestRepeat = createSampler(WGPUFilterMode_Nearest, WGPUMipmapFilterMode_Nearest, WGPUAddressMode_Repeat);
samplerLinearRepeat = createSampler(WGPUFilterMode_Linear, WGPUMipmapFilterMode_Linear, WGPUAddressMode_Repeat);
samplerLinearMirror = createSampler(WGPUFilterMode_Linear, WGPUMipmapFilterMode_Linear, WGPUAddressMode_MirrorRepeat);
samplerLinearClamp = createSampler(WGPUFilterMode_Linear, WGPUMipmapFilterMode_Linear, WGPUAddressMode_ClampToEdge);
assert(samplerNearestRepeat);
assert(samplerLinearRepeat);
assert(samplerLinearMirror);
assert(samplerLinearClamp);
}
void WgContext::release()
{
releaseSampler(samplerLinearClamp);
releaseSampler(samplerLinearMirror);
releaseSampler(samplerLinearRepeat);
releaseSampler(samplerNearestRepeat);
releaseBuffer(bufferIndexFan);
releaseQueue(queue);
}
WGPUSampler WgContext::createSampler(WGPUFilterMode filter, WGPUMipmapFilterMode mipmapFilter, WGPUAddressMode addrMode)
{
const WGPUSamplerDescriptor samplerDesc {
.addressModeU = addrMode, .addressModeV = addrMode, .addressModeW = addrMode,
.magFilter = filter, .minFilter = filter, .mipmapFilter = mipmapFilter,
.lodMinClamp = 0.0f, .lodMaxClamp = 32.0f, .maxAnisotropy = 1
};
return wgpuDeviceCreateSampler(device, &samplerDesc);
}
bool WgContext::allocateTexture(WGPUTexture& texture, uint32_t width, uint32_t height, WGPUTextureFormat format, void* data)
{
if ((texture) && (wgpuTextureGetWidth(texture) == width) && (wgpuTextureGetHeight(texture) == height)) {
// update texture data
const WGPUImageCopyTexture imageCopyTexture{ .texture = texture };
const WGPUTextureDataLayout textureDataLayout{ .bytesPerRow = 4 * width, .rowsPerImage = height };
const WGPUExtent3D writeSize{ .width = width, .height = height, .depthOrArrayLayers = 1 };
wgpuQueueWriteTexture(queue, &imageCopyTexture, data, 4 * width * height, &textureDataLayout, &writeSize);
wgpuQueueSubmit(queue, 0, nullptr);
} else {
releaseTexture(texture);
texture = createTexture(width, height, format);
// update texture data
const WGPUImageCopyTexture imageCopyTexture{ .texture = texture };
const WGPUTextureDataLayout textureDataLayout{ .bytesPerRow = 4 * width, .rowsPerImage = height };
const WGPUExtent3D writeSize{ .width = width, .height = height, .depthOrArrayLayers = 1 };
wgpuQueueWriteTexture(queue, &imageCopyTexture, data, 4 * width * height, &textureDataLayout, &writeSize);
wgpuQueueSubmit(queue, 0, nullptr);
return true;
}
return false;
}
WGPUTexture WgContext::createTexture(uint32_t width, uint32_t height, WGPUTextureFormat format)
{
const WGPUTextureDescriptor textureDesc {
.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding,
.dimension = WGPUTextureDimension_2D, .size = { width, height, 1 },
.format = format, .mipLevelCount = 1, .sampleCount = 1
};
return wgpuDeviceCreateTexture(device, &textureDesc);
}
WGPUTexture WgContext::createTexStorage(uint32_t width, uint32_t height, WGPUTextureFormat format, uint32_t sc)
{
const WGPUTextureDescriptor textureDesc {
.usage = WGPUTextureUsage_CopySrc | WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding | WGPUTextureUsage_StorageBinding | WGPUTextureUsage_RenderAttachment,
.dimension = WGPUTextureDimension_2D, .size = { width, height, 1 },
.format = format, .mipLevelCount = 1, .sampleCount = sc
};
return wgpuDeviceCreateTexture(device, &textureDesc);
}
WGPUTexture WgContext::createTexStencil(uint32_t width, uint32_t height, WGPUTextureFormat format, uint32_t sc)
{
const WGPUTextureDescriptor textureDesc {
.usage = WGPUTextureUsage_RenderAttachment,
.dimension = WGPUTextureDimension_2D, .size = { width, height, 1 },
.format = format, .mipLevelCount = 1, .sampleCount = sc
};
return wgpuDeviceCreateTexture(device, &textureDesc);
}
WGPUTextureView WgContext::createTextureView(WGPUTexture texture)
{
const WGPUTextureViewDescriptor textureViewDesc {
.format = wgpuTextureGetFormat(texture),
.dimension = WGPUTextureViewDimension_2D,
.baseMipLevel = 0,
.mipLevelCount = 1,
.baseArrayLayer = 0,
.arrayLayerCount = 1,
.aspect = WGPUTextureAspect_All
};
return wgpuTextureCreateView(texture, &textureViewDesc);
}
void WgContext::releaseTextureView(WGPUTextureView& textureView)
{
if (textureView) {
wgpuTextureViewRelease(textureView);
textureView = nullptr;
}
}
void WgContext::releaseTexture(WGPUTexture& texture)
{
if (texture) {
wgpuTextureDestroy(texture);
wgpuTextureRelease(texture);
texture = nullptr;
}
}
void WgContext::releaseSampler(WGPUSampler& sampler)
{
if (sampler) {
wgpuSamplerRelease(sampler);
sampler = nullptr;
}
}
bool WgContext::allocateBufferUniform(WGPUBuffer& buffer, const void* data, uint64_t size)
{
if ((buffer) && (wgpuBufferGetSize(buffer) >= size))
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
else {
releaseBuffer(buffer);
const WGPUBufferDescriptor bufferDesc { .usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform, .size = size };
buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
return true;
}
return false;
}
bool WgContext::allocateBufferVertex(WGPUBuffer& buffer, const float* data, uint64_t size)
{
if ((buffer) && (wgpuBufferGetSize(buffer) >= size))
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
else {
releaseBuffer(buffer);
const WGPUBufferDescriptor bufferDesc {
.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex,
.size = size > WG_VERTEX_BUFFER_MIN_SIZE ? size : WG_VERTEX_BUFFER_MIN_SIZE
};
buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
return true;
}
return false;
}
bool WgContext::allocateBufferIndex(WGPUBuffer& buffer, const uint32_t* data, uint64_t size)
{
if ((buffer) && (wgpuBufferGetSize(buffer) >= size))
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
else {
releaseBuffer(buffer);
const WGPUBufferDescriptor bufferDesc {
.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index,
.size = size > WG_INDEX_BUFFER_MIN_SIZE ? size : WG_INDEX_BUFFER_MIN_SIZE
};
buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
return true;
}
return false;
}
bool WgContext::allocateBufferIndexFan(uint64_t vertexCount)
{
uint64_t indexCount = (vertexCount - 2) * 3;
if ((!bufferIndexFan) || (wgpuBufferGetSize(bufferIndexFan) < indexCount * sizeof(uint32_t))) {
tvg::Array<uint32_t> indexes(indexCount);
for (size_t i = 0; i < vertexCount - 2; i++) {
indexes.push(0);
indexes.push(i + 1);
indexes.push(i + 2);
}
releaseBuffer(bufferIndexFan);
WGPUBufferDescriptor bufferDesc{
.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index,
.size = indexCount * sizeof(uint32_t)
};
bufferIndexFan = wgpuDeviceCreateBuffer(device, &bufferDesc);
wgpuQueueWriteBuffer(queue, bufferIndexFan, 0, &indexes[0], indexCount * sizeof(uint32_t));
return true;
}
return false;
}
void WgContext::releaseBuffer(WGPUBuffer& buffer)
{
if (buffer) {
wgpuBufferDestroy(buffer);
wgpuBufferRelease(buffer);
buffer = nullptr;
}
}
void WgContext::releaseQueue(WGPUQueue queue)
{
if (queue) {
wgpuQueueRelease(queue);
queue = nullptr;
}
}