Cogs.Core
BuffersWebGPU.cpp
1#include "BuffersWebGPU.h"
2
3#include "FormatsWebGPU.h"
4#include "GraphicsDeviceWebGPU.h"
5
6#include "Foundation/Logging/Logger.h"
7
8namespace {
9 Cogs::Logging::Log logger = Cogs::Logging::getLogger("BuffersWebGPU");
10
11 void buffer_error_callback(WGPUPopErrorScopeStatus status, WGPUErrorType type, WGPUStringView message, void* userdata1, void* userdata2)
12 {
13 Cogs::BufferWebGPU *buffer = (Cogs::BufferWebGPU*)userdata1;
14 (void)buffer;
16 (void)ptr;
17 if(status == WGPUPopErrorScopeStatus_CallbackCancelled){
18 LOG_ERROR(logger, "WebGPU buffer err status: Cancelled");
19 }
20 if(status == WGPUPopErrorScopeStatus_Error){
21 LOG_ERROR(logger, "WebGPU buffer err status: Error");
22 }
23 if(message.data && message.length){
24 LOG_ERROR(logger, "WebGPU buffer err (%d) %.*s", type, WGPUStringViewFormat(message));
25 }
26 }
27}
28
29namespace Cogs{
30
31 void BufferWebGPU::CreateInstance(GraphicsDeviceWebGPU *graphicsDevice, BuffersWebGPU & /*buffers*/)
32 {
33 ResourceCountersWebGPU &counters = graphicsDevice->counters;
34 WGPUBufferDescriptor desc = WGPU_BUFFER_DESCRIPTOR_INIT;
35 desc.label.length = WGPU_STRLEN;
36 if(label.length()){
37 desc.label.data = label.data();
38 desc.label.length = label.length();
39 }
40 if(accessMode&AccessMode::Read){
41 desc.usage |= WGPUBufferUsage_MapRead;
42 desc.usage |= WGPUBufferUsage_CopyDst;
43 }
44 else{
45 desc.usage |= WGPUBufferUsage_CopyDst;
46 }
47
48 if((bindFlags&BindFlags::VertexBuffer) != 0)
49 desc.usage |= WGPUBufferUsage_Vertex;
50 if((bindFlags&BindFlags::IndexBuffer) != 0)
51 desc.usage |= WGPUBufferUsage_Index;
52 if((bindFlags&BindFlags::ConstantBuffer) != 0)
53 desc.usage |= WGPUBufferUsage_Uniform;
54 if((bindFlags&BindFlags::ShaderResource) != 0)
55 desc.usage |= WGPUBufferUsage_Storage;
56 if((bindFlags&BindFlags::RawBuffer) != 0)
57 desc.usage |= WGPUBufferUsage_Storage;
58 if((bindFlags&BindFlags::StructuredBuffer) != 0)
59 desc.usage |= WGPUBufferUsage_Storage;
61 desc.usage |= WGPUBufferUsage_Storage;
62
63 desc.size = size;
64 desc.mappedAtCreation = false;
65
66 if(graphicsDevice->use_error_scope){
67 wgpuDevicePushErrorScope(graphicsDevice->device, graphicsDevice->filter);
68 }
69
70 buffer = wgpuDeviceCreateBuffer(graphicsDevice->device, &desc);
71 counters.buffer++;
72 if(!is_read_buffer){
73 alias.push_back(buffer);
74 }
75
76 if(graphicsDevice->use_error_scope){
77 WGPUPopErrorScopeCallbackInfo info = WGPU_POP_ERROR_SCOPE_CALLBACK_INFO_INIT;
78 info.mode = WGPUCallbackMode_AllowSpontaneous;
79 info.callback = buffer_error_callback;
80 info.userdata1 = this;
81 info.userdata2 = graphicsDevice;
82 WGPUFuture future = wgpuDevicePopErrorScope(graphicsDevice->device, info);
83 // WGPUWaitStatus wgpuInstanceWaitAny(WGPUInstance instance, size_t futureCount, WGPUFutureWaitInfo * futures, uint64_t timeoutNS);
84 }
85 }
86 void BufferWebGPU::NextInstance(GraphicsDeviceWebGPU *graphicsDevice, BuffersWebGPU &buffers)
87 {
88 uint32_t idx = alias_idx;
89 alias_idx++;
90 assert(idx < 1024);
91 if(idx >= alias.size()){
92 CreateInstance(graphicsDevice, buffers);
93 assert(idx+1 == alias.size());
94 }
95 else{
96 buffer = alias[idx];
97 }
98 }
99 void BufferWebGPU::ResetInstance()
100 {
101 // TODO: this is an oppertunity to free unused aliases and save memory
102 alias_idx = 0;
103 }
104
105 void BuffersWebGPU::initialize(GraphicsDeviceWebGPU *device)
106 {
107 graphicsDevice = device;
108 }
109 void BuffersWebGPU::resetInstances()
110 {
111 for(BufferWebGPU &buffer : buffers){
112 buffer.ResetInstance();
113 }
114 }
115
117 {
118 BufferWebGPU &buffer = buffers[handle];
119 buffer.label = std::string(name.data(), name.length());
120
121 WGPUStringView label;
122 label.data = buffer.label.data();
123 label.length = buffer.label.length();
124
125 if(buffer.is_read_buffer){
126 wgpuBufferSetLabel(buffer.buffer, label);
127 }
128 else{
129 for(WGPUBuffer &alias: buffer.alias){
130 wgpuBufferSetLabel(alias, label);
131 }
132 }
133 }
135 {
136 annotate((BufferHandle)handle, name);
137 }
138
139 VertexBufferHandle BuffersWebGPU::loadVertexBuffer(const void* vertexData, const size_t count, const VertexFormat& vertexFormat)
140 {
141 const uint32_t stride = getSize(vertexFormat);
142 const size_t size = count * stride;
143 auto handle = loadBuffer(vertexData, size, Usage::Dynamic, AccessMode::None, BindFlags::VertexBuffer);
144 return handle;
145 }
146
147 VertexBufferHandle BuffersWebGPU::loadVertexBuffer(const void* vertexData, const size_t count, VertexFormatHandle vertexFormatHandle)
148 {
149 return loadVertexBuffer(vertexData, count, *VertexFormats::getVertexFormat(vertexFormatHandle));
150 }
151
153 {
154 buffers.removeResource(handle);
155 }
156
157 IndexBufferHandle BuffersWebGPU::loadIndexBuffer(const void* indexData, const size_t count, const size_t indexSize)
158 {
159 const size_t size = count * indexSize;
160 auto handle = loadBuffer(indexData, size, Usage::Dynamic, AccessMode::None, BindFlags::IndexBuffer);
161 return handle;
162 }
163
165 {
166 buffers.removeResource(handle);
167 }
168
169 InputLayoutHandle BuffersWebGPU::loadInputLayout(const VertexFormatHandle* vertexFormats, const size_t count, EffectHandle effectHandle)
170 {
171 size_t attribute_count = 0;
172 for(size_t i=0; i<count; i++){
173 const VertexFormat *vtx_format = getVertexFormat(vertexFormats[i]);
174 for(size_t j=0; j<vtx_format->elements.size(); j++){
175 const VertexElement &element = vtx_format->elements[j];
176 if(element.format == DataFormat::MAT4X4_FLOAT){
177 attribute_count += 4;
178 }
179 else{
180 attribute_count += 1;
181 }
182 }
183 }
184
185 InputLayoutWebGPU input_layout = {};
186 input_layout.vertex_attributes.resize(attribute_count);
187 input_layout.vertex_buffer_layout.resize(count);
188
189 size_t attribute_offset = 0;
190 for(size_t i=0; i<count; i++){
191 const VertexFormat *vtx_format = getVertexFormat(vertexFormats[i]);
192 WGPUVertexBufferLayout &vtx_layout = input_layout.vertex_buffer_layout[i];
193 vtx_layout.arrayStride = getSize(*vtx_format);
194 if(vtx_format->elements[0].inputType == InputType::VertexData){
195 vtx_layout.stepMode = WGPUVertexStepMode_Vertex;
196 }
197 else if(vtx_format->elements[0].inputType == InputType::InstanceData){
198 vtx_layout.stepMode = WGPUVertexStepMode_Instance;
199 }
200 else{
201 // vtx_layout.stepMode = WGPUVertexStepMode_VertexBufferNotUsed;
202 assert(false);
203 }
204 vtx_layout.attributeCount = 0;
205 vtx_layout.attributes = vtx_format->elements.size() ? &input_layout.vertex_attributes[attribute_offset] : nullptr;
206 for(size_t j=0; j<vtx_format->elements.size(); j++){
207 const VertexElement &element = vtx_format->elements[j];
208 if(vtx_layout.stepMode == WGPUVertexStepMode_Vertex){
209 assert(element.inputType == InputType::VertexData);
210 assert(element.instanceStep == 0);
211 }
212 else if(vtx_layout.stepMode == WGPUVertexStepMode_Instance){
213 assert(element.inputType == InputType::InstanceData);
214 assert(element.instanceStep == 1);
215 }
216 else{
217 assert(false);
218 }
219 EffectWebGPU& effect = graphicsDevice->effects.effects[effectHandle];
220
221 uint32_t attribute_location = std::numeric_limits<uint32_t>::max();
222 SemanticSlotBinding semantic = {};
223 semantic.format = static_cast<WGPUVertexFormat>(0);
224 for (size_t s = 0; s < effect.num_attribs; s++) {
225 const SemanticSlotBinding& sem = effect.semanticSlotBindings[s];
226 if (sem.semantic == size_t(element.semantic) && sem.slot == element.semanticIndex) {
227 attribute_location = sem.binding;
228 semantic = sem;
229 break;
230 }
231 }
232 if (attribute_location == std::numeric_limits<uint32_t>::max()) {
233 LOG_WARNING(logger, "Vertex attribute semantic %d, slot %d not used in shader", (int)element.semantic, element.semanticIndex);
234 }
235 if(element.format == DataFormat::MAT4X4_FLOAT){
236 for(int k=0; k<4; k++){
237 WGPUVertexAttribute &att = input_layout.vertex_attributes[attribute_offset++];
238 att.format = WGPUVertexFormat_Float32x4;
239 att.offset = element.offset + sizeof(float)*4*k;
240 att.shaderLocation = attribute_location+k;
241 vtx_layout.attributeCount++;
242 }
243 }
244 else{
245 DataFormat format = element.format;
246 // WebGPU does not support RGB vertex formats for 8 and 16 byte types.
247 // Upgrade these to RGBA formats and hope that the buffer stride is wide enough.
248 if(format == DataFormat::R8G8B8_UNORM){
249 format = DataFormat::R8G8B8A8_UNORM;
250 LOG_WARNING(logger, "Vertex attribute format R8G8B8_UNORM not supported by WebGPU (Using R8G8B8A8_UNORM).");
251 }
252 else if(format == DataFormat::R8G8B8_SNORM){
253 format = DataFormat::R8G8B8A8_SNORM;
254 LOG_WARNING(logger, "Vertex attribute format R8G8B8_SNORM not supported by WebGPU (Using R8G8B8A8_SNORM).");
255 }
256 else if(format == DataFormat::R8G8B8_UINT){
257 format = DataFormat::R8G8B8A8_UINT;
258 LOG_WARNING(logger, "Vertex attribute format R8G8B8_UINT not supported by WebGPU (Using R8G8B8A8_UINT).");
259 }
260 else if(format == DataFormat::R8G8B8_SINT){
261 format = DataFormat::R8G8B8A8_SINT;
262 LOG_WARNING(logger, "Vertex attribute format R8G8B8_SINT not supported by WebGPU (Using R8G8B8A8_SINT).");
263 }
264 else if(format == DataFormat::R16G16B16_UNORM){
265 format = DataFormat::R16G16B16A16_UNORM;
266 LOG_WARNING(logger, "Vertex attribute format R16G16B16_UNORM not supported by WebGPU (Using R16G16B16A16_UNORM).");
267 }
268 else if(format == DataFormat::R16G16B16_SNORM){
269 format = DataFormat::R16G16B16A16_SNORM;
270 LOG_WARNING(logger, "Vertex attribute format R16G16B16_SNORM not supported by WebGPU (Using R16G16B16A16_SNORM).");
271 }
272 else if(format == DataFormat::R16G16B16_UINT){
273 format = DataFormat::R16G16B16A16_UINT;
274 LOG_WARNING(logger, "Vertex attribute format R16G16B16_UINT not supported by WebGPU (Using R16G16B16A16_UINT).");
275 }
276 else if(format == DataFormat::R16G16B16_SINT){
277 format = DataFormat::R16G16B16A16_SINT;
278 LOG_WARNING(logger, "Vertex attribute format R16G16B16_SINT not supported by WebGPU (Using R16G16B16A16_SINT).");
279 }
280 else if(format == DataFormat::R16G16B16_FLOAT){
281 format = DataFormat::R16G16B16A16_FLOAT;
282 LOG_WARNING(logger, "Vertex attribute format R16G16B16_FLOAT not supported by WebGPU (Using R16G16B16A16_FLOAT).");
283 }
284 WGPUVertexAttribute &att = input_layout.vertex_attributes[attribute_offset++];
285 att.format = VertexFormatsWebGPU[(size_t)format];
286 assert(att.format != static_cast<WGPUVertexFormat>(0)); // Vertex format not supported
287 att.offset = element.offset;
288 att.shaderLocation = attribute_location;
289 vtx_layout.attributeCount++;
290 }
291 }
292 }
293 assert(attribute_offset == input_layout.vertex_attributes.size());
294 return inputLayouts.addResource(std::move(input_layout));
295 }
296
298 {
299 inputLayouts.removeResource(inputLayoutHandle);
300 }
301
302 BufferHandle BuffersWebGPU::loadBuffer(const void* data, const size_t size, Usage::EUsage /*usage*/, uint32_t accessMode, uint32_t bindFlags, uint32_t /*stride*/)
303 {
304 assert((bindFlags&BindFlags::StreamOutBuffer) == 0);
305 assert((accessMode&AccessMode::ReadWrite) != AccessMode::ReadWrite); // Not available for WebGPU
306
307 BufferWebGPU buffer = {};
308 buffer.size = size;
309 buffer.accessMode = accessMode;
310 buffer.bindFlags = bindFlags;
311
312 if(accessMode&AccessMode::Read){
313 buffer.is_read_buffer = true;
314 assert(bindFlags == 0);
315 // Round up
316 size_t granularity = 256;
317 if(buffer.size%granularity != 0)
318 buffer.size += granularity-buffer.size%granularity;
319 }
320 else{
321 // Round up
322 size_t granularity = 16;
323 if (buffer.size % granularity != 0)
324 buffer.size += granularity - buffer.size % granularity;
325 }
326
327 buffer.CreateInstance(graphicsDevice, *this);
328 if(data){
329 wgpuQueueWriteBuffer(graphicsDevice->queue, buffer.buffer, 0, data, buffer.size);
330 }
331
332 const size_t roundedSize = buffer.size;
333 BufferHandle handle = this->buffers.addResource(std::move(buffer));
334 recordBufferCreate(handle, static_cast<BindFlags::EBindFlags>(bindFlags), roundedSize);
335 return handle;
336 }
337
339 {
340 if (!HandleIsValid(handle)) return;
341
342 ResourceCountersWebGPU &counters = graphicsDevice->counters;
343 BufferWebGPU &buffer = buffers[handle];
344 recordBufferRelease(handle, static_cast<BindFlags::EBindFlags>(buffer.bindFlags), buffer.size);
345 if(buffer.is_read_buffer){
346 wgpuBufferDestroy(buffer.buffer);
347 wgpuBufferRelease(buffer.buffer);
348 counters.buffer--;
349 }
350 else{
351 for(WGPUBuffer &alias: buffer.alias){
352 wgpuBufferDestroy(alias);
353 wgpuBufferRelease(alias);
354 counters.buffer--;
355 }
356 buffer.alias.clear();
357 }
358 buffers.removeResource(handle);
359 }
360
362 {
363 {
364 std::vector<BufferHandle> handles;
365 for (auto& resource : buffers) {
366 handles.push_back(buffers.getHandle(resource));
367 }
368 for (auto& handle : handles) {
369 releaseBuffer(handle);
370 }
371 }
372 {
373 std::vector<InputLayoutHandle> handles;
374 for (auto& resource : inputLayouts) {
375 handles.push_back(inputLayouts.getHandle(resource));
376 }
377 for (auto& handle : handles) {
378 releaseInputLayout(handle);
379 }
380 }
381 }
382
384 {
385 return static_cast<void*>(buffers[handle].buffer);
386 }
387
388}
virtual IndexBufferHandle loadIndexBuffer(const void *, const size_t, const size_t) override
Loads a new index buffer and populates it with the given indexData.
virtual void annotate(BufferHandle handle, const StringView &name) override
Associate a name with an object for use in graphics debugging.
virtual VertexBufferHandle loadVertexBuffer(const void *, const size_t, const VertexFormat &vertexFormat) override
Loads a new vertex buffer and populates it with the given data.
virtual void releaseVertexBuffer(VertexBufferHandle vertexBufferHandle) override
Release the vertex buffer with the given handle.
virtual InputLayoutHandle loadInputLayout(const VertexFormatHandle *vertexFormats, const size_t count, EffectHandle effectHandle) override
Loads a new input layout to map vertex flow between vertex buffers with the given vertexFormats to ef...
virtual BufferHandle loadBuffer(const void *, const size_t, Usage::EUsage, uint32_t, uint32_t, uint32_t=0) override
Loads a new buffer using the given data to populate the buffer.
virtual void * getNativeHandle(BufferHandle bufferHandle) override
Get the device-specific handle (D3D buffer pointer, OpenGL buffer ID etc) associated with the given b...
virtual void releaseResources() override
Releases all allocated buffer resources.
virtual void releaseBuffer(BufferHandle bufferHandle) override
Releases the buffer with the given bufferHandle.
virtual void releaseInputLayout(InputLayoutHandle inputLayoutHandle) override
Releases the input layout with the given inputLayoutHandle.
virtual void releaseIndexBuffer(IndexBufferHandle indexBufferHandle) override
Releases the index buffer with the given handle.
Log implementation class.
Definition: LogManager.h:140
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:50
constexpr const char * data() const noexcept
Get the sequence of characters referenced by the string view.
Definition: StringView.h:197
constexpr size_t length() const noexcept
Get the length of the string.
Definition: StringView.h:211
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
@ InstanceData
Per instance data.
@ VertexData
Per vertex data.
@ Read
The buffer can be mapped and read from by the CPU after creation.
Definition: Flags.h:48
@ None
The buffer can not be either read from or written to by the CPU after creation.
Definition: Flags.h:46
EBindFlags
Bind flags enumeration.
Definition: Flags.h:64
@ ConstantBuffer
The buffer can be bound as input to effects as a constant buffer.
Definition: Flags.h:72
@ StructuredBufferWithCounter
The buffer can be bound as a structured buffer and read or written from shaders, with an additional a...
Definition: Flags.h:82
@ StreamOutBuffer
The buffer can be bound as stream output to receive transform feedback results.
Definition: Flags.h:74
@ VertexBuffer
The buffer can be bound as input to the vertex shader stage as a vertex buffer.
Definition: Flags.h:68
@ RawBuffer
The buffer can be bound as a byte address buffer and read or written from shaders.
Definition: Flags.h:78
@ IndexBuffer
The buffer can be bound as input to the vertex shader stage as an index buffer.
Definition: Flags.h:70
@ ShaderResource
The buffer can be bound as a shader resource and read from shaders.
Definition: Flags.h:76
@ StructuredBuffer
The buffer can be bound as a structured buffer and read or written from shaders.
Definition: Flags.h:80
void recordBufferRelease(BufferHandle handle, BindFlags::EBindFlags bindFlags, size_t bytes)
void recordBufferCreate(BufferHandle handle, BindFlags::EBindFlags bindFlags, size_t bytes)
EUsage
Usage enumeration.
Definition: Flags.h:24
@ Dynamic
Buffer will be loaded and modified with some frequency.
Definition: Flags.h:30
Vertex element structure used to describe a single data element in a vertex for the input assembler.
Definition: VertexFormat.h:38
InputType inputType
Input type of the element, vertex or instance data.
Definition: VertexFormat.h:43
DataFormat format
Format of the element.
Definition: VertexFormat.h:40
uint16_t offset
Offset in bytes from the vertex position in memory.
Definition: VertexFormat.h:39
uint16_t semanticIndex
Index for the semantic mapping.
Definition: VertexFormat.h:42
ElementSemantic semantic
Semantic mapping of the element (position, normal, etc...).
Definition: VertexFormat.h:41
uint16_t instanceStep
Instance step factor.
Definition: VertexFormat.h:44
Vertex format structure used to describe a single vertex for the input assembler.
Definition: VertexFormat.h:60
std::vector< VertexElement > elements
Vector containing all vertex elements of this format.
Definition: VertexFormat.h:62