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 buffers.bufferMemoryConsumption += size;
73 if(!is_read_buffer){
74 alias.push_back(buffer);
75 }
76
77 if(graphicsDevice->use_error_scope){
78 WGPUPopErrorScopeCallbackInfo info = WGPU_POP_ERROR_SCOPE_CALLBACK_INFO_INIT;
79 info.mode = WGPUCallbackMode_AllowSpontaneous;
80 info.callback = buffer_error_callback;
81 info.userdata1 = this;
82 info.userdata2 = graphicsDevice;
83 WGPUFuture future = wgpuDevicePopErrorScope(graphicsDevice->device, info);
84 // WGPUWaitStatus wgpuInstanceWaitAny(WGPUInstance instance, size_t futureCount, WGPUFutureWaitInfo * futures, uint64_t timeoutNS);
85 }
86 }
87 void BufferWebGPU::NextInstance(GraphicsDeviceWebGPU *graphicsDevice, BuffersWebGPU &buffers)
88 {
89 uint32_t idx = alias_idx;
90 alias_idx++;
91 assert(idx < 1024);
92 if(idx >= alias.size()){
93 CreateInstance(graphicsDevice, buffers);
94 assert(idx+1 == alias.size());
95 }
96 else{
97 buffer = alias[idx];
98 }
99 }
100 void BufferWebGPU::ResetInstance()
101 {
102 // TODO: this is an oppertunity to free unused aliases and save memory
103 alias_idx = 0;
104 }
105
106 void BuffersWebGPU::initialize(GraphicsDeviceWebGPU *device)
107 {
108 graphicsDevice = device;
109 }
110 void BuffersWebGPU::resetInstances()
111 {
112 for(BufferWebGPU &buffer : buffers){
113 buffer.ResetInstance();
114 }
115 }
116
118 {
119 BufferWebGPU &buffer = buffers[handle];
120 buffer.label = std::string(name.data(), name.length());
121
122 WGPUStringView label;
123 label.data = buffer.label.data();
124 label.length = buffer.label.length();
125
126 if(!buffer.is_read_buffer){
127 wgpuBufferSetLabel(buffer.buffer, label);
128 }
129 else{
130 for(WGPUBuffer &alias: buffer.alias){
131 wgpuBufferRelease(alias);
132 }
133 }
134 }
136 {
137 annotate((BufferHandle)handle, name);
138 }
139
140 VertexBufferHandle BuffersWebGPU::loadVertexBuffer(const void* vertexData, const size_t count, const VertexFormat& vertexFormat)
141 {
142 const uint32_t stride = getSize(vertexFormat);
143 const size_t size = count * stride;
144 auto handle = loadBuffer(vertexData, size, Usage::Dynamic, AccessMode::None, BindFlags::VertexBuffer);
145 return handle;
146 }
147
148 VertexBufferHandle BuffersWebGPU::loadVertexBuffer(const void* vertexData, const size_t count, VertexFormatHandle vertexFormatHandle)
149 {
150 return loadVertexBuffer(vertexData, count, *VertexFormats::getVertexFormat(vertexFormatHandle));
151 }
152
154 {
155 buffers.removeResource(handle);
156 }
157
158 IndexBufferHandle BuffersWebGPU::loadIndexBuffer(const void* indexData, const size_t count, const size_t indexSize)
159 {
160 const size_t size = count * indexSize;
161 auto handle = loadBuffer(indexData, size, Usage::Dynamic, AccessMode::None, BindFlags::IndexBuffer);
162 return handle;
163 }
164
166 {
167 buffers.removeResource(handle);
168 }
169
170 InputLayoutHandle BuffersWebGPU::loadInputLayout(const VertexFormatHandle* vertexFormats, const size_t count, EffectHandle effectHandle)
171 {
172 size_t attribute_count = 0;
173 for(size_t i=0; i<count; i++){
174 const VertexFormat *vtx_format = getVertexFormat(vertexFormats[i]);
175 for(size_t j=0; j<vtx_format->elements.size(); j++){
176 const VertexElement &element = vtx_format->elements[j];
177 if(element.format == DataFormat::MAT4X4_FLOAT){
178 attribute_count += 4;
179 }
180 else{
181 attribute_count += 1;
182 }
183 }
184 }
185
186 InputLayoutWebGPU input_layout = {};
187 input_layout.vertex_attributes.resize(attribute_count);
188 input_layout.vertex_buffer_layout.resize(count);
189
190 size_t attribute_offset = 0;
191 for(size_t i=0; i<count; i++){
192 const VertexFormat *vtx_format = getVertexFormat(vertexFormats[i]);
193 WGPUVertexBufferLayout &vtx_layout = input_layout.vertex_buffer_layout[i];
194 vtx_layout.arrayStride = getSize(*vtx_format);
195 if(vtx_format->elements[0].inputType == InputType::VertexData){
196 vtx_layout.stepMode = WGPUVertexStepMode_Vertex;
197 }
198 else if(vtx_format->elements[0].inputType == InputType::InstanceData){
199 vtx_layout.stepMode = WGPUVertexStepMode_Instance;
200 }
201 else{
202 // vtx_layout.stepMode = WGPUVertexStepMode_VertexBufferNotUsed;
203 assert(false);
204 }
205 vtx_layout.attributeCount = 0;
206 vtx_layout.attributes = vtx_format->elements.size() ? &input_layout.vertex_attributes[attribute_offset] : nullptr;
207 for(size_t j=0; j<vtx_format->elements.size(); j++){
208 const VertexElement &element = vtx_format->elements[j];
209 if(vtx_layout.stepMode == WGPUVertexStepMode_Vertex){
210 assert(element.inputType == InputType::VertexData);
211 assert(element.instanceStep == 0);
212 }
213 else if(vtx_layout.stepMode == WGPUVertexStepMode_Instance){
214 assert(element.inputType == InputType::InstanceData);
215 assert(element.instanceStep == 1);
216 }
217 else{
218 assert(false);
219 }
220 EffectWebGPU& effect = graphicsDevice->effects.effects[effectHandle];
221
222 uint32_t attribute_location = std::numeric_limits<uint32_t>::max();
223 SemanticSlotBinding semantic = {};
224 semantic.format = static_cast<WGPUVertexFormat>(0);
225 for (size_t s = 0; s < effect.num_attribs; s++) {
226 const SemanticSlotBinding& sem = effect.semanticSlotBindings[s];
227 if (sem.semantic == size_t(element.semantic) && sem.slot == element.semanticIndex) {
228 attribute_location = sem.binding;
229 semantic = sem;
230 break;
231 }
232 }
233 if (attribute_location == std::numeric_limits<uint32_t>::max()) {
234 LOG_WARNING(logger, "Vertex attribute semantic %d, slot %d not used in shader", (int)element.semantic, element.semanticIndex);
235 }
236 if(element.format == DataFormat::MAT4X4_FLOAT){
237 for(int k=0; k<4; k++){
238 WGPUVertexAttribute &att = input_layout.vertex_attributes[attribute_offset++];
239 att.format = WGPUVertexFormat_Float32x4;
240 att.offset = element.offset + sizeof(float)*4*k;
241 att.shaderLocation = attribute_location+k;
242 vtx_layout.attributeCount++;
243 }
244 }
245 else{
246 DataFormat format = element.format;
247 // WebGPU does not support RGB vertex formats for 8 and 16 byte types.
248 // Upgrade these to RGBA formats and hope that the buffer stride is wide enough.
249 if(format == DataFormat::R8G8B8_UNORM){
250 format = DataFormat::R8G8B8A8_UNORM;
251 LOG_WARNING(logger, "Vertex attribute format R8G8B8_UNORM not supported by WebGPU (Using R8G8B8A8_UNORM).");
252 }
253 else if(format == DataFormat::R8G8B8_SNORM){
254 format = DataFormat::R8G8B8A8_SNORM;
255 LOG_WARNING(logger, "Vertex attribute format R8G8B8_SNORM not supported by WebGPU (Using R8G8B8A8_SNORM).");
256 }
257 else if(format == DataFormat::R8G8B8_UINT){
258 format = DataFormat::R8G8B8A8_UINT;
259 LOG_WARNING(logger, "Vertex attribute format R8G8B8_UINT not supported by WebGPU (Using R8G8B8A8_UINT).");
260 }
261 else if(format == DataFormat::R8G8B8_SINT){
262 format = DataFormat::R8G8B8A8_SINT;
263 LOG_WARNING(logger, "Vertex attribute format R8G8B8_SINT not supported by WebGPU (Using R8G8B8A8_SINT).");
264 }
265 else if(format == DataFormat::R16G16B16_UNORM){
266 format = DataFormat::R16G16B16A16_UNORM;
267 LOG_WARNING(logger, "Vertex attribute format R16G16B16_UNORM not supported by WebGPU (Using R16G16B16A16_UNORM).");
268 }
269 else if(format == DataFormat::R16G16B16_SNORM){
270 format = DataFormat::R16G16B16A16_SNORM;
271 LOG_WARNING(logger, "Vertex attribute format R16G16B16_SNORM not supported by WebGPU (Using R16G16B16A16_SNORM).");
272 }
273 else if(format == DataFormat::R16G16B16_UINT){
274 format = DataFormat::R16G16B16A16_UINT;
275 LOG_WARNING(logger, "Vertex attribute format R16G16B16_UINT not supported by WebGPU (Using R16G16B16A16_UINT).");
276 }
277 else if(format == DataFormat::R16G16B16_SINT){
278 format = DataFormat::R16G16B16A16_SINT;
279 LOG_WARNING(logger, "Vertex attribute format R16G16B16_SINT not supported by WebGPU (Using R16G16B16A16_SINT).");
280 }
281 else if(format == DataFormat::R16G16B16_FLOAT){
282 format = DataFormat::R16G16B16A16_FLOAT;
283 LOG_WARNING(logger, "Vertex attribute format R16G16B16_FLOAT not supported by WebGPU (Using R16G16B16A16_FLOAT).");
284 }
285 WGPUVertexAttribute &att = input_layout.vertex_attributes[attribute_offset++];
286 att.format = VertexFormatsWebGPU[(size_t)format];
287 if (semantic.format != att.format) {
288 LOG_WARNING(logger, "Inconsistent vertex attribute format");
289 }
290 assert(att.format != static_cast<WGPUVertexFormat>(0)); // Vertex format not supported
291 att.offset = element.offset;
292 att.shaderLocation = attribute_location;
293 vtx_layout.attributeCount++;
294 }
295 }
296 }
297 assert(attribute_offset == input_layout.vertex_attributes.size());
298 return inputLayouts.addResource(std::move(input_layout));
299 }
300
302 {
303 inputLayouts.removeResource(inputLayoutHandle);
304 }
305
306 BufferHandle BuffersWebGPU::loadBuffer(const void* data, const size_t size, Usage::EUsage /*usage*/, uint32_t accessMode, uint32_t bindFlags, uint32_t /*stride*/)
307 {
308 assert((bindFlags&BindFlags::StreamOutBuffer) == 0);
309 assert((accessMode&AccessMode::ReadWrite) != AccessMode::ReadWrite); // Not available for WebGPU
310
311 BufferWebGPU buffer = {};
312 buffer.size = size;
313 buffer.accessMode = accessMode;
314 buffer.bindFlags = bindFlags;
315
316 if(accessMode&AccessMode::Read){
317 buffer.is_read_buffer = true;
318 assert(bindFlags == 0);
319 // Round up
320 size_t granularity = 256;
321 if(buffer.size%granularity != 0)
322 buffer.size += granularity-buffer.size%granularity;
323 }
324 else{
325 // Round up
326 size_t granularity = 16;
327 if (buffer.size % granularity != 0)
328 buffer.size += granularity - buffer.size % granularity;
329 }
330
331 buffer.CreateInstance(graphicsDevice, *this);
332 if(data){
333 wgpuQueueWriteBuffer(graphicsDevice->queue, buffer.buffer, 0, data, buffer.size);
334 }
335
336 return this->buffers.addResource(std::move(buffer));
337 }
338
340 {
341 ResourceCountersWebGPU &counters = graphicsDevice->counters;
342 BufferWebGPU &buffer = buffers[handle];
343 if(!buffer.is_read_buffer){
344 wgpuBufferRelease(buffer.buffer);
345 counters.buffer--;
346 bufferMemoryConsumption -= buffer.size;
347 }
348 else{
349 for(WGPUBuffer &alias: buffer.alias){
350 wgpuBufferRelease(alias);
351 counters.buffer--;
352 bufferMemoryConsumption -= buffer.size;
353 }
354 }
355 buffers.removeResource(handle);
356 }
357
359 {
360 return static_cast<void*>(buffers[handle].buffer);
361 }
362
363}
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 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
@ 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
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