You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

415 lines
13 KiB

/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <emscripten/html5_webgpu.h>
#include <utility>
#include "WGPUDef.h"
#include "base/Utils.h"
#include "base/std/container/map.h"
#include "base/std/container/set.h"
#include "base/std/container/unordered_map.h"
#include "base/std/container/vector.h"
#include "base/threading/Semaphore.h"
#include "gfx-base/GFXDef.h"
class WGPURenderPassDescriptor;
class WGPUComputePassDescriptor;
namespace cc {
namespace gfx {
constexpr uint8_t CC_WGPU_MAX_ATTACHMENTS = 16;
constexpr decltype(nullptr) wgpuDefaultHandle = nullptr;
constexpr ccstd::hash_t WGPU_HASH_SEED = 0x811C9DC5;
constexpr uint8_t CC_WGPU_MAX_FRAME_COUNT = 3;
const DescriptorType COMBINED_ST_IN_USE = DescriptorType::SAMPLER_TEXTURE | DescriptorType::INPUT_ATTACHMENT;
class CCWGPUTexture;
class CCWGPUBuffer;
class CCWGPUSampler;
class CCWGPUQueue;
class CCWGPUPipelineState;
class CCWGPUDescriptorSet;
class CCWGPUInputAssembler;
struct CCWGPUResource {
CCWGPUBuffer *uniformBuffer = nullptr;
CCWGPUBuffer *storageBuffer = nullptr;
CCWGPUTexture *commonTexture = nullptr;
CCWGPUTexture *storageTexture = nullptr;
CCWGPUSampler *filterableSampler = nullptr;
CCWGPUSampler *unfilterableSampler = nullptr;
};
struct CCWGPUInstanceObject {
WGPUInstance wgpuInstance = wgpuDefaultHandle;
WGPUSurface wgpuSurface = wgpuDefaultHandle;
WGPUAdapter wgpuAdapter = wgpuDefaultHandle;
};
struct CCWGPUDeviceObject {
WGPUDevice wgpuDevice = wgpuDefaultHandle;
WGPUQueue wgpuQueue = wgpuDefaultHandle;
CCWGPUInstanceObject instance;
CCWGPUResource defaultResources;
};
struct CCWGPUSwapchainObject {
WGPUSwapChain wgpuSwapChain = wgpuDefaultHandle;
WGPUSurface wgpuSurface = wgpuDefaultHandle;
CCWGPUTexture *swapchainColor = nullptr;
CCWGPUTexture *swapchainDepthStencil = nullptr;
};
struct CCWGPURenderPassObject {
RenderPassInfo info;
ccstd::string label;
uint8_t sampleCount = 1;
WGPURenderPassDescriptor *wgpuRenderPassDesc = wgpuDefaultHandle;
};
struct CCWGPUTextureObject {
WGPUTexture wgpuTexture = wgpuDefaultHandle;
WGPUTextureView wgpuTextureView = wgpuDefaultHandle;
WGPUTextureView selfView = wgpuDefaultHandle;
std::vector<WGPUTextureView> planeViews;
};
// The indirect drawIndexed parameters encoded in the buffer must be a tightly packed block
// of five 32-bit unsigned integer values (20 bytes total), given in the same order as the arguments for drawIndexed().
// let drawIndexedIndirectParameters = new Uint32Array(5);
// drawIndexedIndirectParameters[0] = indexCount;
// drawIndexedIndirectParameters[1] = instanceCount;
// drawIndexedIndirectParameters[2] = firstIndex;
// drawIndexedIndirectParameters[3] = baseVertex;
// drawIndexedIndirectParameters[4] = 0; // firstInstance. Must be 0.
struct CCWGPUDrawIndexedIndirectObject {
uint32_t indexCount = 0;
uint32_t instanceCount = 0;
uint32_t firstIndex = 0;
uint32_t baseVertex = 0;
uint32_t firstInstance = 0;
};
static_assert(sizeof(CCWGPUDrawIndexedIndirectObject) == 20, "WGPU drawIndexedIndirect structure validation failed!");
// The indirect draw parameters encoded in the buffer must be a tightly packed block
// of four 32-bit unsigned integer values (16 bytes total), given in the same order as the arguments for draw().
// let drawIndirectParameters = new Uint32Array(4);
// drawIndirectParameters[0] = vertexCount;
// drawIndirectParameters[1] = instanceCount;
// drawIndirectParameters[2] = firstVertex;
// drawIndirectParameters[3] = 0; // firstInstance. Must be 0.
struct CCWGPUDrawIndirectObject {
uint32_t vertexCount = 0;
uint32_t instanceCount = 0;
uint32_t firstIndex = 0;
uint32_t firstInstance = 0;
};
static_assert(sizeof(CCWGPUDrawIndirectObject) == 16, "WGPU drawIndirect structure validation failed!");
struct CCWGPUBufferObject {
WGPUBuffer wgpuBuffer = wgpuDefaultHandle;
ccstd::vector<CCWGPUDrawIndexedIndirectObject> indexedIndirectObjs;
ccstd::vector<CCWGPUDrawIndirectObject> indirectObjs;
bool mapped = false;
bool hasDynamicOffsets = false;
};
struct CCWGPUSamplerObject {
WGPUSampler wgpuSampler = wgpuDefaultHandle;
WGPUAddressMode addressModeU = WGPUAddressMode_Repeat;
WGPUAddressMode addressModeV = WGPUAddressMode_Repeat;
WGPUAddressMode addressModeW = WGPUAddressMode_Repeat;
WGPUFilterMode magFilter = WGPUFilterMode_Linear;
WGPUFilterMode minFilter = WGPUFilterMode_Linear;
WGPUFilterMode mipmapFilter = WGPUFilterMode_Linear;
float lodMinClamp = 0.1f;
float lodMaxClamp = 1000.0f;
WGPUCompareFunction compare = WGPUCompareFunction_Always;
uint16_t maxAnisotropy = 0;
};
struct CCWGPUBindGroupLayoutObject {
WGPUBindGroupLayout bindGroupLayout = wgpuDefaultHandle;
ccstd::map<uint32_t, WGPUBindGroupLayoutEntry> bindGroupLayoutEntries;
};
struct CCWGPUBindGroupObject {
WGPUBindGroup bindgroup = wgpuDefaultHandle;
ccstd::vector<WGPUBindGroupEntry> bindGroupEntries;
ccstd::set<uint8_t> bindingSet; // bindingInDesc
ccstd::set<uint8_t> bindingInShader; // bindingInShader
};
struct CCWGPUPipelineLayoutObject {
WGPUPipelineLayout wgpuPipelineLayout = wgpuDefaultHandle;
};
struct CCWGPUPipelineStateObject {
WGPURenderPipeline wgpuRenderPipeline = wgpuDefaultHandle;
WGPUComputePipeline wgpuComputePipeline = wgpuDefaultHandle;
ccstd::vector<WGPUVertexAttribute> redundantAttr;
uint32_t maxAttrLength = 0;
};
using BindingList = ccstd::vector<uint8_t>;
struct CCWGPUShaderObject {
ccstd::string name;
WGPUShaderModule wgpuShaderVertexModule = wgpuDefaultHandle;
WGPUShaderModule wgpuShaderFragmentModule = wgpuDefaultHandle;
WGPUShaderModule wgpuShaderComputeModule = wgpuDefaultHandle;
ccstd::vector<BindingList> bindings;
};
struct CCWGPUInputAssemblerObject {
WGPUVertexState wgpuVertexState;
};
struct CCWGPUQueueObject {
WGPUQueue wgpuQueue = wgpuDefaultHandle;
QueueType type = QueueType::GRAPHICS;
};
struct CCWGPUDescriptorSetObject {
CCWGPUDescriptorSet *descriptorSet = nullptr;
uint32_t dynamicOffsetCount = 0;
const uint32_t *dynamicOffsets = nullptr;
};
struct CCWGPUStencilMasks {
uint32_t writeMask = 0;
uint32_t compareRef = 0;
uint32_t compareMask = 0;
};
struct CCWGPUStateCache {
CCWGPUPipelineState *pipelineState = nullptr;
CCWGPUInputAssembler *inputAssembler = nullptr;
float depthBiasConstant = 0.0f;
float depthBiasClamp = 0.0f;
float depthBiasSlope = 0.0f;
float depthMinBound = 0.0f;
float depthMaxBound = 100.0f;
Color blendConstants;
Viewport viewport;
Rect rect;
uint32_t minAttachmentWidth = 0;
uint32_t minAttachmentHeight = 0;
ccstd::vector<CCWGPUDescriptorSetObject> descriptorSets;
ccstd::map<StencilFace, CCWGPUStencilMasks> stencilMasks;
};
struct CCWGPUCommandBufferObject {
bool renderPassBegan = false;
WGPUCommandBuffer wgpuCommandBuffer = wgpuDefaultHandle;
WGPUCommandEncoder wgpuCommandEncoder = wgpuDefaultHandle;
WGPURenderPassEncoder wgpuRenderPassEncoder = wgpuDefaultHandle;
WGPUComputePassEncoder wgpuComputeEncoder = wgpuDefaultHandle;
CommandBufferType type = CommandBufferType::PRIMARY;
CCWGPUQueue *queue = nullptr;
WGPURenderPassDescriptor renderPassDescriptor;
CCWGPUStateCache stateCache;
ccstd::vector<WGPUCommandBuffer> computeCmdBuffs;
ccstd::unordered_map<uint32_t, CCWGPUBuffer *> redundantVertexBufferMap;
};
struct CCWGPUQueryPoolObject {
QueryType type = QueryType::OCCLUSION;
uint32_t maxQueryObjects = 0;
ccstd::vector<uint32_t> idPool;
};
template <typename T>
class RecycleBin {
public:
RecycleBin() = default;
~RecycleBin() = default;
RecycleBin(const RecycleBin &) = delete;
RecycleBin &operator=(const RecycleBin &) = delete;
void collect(T t) {
_recycleBin.emplace(t);
}
void purge() {
for (auto &t : _recycleBin) {
purge(t);
}
_recycleBin.clear();
}
private:
void purge(T t);
ccstd::set<T> _recycleBin;
};
template <>
inline void RecycleBin<WGPUBuffer>::purge(WGPUBuffer buffer) {
wgpuBufferDestroy(buffer);
wgpuBufferRelease(buffer);
}
template <>
inline void RecycleBin<WGPUQuerySet>::purge(WGPUQuerySet querySet) {
wgpuQuerySetDestroy(querySet);
wgpuQuerySetRelease(querySet);
}
template <>
inline void RecycleBin<WGPUTexture>::purge(WGPUTexture texture) {
wgpuTextureDestroy(texture);
wgpuTextureRelease(texture);
}
struct CCWGPURecycleBin {
RecycleBin<WGPUBuffer> bufferBin;
RecycleBin<WGPUTexture> textureBin;
RecycleBin<WGPUQuerySet> queryBin;
};
namespace {
inline void onStagingBufferMapDone(WGPUBufferMapAsyncStatus status, void *userdata) {
if (status == WGPUBufferMapAsyncStatus_Success) {
auto *semaphore = static_cast<Semaphore *>(userdata);
semaphore->signal();
}
}
} // namespace
constexpr uint32_t INIT_BUFFER_SIZE = 1024 * 1024 * 1; // 1MB
class CCWGPUStagingBuffer {
CCWGPUStagingBuffer(const CCWGPUStagingBuffer &) = delete;
CCWGPUStagingBuffer &operator=(const CCWGPUStagingBuffer &) = delete;
CCWGPUStagingBuffer() = default;
~CCWGPUStagingBuffer() = default;
public:
explicit CCWGPUStagingBuffer(WGPUDevice device, const std::function<void(WGPUBuffer)> &recycleFunc)
: _device(device), _size(INIT_BUFFER_SIZE), _recycleFunc(recycleFunc) {
WGPUBufferDescriptor desc = {
.label = "staging buffer",
.usage = WGPUBufferUsage_CopySrc | WGPUBufferUsage_MapWrite,
.size = _size,
.mappedAtCreation = true,
};
_buffer = wgpuDeviceCreateBuffer(device, &desc);
_mappedData = wgpuBufferGetMappedRange(_buffer, 0, _size);
}
uint32_t alloc(uint32_t size) {
// 1. what if it's still unmapped
if (!_mapped) {
Semaphore sem{0};
wgpuBufferMapAsync(_buffer, WGPUMapMode_Write, 0, _size, onStagingBufferMapDone, &sem);
sem.wait();
_mappedData = wgpuBufferGetMappedRange(_buffer, 0, _size);
_mapped = true;
}
const auto oldOffset = _offset;
if (_offset + size > _size) {
const auto oldBuffer = _buffer;
const auto oldSize = _size;
const auto *oldMappedData = _mappedData;
_size = utils::nextPOT(_offset + size);
WGPUBufferDescriptor desc = {
.label = "staging buffer",
.usage = WGPUBufferUsage_CopySrc | WGPUBufferUsage_MapWrite,
.size = _size,
.mappedAtCreation = true,
};
_buffer = wgpuDeviceCreateBuffer(_device, &desc);
_mappedData = wgpuBufferGetMappedRange(_buffer, 0, _size);
_offset = 0;
if (_mapped) {
memcpy(_mappedData, oldMappedData, oldSize);
_offset = oldOffset;
} else {
// do nothing, unmap means this buffer has flush its data to gpu
}
_recycleFunc(oldBuffer);
}
_offset += size;
return oldOffset;
}
void destroy() {
if (_mapped) {
wgpuBufferUnmap(_buffer);
}
if (_recycleFunc) {
_recycleFunc(_buffer);
}
}
void unmap() {
if (_mapped) {
wgpuBufferUnmap(_buffer);
_mapped = false;
}
_offset = 0;
}
void *getMappedData(uint32_t offset) {
return static_cast<uint8_t *>(_mappedData) + offset;
}
WGPUBuffer getBuffer() {
return _buffer;
}
private:
WGPUDevice _device{wgpuDefaultHandle};
WGPUBuffer _buffer{wgpuDefaultHandle};
void *_mappedData{nullptr};
uint32_t _size{0};
uint32_t _offset{0};
std::function<void(WGPUBuffer)> _recycleFunc;
bool _mapped{false};
};
} // namespace gfx
} // namespace cc