Cogs.Core
ContextWebGPU.cpp
1#include "ContextWebGPU.h"
2
3#include "GraphicsDeviceWebGPU.h"
4#include "PipelineStatesWebGPU.h"
5#include "BindGroupsWebGPU.h"
6#include "Foundation/Logging/Logger.h"
7
8namespace {
9 Cogs::Logging::Log logger = Cogs::Logging::getLogger("ContextWebGPU");
10
11 void context_error_callback(WGPUPopErrorScopeStatus status, WGPUErrorType type, WGPUStringView message, void* userdata1, void* userdata2)
12 {
13 Cogs::ContextWebGPU *context = (Cogs::ContextWebGPU*)userdata1;
14 (void)context;
16 (void)ptr;
17 if(status == WGPUPopErrorScopeStatus_CallbackCancelled){
18 LOG_ERROR(logger, "WebGPU context err status: Cancelled");
19 }
20 if(status == WGPUPopErrorScopeStatus_Error){
21 LOG_ERROR(logger, "WebGPU context err status: Error");
22 }
23 if(message.data && message.length){
24 LOG_ERROR(logger, "WebGPU context err (%d) %.*s", type, WGPUStringViewFormat(message));
25 }
26 }
27
28 WGPULoadOp ConvWebGPULoadOp(Cogs::LoadOp load_op)
29 {
30 switch(load_op){
31 case Cogs::LoadOp::Undefined: return WGPULoadOp_Undefined;
32 case Cogs::LoadOp::Clear: return WGPULoadOp_Clear;
33 case Cogs::LoadOp::Load: return WGPULoadOp_Load;
34 }
35 return WGPULoadOp_Undefined;
36 }
37 WGPUStoreOp ConvWebGPUStoreOp(Cogs::StoreOp store_op)
38 {
39 switch(store_op){
40 case Cogs::StoreOp::Undefined: return WGPUStoreOp_Undefined;
41 case Cogs::StoreOp::Store: return WGPUStoreOp_Store;
42 case Cogs::StoreOp::Discard: return WGPUStoreOp_Discard;
43 }
44 return WGPUStoreOp_Undefined;
45 }
46
47 void decodeConstantBufferBindingHandle(int64_t handle, uint32_t &group, uint32_t &binding) {
48 int64_t i = handle;
49 group = static_cast<uint32_t>(i >> 16);
50 binding = (i & 0xFFFF) - 1;
51 }
52
53 bool operator==(const WGPUBindGroupEntry &a, const WGPUBindGroupEntry &b)
54 {
55 assert(a.nextInChain == nullptr);
56 assert(b.nextInChain == nullptr);
57 if(a.binding != b.binding) return false;
58 if(a.buffer != b.buffer) return false;
59 if(a.offset != b.offset) return false;
60 if(a.size != b.size) return false;
61 if(a.sampler != b.sampler) return false;
62 if(a.textureView != b.textureView) return false;
63 return true;
64 }
65 bool operator!=(const WGPUBindGroupEntry &a, const WGPUBindGroupEntry &b)
66 {
67 return !(a == b);
68 }
69
70}
71
72namespace Cogs{
73
74 void ContextWebGPU::initialize(GraphicsDeviceWebGPU *graphicsDeviceIn)
75 {
76 graphicsDevice = graphicsDeviceIn;
77 }
78
79 void ContextWebGPU::signal(FenceHandle /*fenceHandle*/)
80 {
81 // assert(false); // TODO
82 }
83
84 void ContextWebGPU::clearRenderTarget(const float* value)
85 {
86 assert(!inRenderPass);
87 for(size_t i=0; i<sizeof(clearColor)/sizeof(clearColor[0]); i++){
88 clearColor[i].r = value[0];
89 clearColor[i].g = value[1];
90 clearColor[i].b = value[2];
91 clearColor[i].a = value[3];
92 }
93 do_clear_render_target = true;
94 update_render_target = true;
95 }
96
97 void ContextWebGPU::clearRenderTarget(const float** values, const int numvalues)
98 {
99 assert(!inRenderPass);
100 for(int i=0; i<numvalues; i++){
101 clearColor[i].r = values[i][0];
102 clearColor[i].g = values[i][1];
103 clearColor[i].b = values[i][2];
104 clearColor[i].a = values[i][3];
105 }
106 for(size_t i=numvalues; i<sizeof(clearColor)/sizeof(clearColor[0]); i++){
107 clearColor[i] = WGPU_COLOR_INIT;
108 }
109 do_clear_render_target = true;
110 update_render_target = true;
111 }
112
113 void ContextWebGPU::clearDepth(const float depth)
114 {
115 assert(!inRenderPass);
116 clearDepthVal = depth;
117 do_clear_depth = true;
118 update_render_target = true;
119 }
120
122 {
123 (void)name;
124
125 // TODO: This seems to become messed up when changing render passes, etc
126
127 // WGPUStringView label = {name.data(), name.length()};
128 // if(renderPassEncoder != 0){
129 // wgpuRenderPassEncoderPushDebugGroup(renderPassEncoder, label);
130 // annotation_type.push_back(ANNOTATION_TYPE_RENDER_PASS);
131 // }
132 // else if(computePassEncoder != 0){
133 // wgpuComputePassEncoderPushDebugGroup(computePassEncoder, label);
134 // annotation_type.push_back(ANNOTATION_TYPE_COMPUTE_PASS);
135 // }
136 // // else if(renderBundleEncoder != 0){
137 // // annotation_type.push_back(ANNOTATION_TYPE_RENDER_BUNDLE);
138 // // wgpuRenderBundleEncoderPushDebugGroup(renderBundleEncoder, label);
139 // // }
140 // else{
141 // wgpuCommandEncoderPushDebugGroup(graphicsDevice->commandEncoder, label);
142 // annotation_type.push_back(ANNOTATION_TYPE_COMMAND_ENCODER);
143 // }
144 }
145
147 {
148 // assert(annotation_type.size());
149 // WGPUAnnotationType type = annotation_type.back();
150 // annotation_type.pop_back();
151 // if(type == ANNOTATION_TYPE_RENDER_PASS){
152 // assert(renderPassEncoder != 0);
153 // wgpuRenderPassEncoderPopDebugGroup(renderPassEncoder);
154 // }
155 // else if(type == ANNOTATION_TYPE_COMPUTE_PASS){
156 // assert(computePassEncoder != 0);
157 // wgpuComputePassEncoderPopDebugGroup(computePassEncoder);
158 // }
159 // // else if(type == ANNOTATION_TYPE_RENDER_BUNDLE){
160 // // assert(renderBundleEncoder != 0);
161 // // wgpuRenderBundleEncoderPopDebugGroup(renderBundleEncoder);
162 // // }
163 // else{
164 // assert(type == ANNOTATION_TYPE_COMMAND_ENCODER);
165 // wgpuCommandEncoderPopDebugGroup(graphicsDevice->commandEncoder);
166 // }
167 }
168
170 {
171 WGPUStringView label = {name.data(), name.length()};
172 if(renderPassEncoder != 0){
173 wgpuRenderPassEncoderInsertDebugMarker(renderPassEncoder, label);
174 }
175 else if(computePassEncoder != 0){
176 wgpuComputePassEncoderInsertDebugMarker(computePassEncoder, label);
177 }
178 // else if(renderBundleEncoder != 0){
179 // wgpuRenderBundleEncoderInsertDebugMarker(renderBundleEncoder, label);
180 // }
181 else{
182 wgpuCommandEncoderInsertDebugMarker(graphicsDevice->commandEncoder, label);
183 }
184 }
185
187 {
188 assert(renderPassEncoder == 0);
189 assert(computePassEncoder == 0);
190 assert(!inRenderPass);
191 inRenderPass = true;
192 update_render_target = false;
193
194 renderTargetHandle = info.renderTargetHandle;
195 depthStencilHandle = info.depthStencilHandle;
196
197 WGPURenderPassColorAttachment color_attachment[8] = {}; // WGPU_RENDER_PASS_COLOR_ATTACHMENT_INIT
198 WGPURenderPassDepthStencilAttachment ds_attachment = WGPU_RENDER_PASS_DEPTH_STENCIL_ATTACHMENT_INIT;
199 uint32_t color_attachment_count = 0;
200 uint32_t ds_attachment_count = 0;
201
202 const char *name = nullptr;
203 if (HandleIsValid(info.renderTargetHandle) || HandleIsValid(info.depthStencilHandle)) {
204 name = "Custom Render Pass";
205 if(HandleIsValid(info.renderTargetHandle)){
206 RenderTargetWebGPU &target = graphicsDevice->renderTargets.render_targets[info.renderTargetHandle];
207 for(size_t i=0; i<target.count; i++){
208 WGPURenderPassColorAttachment &att = color_attachment[i];
209 att.view = target.view[i];
210 att.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
211 if(info.resolveHandle[i]){
212 att.resolveTarget = graphicsDevice->textures.textureViews[info.resolveHandle[i]].texture_view;
213 }
214 else{
215 att.resolveTarget = nullptr;
216 }
217 att.loadOp = ConvWebGPULoadOp(info.loadOp[i]);
218 att.storeOp = ConvWebGPUStoreOp(info.storeOp[i]);
219 att.clearValue.r = info.clearValue[i][0];
220 att.clearValue.g = info.clearValue[i][1];
221 att.clearValue.b = info.clearValue[i][2];
222 att.clearValue.a = info.clearValue[i][3];
223 }
224 color_attachment_count = target.count;
225 }
226 if(HandleIsValid(info.depthStencilHandle)){
227 DepthStencilTargetWebGPU &target = graphicsDevice->renderTargets.depth_stencil_targets[info.depthStencilHandle];
228 WGPURenderPassDepthStencilAttachment &att = ds_attachment;
229 att.view = target.view;
230 att.depthLoadOp = ConvWebGPULoadOp(info.depthLoadOp);
231 att.depthStoreOp = ConvWebGPUStoreOp(info.depthStoreOp);
232 att.depthClearValue = info.depthClearValue;
233 att.depthReadOnly = info.depthReadOnly;
234 att.stencilLoadOp = WGPULoadOp_Undefined;
235 att.stencilStoreOp = WGPUStoreOp_Undefined;
236 att.stencilClearValue = 0;
237 att.stencilReadOnly = false;
238 ds_attachment_count = 1;
239 }
240 }
241 else{
242 name = "Default Render Pass";
243 SwapChainWebGPU &defaultSwapChain = graphicsDevice->defaultSwapChain;
244 if(defaultSwapChain.samples > 1){
245 color_attachment[0].view = defaultSwapChain.colorBufferView;
246 color_attachment[0].resolveTarget = defaultSwapChain.resolveView;
247 }
248 else{
249 color_attachment[0].view = defaultSwapChain.resolveView;
250 }
251 color_attachment[0].depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
252 color_attachment[0].loadOp = ConvWebGPULoadOp(info.loadOp[0]);
253 color_attachment[0].storeOp = ConvWebGPUStoreOp(info.storeOp[0]);
254 color_attachment[0].clearValue.r = info.clearValue[0][0];
255 color_attachment[0].clearValue.g = info.clearValue[0][1];
256 color_attachment[0].clearValue.b = info.clearValue[0][2];
257 color_attachment[0].clearValue.a = info.clearValue[0][3];
258 color_attachment_count = 1;
259
260 ds_attachment.view = defaultSwapChain.depthBufferView;
261 ds_attachment.depthLoadOp = ConvWebGPULoadOp(info.depthLoadOp);
262 ds_attachment.depthStoreOp = ConvWebGPUStoreOp(info.depthStoreOp);
263 ds_attachment.depthClearValue = info.depthClearValue;
264 ds_attachment.depthReadOnly = info.depthReadOnly;
265 ds_attachment.stencilLoadOp = WGPULoadOp_Undefined;
266 ds_attachment.stencilStoreOp = WGPUStoreOp_Undefined;
267 ds_attachment.stencilClearValue = 0;
268 ds_attachment.stencilReadOnly = false;
269 ds_attachment_count = 1;
270 }
271
272 if(graphicsDevice->use_error_scope){
273 wgpuDevicePushErrorScope(graphicsDevice->device, graphicsDevice->filter);
274 }
275
276 WGPURenderPassDescriptor render_pass_desc = WGPU_RENDER_PASS_DESCRIPTOR_INIT;
277 render_pass_desc.label = {name, WGPU_STRLEN};
278 if(color_attachment_count){
279 render_pass_desc.colorAttachmentCount = color_attachment_count;
280 render_pass_desc.colorAttachments = color_attachment;
281 }
282 if(ds_attachment_count){
283 render_pass_desc.depthStencilAttachment = &ds_attachment;
284 }
285 render_pass_desc.occlusionQuerySet = nullptr;
286 // render_pass_desc.timestampWriteCount = 0;
287 render_pass_desc.timestampWrites = nullptr;
288 renderPassEncoder = wgpuCommandEncoderBeginRenderPass(graphicsDevice->commandEncoder, &render_pass_desc);
289 }
291 {
292 assert(renderPassEncoder);
293 assert(inRenderPass);
294 inRenderPass = false;
295 wgpuRenderPassEncoderEnd(renderPassEncoder);
296 wgpuRenderPassEncoderRelease(renderPassEncoder);
297 renderPassEncoder = 0;
298 update_render_target = true;
299
300// for (uint32_t g = 0; g < MAX_BIND_GROUPS; g++) {
301// current_bind_groups[g] = {};
302// update_descriptors[g] = true;
303// descriptors[g].clear();
304// }
305
306 if(graphicsDevice->use_error_scope){
307 WGPUPopErrorScopeCallbackInfo info = WGPU_POP_ERROR_SCOPE_CALLBACK_INFO_INIT;
308 info.mode = WGPUCallbackMode_AllowSpontaneous;
309 info.callback = context_error_callback;
310 info.userdata1 = this;
311 info.userdata2 = graphicsDevice;
312 WGPUFuture future = wgpuDevicePopErrorScope(graphicsDevice->device, info);
313 // WGPUWaitStatus wgpuInstanceWaitAny(WGPUInstance instance, size_t futureCount, WGPUFutureWaitInfo * futures, uint64_t timeoutNS);
314 }
315 }
316
318 {
319 if(HandleIsValid(rt_handle))
320 renderTargetHandle = rt_handle;
321 else
322 renderTargetHandle = {};
323 if(HandleIsValid(ds_handle))
324 depthStencilHandle = ds_handle;
325 else
326 depthStencilHandle = {};
327 rasterizeStateHandle = {};
328 blendStateHandle = {};
329 assert(do_clear_render_target == false); // Maybe do update render pass to force clear?
330 assert(do_clear_depth == false); // Maybe do update render pass to force clear?
331 do_clear_render_target = false;
332 do_clear_depth = false;
333 update_render_target = true;
334 }
335
336 void ContextWebGPU::setViewport(const float x, const float y, const float width, const float height)
337 {
338 updateRenderPass();
339 float minDepth = 0.0f; // TODO
340 float maxDepth = 1.0f; // TODO
341 wgpuRenderPassEncoderSetViewport(renderPassEncoder, x, y, width, height, minDepth, maxDepth);
342 }
343
344 void ContextWebGPU::setScissor(const int x, const int y, const int width, const int height)
345 {
346 updateRenderPass();
347 int w, h;
348 graphicsDevice->getSize(w, h); // TODO
349 int xx = glm::clamp(x, 0, w);
350 int yy = glm::clamp(y, 0, h);
351 int ww = glm::clamp(width, 0, w-xx);
352 int hh = glm::clamp(height, 0, h-yy);
353 wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, xx, yy, ww, hh);
354 }
355
357 {
358 if(HandleIsValid(handle))
359 depthStencilStateHandle = handle;
360 else
361 depthStencilStateHandle = {};
362 }
363
364 void ContextWebGPU::setBlendState(const BlendStateHandle handle, const float *constants)
365 {
366 updateRenderPass();
367 if(HandleIsValid(handle))
368 blendStateHandle = handle;
369 else
370 blendStateHandle = {};
371 WGPUColor color; // Emulate dx11 with 1.0f constants if not set
372 if(constants){
373 color.r = constants[0];
374 color.g = constants[1];
375 color.b = constants[2];
376 color.a = constants[3];
377 }
378 else{
379 color.r = 1.0f;
380 color.g = 1.0f;
381 color.b = 1.0f;
382 color.a = 1.0f;
383 }
384 wgpuRenderPassEncoderSetBlendConstant(renderPassEncoder, &color);
385 }
386
388 {
389 if(HandleIsValid(handle))
390 rasterizeStateHandle = handle;
391 else
392 rasterizeStateHandle = {};
393 }
394
395 void ContextWebGPU::setDefaults() {
396 rasterizeStateHandle = {};
397 depthStencilStateHandle = {};
398 blendStateHandle = {};
399 }
400
402 {
403 if(HandleIsValid(handle))
404 effect = handle;
405 else
406 effect = {};
407 inputLayoutHandle = {};
408 uint32_t numGroups = std::min(getEffects()->getNumBindGroups(handle), static_cast<uint32_t>(MAX_BIND_GROUPS));
409 for (uint32_t g = 0; g < numGroups; g++) {
410 if (!HandleIsValid(bind_group_handles[g])) {
411 bind_group_handles[g] = BindGroupHandle::NoHandle;
412 bind_group_owned_by_context[g] = false;
413 continue;
414 }
415 if (getEffects()->getBindGroupDescription(handle, g).numEntries == 0) continue;
416
417
418 if (bind_group_owned_by_context[g]) {
419 if (!getBindGroups()->isCompatable(bind_group_handles[g], getEffects()->getBindGroupDescHash(handle, g))) {
420 getBindGroups()->releaseBindGroup(bind_group_handles[g]);
421 bind_group_handles[g] = BindGroupHandle::NoHandle;
422 bind_group_owned_by_context[g] = false;
423 }
424 } else {
425// bind_group_handles[g] = BindGroupHandle::NoHandle;
426 }
427 }
428 }
429
430 void ContextWebGPU::setTexture(const StringView& name, unsigned int unit, TextureHandle textureHandle)
431 {
432 setTexture(getEffects()->getTextureBinding(getCurrentEffect(), name, unit), textureHandle);
433 }
434
436 {
437 if (!HandleIsValid(binding)) {
438 LOG_DEBUG_ONCE(logger, "Invalid texture binding handle");
439 return;
440 }
441 uint32_t group = ~(uint32_t)0;
442 uint32_t decoded_binding = 0;
443 decodeConstantBufferBindingHandle(binding.handle, group, decoded_binding);
444 if (group >= MAX_BIND_GROUPS) {
445 LOG_ERROR_ONCE(logger, "Texture binding group index out of range: %llu", (unsigned long long)group);
446 return;
447 }
448
449 if (!HandleIsValid(bind_group_handles[group])) {
450 if (!createOwnedBindGroup(group)) {
451 LOG_ERROR_ONCE(logger, "Failed to create owned bindgroup for group %u", group);
452 return;
453 }
454 }
455 BindGroupTextureBindingHandle bindGroupBindingHandle = static_cast<BindGroupTextureBindingHandle>(decoded_binding + 1); // TODO: Must be a prettier way
456 getBindGroups()->setTexture(bind_group_handles[group], bindGroupBindingHandle, handle);
457 }
458
459 void ContextWebGPU::setTexture(const StringView& name, TextureViewHandle textureViewHandle)
460 {
461 setTexture(getEffects()->getTextureBinding(getCurrentEffect(), name, 1), textureViewHandle);
462 }
463
464 void ContextWebGPU::setTexture(const TextureBindingHandle binding, TextureViewHandle handle)
465 {
466 if (!HandleIsValid(binding)) {
467 LOG_DEBUG_ONCE(logger, "Invalid texture binding handle");
468 return;
469 }
470 uint32_t group = ~(uint32_t)0;
471 uint32_t decoded_binding = 0;
472 decodeConstantBufferBindingHandle(binding.handle, group, decoded_binding);
473 if (group >= MAX_BIND_GROUPS) {
474 LOG_ERROR_ONCE(logger, "Texture-view binding group index out of range: %u", group);
475 return;
476 }
477
478 if (!HandleIsValid(bind_group_handles[group])) {
479 if (!createOwnedBindGroup(group)) {
480 LOG_ERROR_ONCE(logger, "Failed to create owned bindgroup for group %u", group);
481 return;
482 }
483 }
484 BindGroupTextureBindingHandle bindGroupBindingHandle = static_cast<BindGroupTextureBindingHandle>(decoded_binding + 1); // TODO: Must be a prettier way
485 getBindGroups()->setTexture(bind_group_handles[group], bindGroupBindingHandle, handle);
486 }
487
488 void ContextWebGPU::setSamplerState(const StringView& name, unsigned int unit, SamplerStateHandle handle)
489 {
490 setSamplerState(getEffects()->getSamplerStateBinding(getCurrentEffect(), name, unit), handle);
491 }
492
494 {
495 if (!HandleIsValid(binding)) {
496 LOG_DEBUG_ONCE(logger, "Invalid sampler state binding handle");
497 return;
498 }
499 uint32_t group = ~(uint32_t)0;
500 uint32_t decoded_binding = 0;
501 decodeConstantBufferBindingHandle(binding.handle, group, decoded_binding);
502 if (group >= MAX_BIND_GROUPS) {
503 LOG_ERROR_ONCE(logger, "Sampler binding group index out of range: %u", group);
504 return;
505 }
506
507 if (!HandleIsValid(bind_group_handles[group])) {
508 if (!createOwnedBindGroup(group)) {
509 LOG_ERROR_ONCE(logger, "Failed to create owned bindgroup for group %u", group);
510 return;
511 }
512 }
513 BindGroupSamplerStateBindingHandle bindGroupBindingHandle = static_cast<BindGroupSamplerStateBindingHandle>(decoded_binding + 1); // TODO: Must be a prettier way
514 getBindGroups()->setSamplerState(bind_group_handles[group], bindGroupBindingHandle, handle);
515 }
516
518 {
519 if(HandleIsValid(handle))
520 inputLayoutHandle = handle;
521 else
522 inputLayoutHandle = {};
523 }
524
525 void ContextWebGPU::setVertexBuffers(const VertexBufferHandle* vertexBufferHandles, const size_t count, const uint32_t* /*strides*/, const uint32_t* offsets)
526 {
527 updateRenderPass();
528 for(size_t i=0; i<count; i++){
529 BufferWebGPU& buffer = graphicsDevice->buffers.buffers[(BufferHandle)vertexBufferHandles[i].handle];
530 uint32_t slot = (uint32_t)i;
531 // uint32_t stride = strides ? strides[i] : 0;
532 uint32_t offset = offsets ? offsets[i] : 0;
533 wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, slot, buffer.buffer, offset, buffer.size-offset);
534 }
535 }
536
537 void ContextWebGPU::setVertexBuffers(const VertexBufferHandle * handles, const size_t count)
538 {
539 setVertexBuffers(handles, count, nullptr, nullptr);
540 }
541
542 void ContextWebGPU::setIndexBuffer(IndexBufferHandle bufferHandle, uint32_t stride, uint32_t offset_in)
543 {
544 if (bufferHandle == IndexBufferHandle::NoHandle) {
545 return;
546 }
547 updateRenderPass();
548 WGPUIndexFormat format = (stride == 2) ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32;
549 currentIndexFormat = format;
550 uint64_t offset = offset_in;
551 BufferWebGPU& buffer = graphicsDevice->buffers.buffers[(BufferHandle)bufferHandle.handle];
552 wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, buffer.buffer, format, offset, buffer.size);
553 }
554
556 {
557 assert(false); // TODO
558 }
559
560 void ContextWebGPU::setBindGroup(BindGroupHandle bindGroupHandle, uint32_t groupIdx) {
561 if (groupIdx >= MAX_BIND_GROUPS) {
562 LOG_ERROR_ONCE(logger, "Group index out of range %u", groupIdx);
563 return;
564 }
565 if (bind_group_owned_by_context[groupIdx]) {
566 getBindGroups()->releaseBindGroup(bind_group_handles[groupIdx]);
567 }
568 bind_group_owned_by_context[groupIdx] = false;
569 if (bindGroupHandle == BindGroupHandle::NoHandle) {
570 bind_group_handles[groupIdx] = BindGroupHandle::NoHandle;
571 return;
572 }
573 bind_group_handles[groupIdx] = bindGroupHandle;
574 if (!HandleIsValid(bindGroupHandle)) {
575 bind_group_handles[groupIdx] = BindGroupHandle::InvalidHandle;
576 LOG_ERROR_ONCE(logger, "Failed to set bindgroup");
577 return;
578 }
579 }
580
581 void ContextWebGPU::setConstantBuffer(const StringView& name, const BufferHandle bufferHandle, const uint32_t offset, const uint32_t size)
582 {
583 setConstantBuffer(getEffects()->getConstantBufferBinding(getCurrentEffect(), name), bufferHandle, offset, size);
584 }
585
586 bool ContextWebGPU::createOwnedBindGroup(uint32_t group) {
587 if (group >= MAX_BIND_GROUPS) {
588 LOG_ERROR_ONCE(logger, "Cannot create bindgroup for out-of-range group index %llu", (unsigned long long)group);
589 return false;
590 }
591 if (!HandleIsValid(bind_group_handles[group])) {
592 const BindGroupDescription& desc = getEffects()->getBindGroupDescription(getCurrentEffect(), group);
593 if (desc.numEntries == 0) {
594 LOG_ERROR_ONCE(logger, "Cannot create bindgroup with no elements index %llu", (unsigned long long)group);
595 return false;
596 }
597 BindGroupHandle newBindGroupHandle = getBindGroups()->createBindGroup(&desc);
598 if (HandleIsValid(newBindGroupHandle)) {
599 bind_group_handles[group] = newBindGroupHandle;
600 bind_group_owned_by_context[group] = true;
601 }
602 else {
603 LOG_ERROR_ONCE(logger, "Failed to create bindgroup for group %u", group);
604 return false;
605 }
606 }
607 return true;
608 }
609
610 void ContextWebGPU::setConstantBuffer(const ConstantBufferBindingHandle binding, const BufferHandle handle, const uint32_t offset, const uint32_t size)
611 {
612 if (!HandleIsValid(binding)) {
613 LOG_DEBUG_ONCE(logger, "Invalid constant buffer binding handle");
614 return;
615 }
616 uint32_t group = ~(uint32_t)0;
617 uint32_t decoded_binding = 0;
618 decodeConstantBufferBindingHandle(binding.handle, group, decoded_binding);
619 if (group >= MAX_BIND_GROUPS) {
620 LOG_ERROR_ONCE(logger, "Constant-buffer binding group index out of range: %u", group);
621 return;
622 }
623
624 if (!HandleIsValid(bind_group_handles[group])) {
625 if (!createOwnedBindGroup(group)) {
626 LOG_ERROR_ONCE(logger, "Failed to create owned bindgroup for group %u", group);
627 return;
628 }
629 }
630 BindGroupConstantBufferBindingHandle bindGroupBindingHandle = static_cast<BindGroupConstantBufferBindingHandle>(decoded_binding + 1); // TODO: Must be a prettier way
631 getBindGroups()->setConstantBuffer(bind_group_handles[group], bindGroupBindingHandle, handle, offset, size);
632 }
633
634 void ContextWebGPU::setBuffer(const StringView& name, BufferHandle bufferHandle)
635 {
636 auto binding = getEffects()->getBufferBinding(getCurrentEffect(), name);
637 setBuffer(binding, bufferHandle);
638 getEffects()->releaseBufferBinding(binding);
639 }
640
641 void ContextWebGPU::setBuffer(const BufferBindingHandle /*bufferBindingHandle*/, BufferHandle /*bufferHandle*/)
642 {
643 assert(false); // TODO
644 }
645
646 void ContextWebGPU::setBufferCounter(BufferHandle /*bufferHandle*/, uint32_t /*value*/)
647 {
648 assert(false); // TODO
649 }
650
651 void ContextWebGPU::setBufferCounter(BufferHandle /*bufferHandle*/, BufferHandle /*sourceBufferHandle*/)
652 {
653 assert(false); // TODO
654 }
655
656 void ContextWebGPU::getBufferCounter(BufferHandle /*bufferHandle*/, BufferHandle /*destinationBufferHandle*/)
657 {
658 assert(false); // TODO
659 }
660
661 void ContextWebGPU::draw(PrimitiveType primitiveType, const size_t startVertex, const size_t numVertexes)
662 {
663 drawInstanced(primitiveType, startVertex, numVertexes, 0, 1);
664 }
665
666 void ContextWebGPU::drawIndexed(PrimitiveType primitiveType, const size_t startIndex, const size_t numIndexes, const size_t startVertex)
667 {
668 updateRenderPass();
669 if (updateRenderPipeline(primitiveType)) {
670 wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, (uint32_t)numIndexes, 1, (uint32_t)startIndex, (int32_t)startVertex, 0);
671 if (frameStatisticsEnabled) { frameStatisticsAccountDrawCall(numIndexes, true); }
672 }
673 else{
674 LOG_ERROR_ONCE(logger, "skip drawIndexed(), updateRenderPipeline failed.");
675 }
676 }
677
678 void ContextWebGPU::drawInstanced(PrimitiveType primitiveType, const size_t startVertex, const size_t numVertexes, const size_t startInstance, const size_t numInstances)
679 {
680 updateRenderPass();
681 if (updateRenderPipeline(primitiveType)) {
682 wgpuRenderPassEncoderDraw(renderPassEncoder, (uint32_t)numVertexes, (uint32_t)numInstances, (uint32_t)startVertex, (uint32_t)startInstance);
683 if (frameStatisticsEnabled) { frameStatisticsAccountDrawCall(numVertexes*numInstances, false); }
684 }
685 else{
686 LOG_ERROR_ONCE(logger, "skip draw()/drawInstanced(), updateRenderPipeline failed.");
687 }
688 }
689
690 void ContextWebGPU::drawInstancedIndexed(PrimitiveType primitiveType, const size_t startInstance, const size_t numInstances, const size_t startIndex, const size_t numIndexes)
691 {
692 updateRenderPass();
693 if (updateRenderPipeline(primitiveType)) {
694 wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, (uint32_t)numIndexes, (uint32_t)numInstances, (uint32_t)startIndex, 0, (uint32_t)startInstance);
695 if (frameStatisticsEnabled) { frameStatisticsAccountDrawCall(numIndexes*numInstances, true); }
696 }
697 else{
698 LOG_ERROR_ONCE(logger, "skip drawInstancedIndexed(), updateRenderPipeline failed.");
699 }
700 }
701
702 void ContextWebGPU::beginRenderPassInt()
703 {
704 assert(renderPassEncoder == 0);
705 assert(computePassEncoder == 0);
706 WGPURenderPassColorAttachment color_attachment[8] = {}; // WGPU_RENDER_PASS_COLOR_ATTACHMENT_INIT
707 WGPURenderPassDepthStencilAttachment ds_attachment = WGPU_RENDER_PASS_DEPTH_STENCIL_ATTACHMENT_INIT;
708 uint32_t color_attachment_count = 0;
709 uint32_t ds_attachment_count = 0;
710
711 WGPULoadOp loadOp = do_clear_render_target ? WGPULoadOp_Clear : WGPULoadOp_Load;
712 do_clear_render_target = false;
713 WGPULoadOp depthLoadOp = do_clear_depth ? WGPULoadOp_Clear : WGPULoadOp_Load;
714 do_clear_depth = false;
715 const char *name = nullptr;
716 if (HandleIsValid(renderTargetHandle) || HandleIsValid(depthStencilHandle)) {
717 name = "Custom Render Pass";
718 if(HandleIsValid(renderTargetHandle)){
719 RenderTargetWebGPU &target = graphicsDevice->renderTargets.render_targets[renderTargetHandle];
720 for(size_t i=0; i<target.count; i++){
721 WGPURenderPassColorAttachment &att = color_attachment[i];
722 att.view = target.view[i];
723 att.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
724 att.resolveTarget = nullptr;
725 att.loadOp = loadOp;
726 att.storeOp = WGPUStoreOp_Store;
727 att.clearValue = clearColor[i];
728 }
729 color_attachment_count = target.count;
730 }
731 if(HandleIsValid(depthStencilHandle)){
732 DepthStencilTargetWebGPU &target = graphicsDevice->renderTargets.depth_stencil_targets[depthStencilHandle];
733 WGPURenderPassDepthStencilAttachment &att = ds_attachment;
734 att.view = target.view;
735 att.depthLoadOp = depthLoadOp;
736 att.depthStoreOp = WGPUStoreOp_Store;
737 att.depthClearValue = clearDepthVal;
738 att.depthReadOnly = false;
739 att.stencilLoadOp = WGPULoadOp_Undefined;
740 att.stencilStoreOp = WGPUStoreOp_Undefined;
741 att.stencilClearValue = 0;
742 att.stencilReadOnly = false;
743 ds_attachment_count = 1;
744 }
745 }
746 else{
747 name = "Default Render Pass";
748 SwapChainWebGPU &defaultSwapChain = graphicsDevice->defaultSwapChain;
749 if(defaultSwapChain.samples > 1){
750 color_attachment[0].view = defaultSwapChain.colorBufferView;
751 color_attachment[0].resolveTarget = defaultSwapChain.resolveView;
752 }
753 else{
754 color_attachment[0].view = defaultSwapChain.resolveView;
755 }
756 color_attachment[0].depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
757 color_attachment[0].loadOp = loadOp;
758 color_attachment[0].storeOp = WGPUStoreOp_Store;
759 color_attachment[0].clearValue = clearColor[0];
760 color_attachment_count = 1;
761
762 ds_attachment.view = defaultSwapChain.depthBufferView;
763 ds_attachment.depthLoadOp = depthLoadOp;
764 ds_attachment.depthStoreOp = WGPUStoreOp_Store;
765 ds_attachment.depthClearValue = clearDepthVal;
766 ds_attachment.depthReadOnly = false;
767 ds_attachment.stencilLoadOp = WGPULoadOp_Undefined;
768 ds_attachment.stencilStoreOp = WGPUStoreOp_Undefined;
769 ds_attachment.stencilClearValue = 0;
770 ds_attachment.stencilReadOnly = false;
771 ds_attachment_count = 1;
772 }
773
774 if(graphicsDevice->use_error_scope){
775 wgpuDevicePushErrorScope(graphicsDevice->device, graphicsDevice->filter);
776 }
777
778 WGPURenderPassDescriptor render_pass_desc = WGPU_RENDER_PASS_DESCRIPTOR_INIT;
779 render_pass_desc.label = {name, WGPU_STRLEN};
780 if(color_attachment_count){
781 render_pass_desc.colorAttachmentCount = color_attachment_count;
782 render_pass_desc.colorAttachments = color_attachment;
783 }
784 if(ds_attachment_count){
785 render_pass_desc.depthStencilAttachment = &ds_attachment;
786 }
787 render_pass_desc.occlusionQuerySet = nullptr;
788 // render_pass_desc.timestampWriteCount = 0;
789 render_pass_desc.timestampWrites = nullptr;
790 renderPassEncoder = wgpuCommandEncoderBeginRenderPass(graphicsDevice->commandEncoder, &render_pass_desc);
791 }
792
793 void ContextWebGPU::endRenderPassInt()
794 {
795 assert(!inRenderPass);
796 if(!renderPassEncoder) return;
797 wgpuRenderPassEncoderEnd(renderPassEncoder);
798 wgpuRenderPassEncoderRelease(renderPassEncoder);
799 renderPassEncoder = 0;
800 update_render_target = true;
801
802// for (uint32_t g = 0; g < MAX_BIND_GROUPS; g++) {
803// current_bind_groups[g] = {};
804// update_descriptors[g] = true;
805// descriptors[g].clear();
806// direct_bind_group_active[g] = false;
807// }
808
809 if(graphicsDevice->use_error_scope){
810 WGPUPopErrorScopeCallbackInfo info = WGPU_POP_ERROR_SCOPE_CALLBACK_INFO_INIT;
811 info.mode = WGPUCallbackMode_AllowSpontaneous;
812 info.callback = context_error_callback;
813 info.userdata1 = this;
814 info.userdata2 = graphicsDevice;
815 WGPUFuture future = wgpuDevicePopErrorScope(graphicsDevice->device, info);
816 // WGPUWaitStatus wgpuInstanceWaitAny(WGPUInstance instance, size_t futureCount, WGPUFutureWaitInfo * futures, uint64_t timeoutNS);
817 }
818 }
819
820 void ContextWebGPU::updateRenderPass()
821 {
822 if(update_render_target){
823 endComputePass();
824 endRenderPassInt();
825 beginRenderPassInt();
826 update_render_target = false;
827 }
828 }
829
830 bool ContextWebGPU::updateRenderPipeline(PrimitiveType primitiveType)
831 {
832 assert(renderPassEncoder);
833 PipelineStatesWebGPU &pipeline_states = graphicsDevice->pipeline_states;
834 bool reload = false;
835 if(HandleIsValid(currentRenderPipeline)){
836 const RenderPipelineWebGPU& pipelineState = pipeline_states.renderPipeline[currentRenderPipeline];
837 size_t hash = pipeline_states.renderPipelineHash(effect,
838 inputLayoutHandle,
839 primitiveType,
840 rasterizeStateHandle,
841 depthStencilStateHandle,
842 blendStateHandle,
843 renderTargetHandle,
844 depthStencilHandle,
845 currentIndexFormat);
846 if(hash != pipelineState.hash) reload = true;
847 }
848 else{
849 reload = true;
850 }
851 if(reload){
852 RenderPipelineHandle pipelineStateHandle = pipeline_states.loadRenderPipeline
853 (
854 effect,
855 inputLayoutHandle,
856 primitiveType,
857 rasterizeStateHandle,
858 depthStencilStateHandle,
859 blendStateHandle,
860 renderTargetHandle,
861 depthStencilHandle,
862 currentIndexFormat
863 );
864 // RenderPipelineWebGPU& pipelineState = pipeline_states.renderPipeline[pipelineStateHandle];
865 currentRenderPipeline = pipelineStateHandle;
866 }
867 if(HandleIsValid(currentRenderPipeline)){
868 RenderPipelineWebGPU &pipelineState = pipeline_states.renderPipeline[currentRenderPipeline];
869 wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelineState.pipeline);
870 }
871 for (uint16_t g = 0; g < getEffects()->getNumBindGroups(effect); g++) {
872 // Groups the current effect leaves empty are kept alive for reuse by a later effect, but must not
873 // be bound: the pipeline layout holds an empty layout in that slot.
874 if (getEffects()->getBindGroupDescription(effect, g).numEntries == 0) continue;
875 if (HandleIsValid(bind_group_handles[g])) {
876 WGPUBindGroup nativeGroup = getBindGroups()->getNativeBindGroup(bind_group_handles[g]);
877 if (!nativeGroup) {
878 LOG_ERROR_ONCE(logger, "Failed to get native bind group for group %d", g);
879 return false;
880 }
881 wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, g, nativeGroup, 0, nullptr);
882 } else {
883 LOG_ERROR_ONCE(logger, "Required bind group %d is missing for effect", g);
884 return false;
885 }
886 }
887 return true;
888 }
889
890 void ContextWebGPU::dispatchCompute(const unsigned int threadGroupsX, const unsigned int threadGroupsY, const unsigned int threadGroupsZ)
891 {
892 updateComputePass();
893 if (!updateComputePipeline()) return;
894 wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, threadGroupsX, threadGroupsY, threadGroupsZ);
895 }
896
897 void ContextWebGPU::beginComputePass()
898 {
899 assert(renderPassEncoder == 0);
900 assert(computePassEncoder == 0);
901 WGPUComputePassDescriptor compute_pass_desc = WGPU_COMPUTE_PASS_DESCRIPTOR_INIT;
902 compute_pass_desc.label = {"Compute Pass", WGPU_STRLEN};
903 // compute_pass_desc.timestampWriteCount = 0;
904 compute_pass_desc.timestampWrites = nullptr;
905 computePassEncoder = wgpuCommandEncoderBeginComputePass(graphicsDevice->commandEncoder, &compute_pass_desc);
906 }
907
908 void ContextWebGPU::endComputePass()
909 {
910 if(!computePassEncoder) return;
911 wgpuComputePassEncoderEnd(computePassEncoder);
912 wgpuComputePassEncoderRelease(computePassEncoder);
913 computePassEncoder = 0;
914 }
915
916 void ContextWebGPU::updateComputePass()
917 {
918 if(!computePassEncoder){
919 endRenderPassInt();
920 beginComputePass();
921 update_render_target = true;
922 }
923 }
924
925 bool ContextWebGPU::updateComputePipeline()
926 {
927 assert(computePassEncoder);
928 PipelineStatesWebGPU &pipeline_states = graphicsDevice->pipeline_states;
929 bool reload = false;
930 if(HandleIsValid(currentComputePipeline)){
931 const ComputePipelineWebGPU& pipelineState = pipeline_states.computePipeline[currentComputePipeline];
932 if(pipelineState.effect != effect) reload = true;
933 }
934 else{
935 reload = true;
936 }
937 if(reload){
938 ComputePipelineHandle pipelineStateHandle = pipeline_states.loadComputePipeline(effect);
939 // ComputePipelineWebGPU& pipelineState = pipeline_states.computePipeline[pipelineStateHandle];
940 currentComputePipeline = pipelineStateHandle;
941 }
942 if(HandleIsValid(currentComputePipeline)){
943 ComputePipelineWebGPU &pipelineState = pipeline_states.computePipeline[currentComputePipeline];
944 wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelineState.pipeline);
945 }
946 else {
947 LOG_ERROR_ONCE(logger, "Failed to load compute pipeline");
948 return false;
949 }
950 for (uint16_t g = 0; g < getEffects()->getNumBindGroups(effect); g++) {
951 // Groups the current effect leaves empty are kept alive for reuse by a later effect, but must not
952 // be bound: the pipeline layout holds an empty layout in that slot.
953 if (getEffects()->getBindGroupDescription(effect, g).numEntries == 0) continue;
954 if (HandleIsValid(bind_group_handles[g])) {
955 WGPUBindGroup nativeGroup = getBindGroups()->getNativeBindGroup(bind_group_handles[g]);
956 if (!nativeGroup) {
957 LOG_ERROR_ONCE(logger, "Failed to get native bind group for group %d", g);
958 return false;
959 }
960 wgpuComputePassEncoderSetBindGroup(computePassEncoder, g, nativeGroup, 0, nullptr);
961 } else {
962 LOG_ERROR_ONCE(logger, "Required bind group %d is missing for compute effect", g);
963 return false;
964 }
965 }
966 return true;
967 }
968
969 void ContextWebGPU::readDepthBuffer(BufferHandle bufferHandle, int x, int y, int width, int height, Framebuffer)
970 {
971 const WGPUCommandEncoder &commandEncoder = graphicsDevice->commandEncoder;
972 TexturesWebGPU &textures = graphicsDevice->textures;
973 BufferWebGPU& buffer = graphicsDevice->buffers.buffers[bufferHandle];
974
975 endRenderPassInt();
976
977 WGPUTexelCopyTextureInfo src = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT;
978 if (HandleIsValid(renderTargetHandle) || HandleIsValid(depthStencilHandle)) {
979 if(!HandleIsValid(depthStencilHandle)) return;
980 DepthStencilTargetWebGPU &target = graphicsDevice->renderTargets.depth_stencil_targets[depthStencilHandle];
981 TextureWebGPU &src_tex = textures.textures[target.textureHandle];
982 src.texture = src_tex.texture;
983 }
984 else{
985 SwapChainWebGPU &defaultSwapChain = graphicsDevice->defaultSwapChain;
986 src.texture = defaultSwapChain.depthBufferTexture;
987 }
988 src.mipLevel = 0;
989 src.origin = {(uint32_t)x, (uint32_t)y, 0};
990 src.aspect = WGPUTextureAspect_DepthOnly;
991 WGPUTexelCopyBufferInfo dst = WGPU_TEXEL_COPY_BUFFER_INFO_INIT;
992 dst.layout.offset = 0;
993 dst.layout.bytesPerRow = (uint32_t)buffer.size;
994 dst.layout.rowsPerImage = 1;
995 dst.buffer = buffer.buffer;
996 WGPUExtent3D size = WGPU_EXTENT_3D_INIT;
997 size.width = width;
998 size.height = height;
999 size.depthOrArrayLayers = 1;
1000 wgpuCommandEncoderCopyTextureToBuffer(commandEncoder, &src, &dst, &size);
1001 }
1002
1003 void ContextWebGPU::readColorBuffer(BufferHandle bufferHandle, int x, int y, int width, int height, Framebuffer)
1004 {
1005 const WGPUCommandEncoder &commandEncoder = graphicsDevice->commandEncoder;
1006 TexturesWebGPU &textures = graphicsDevice->textures;
1007 BufferWebGPU& buffer = graphicsDevice->buffers.buffers[bufferHandle];
1008
1009 endRenderPassInt();
1010
1011 WGPUTexelCopyTextureInfo src = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT;
1012 if (HandleIsValid(renderTargetHandle) || HandleIsValid(depthStencilHandle)) {
1013 if(!HandleIsValid(renderTargetHandle)) return;
1014 RenderTargetWebGPU& target = graphicsDevice->renderTargets.render_targets[renderTargetHandle];
1015 TextureWebGPU &src_tex = textures.textures[target.textureHandle[0]];
1016 src.texture = src_tex.texture;
1017 }
1018 else{
1019 SwapChainWebGPU &defaultSwapChain = graphicsDevice->defaultSwapChain;
1020#ifdef __EMSCRIPTEN__
1021 // assert(false); // TODO ...
1022 src.texture = defaultSwapChain.colorBufferTexture;
1023#else
1024 src.texture = defaultSwapChain.resolveTexture;
1025#endif
1026 }
1027 src.mipLevel = 0;
1028 src.origin = {(uint32_t)x, (uint32_t)y, 0};
1029 src.aspect = WGPUTextureAspect_All;
1030 WGPUTexelCopyBufferInfo dst = WGPU_TEXEL_COPY_BUFFER_INFO_INIT;
1031 dst.layout.offset = 0;
1032 dst.layout.bytesPerRow = (uint32_t)buffer.size;
1033 dst.layout.rowsPerImage = 1;
1034 dst.buffer = buffer.buffer;
1035 WGPUExtent3D size = WGPU_EXTENT_3D_INIT;
1036 size.width = width;
1037 size.height = height;
1038 size.depthOrArrayLayers = 1;
1039 wgpuCommandEncoderCopyTextureToBuffer(commandEncoder, &src, &dst, &size);
1040 }
1041
1042 void *ContextWebGPU::map(BufferHandle handle, MapMode::EMapMode mapMode, uint32_t* stride)
1043 {
1044 assert(stride == nullptr || stride[0] == 0);
1045 BufferWebGPU &buffer = graphicsDevice->buffers.buffers[handle];
1046 if(buffer.is_read_buffer){
1047 assert(mapMode == MapMode::Read);
1048 uint32_t offset = 0;
1049 WGPUBufferMapCallback callback = nullptr;
1050 void * userdata = nullptr;
1051 WGPUBufferMapCallbackInfo callbackInfo = WGPU_BUFFER_MAP_CALLBACK_INFO_INIT;
1052 callbackInfo.callback = callback;
1053 callbackInfo.mode = WGPUCallbackMode_AllowProcessEvents;
1054 callbackInfo.nextInChain = nullptr;
1055 callbackInfo.userdata1 = userdata;
1056 wgpuBufferMapAsync(buffer.buffer, WGPUMapMode_Read, offset, buffer.size, callbackInfo);
1057#ifndef __EMSCRIPTEN__
1058 WGPUBufferMapState state = wgpuBufferGetMapState(buffer.buffer);
1059 while(state != WGPUBufferMapState_Mapped){
1060 // TODO this is bad... (Redesign for callback?)
1061 assert(state == WGPUBufferMapState_Pending);
1062 wgpuInstanceProcessEvents(graphicsDevice->instance);
1063 wgpuDeviceTick(graphicsDevice->device);
1064 state = wgpuBufferGetMapState(buffer.buffer);
1065 }
1066#else
1067 assert(false);
1068#endif
1069
1070 return (void*)wgpuBufferGetConstMappedRange(buffer.buffer, 0, buffer.size);
1071 }
1072 else{
1073 assert(mapMode == MapMode::WriteDiscard);
1074 buffer.NextInstance(graphicsDevice, graphicsDevice->buffers);
1075 buffer.map = new char[buffer.size];
1076 return buffer.map;
1077 }
1078 }
1079
1080 void *ContextWebGPU::map(TextureHandle /*textureHandle*/, MapMode::EMapMode /*accessMode*/, uint32_t* /*rowPitch*/, uint32_t* /*depthPitch*/)
1081 {
1082 assert(false); // TODO
1083 return nullptr;
1084 }
1085
1087 {
1088 BufferWebGPU &buffer = graphicsDevice->buffers.buffers[handle];
1089 if(buffer.is_read_buffer){
1090 // WGPUBufferMapState state = wgpuBufferGetMapState(buffer.buffer);
1091 // if(state == WGPUBufferMapState_Mapped)
1092 wgpuBufferUnmap(buffer.buffer);
1093 }
1094 else{
1095 if(buffer.size > 0){
1096 wgpuQueueWriteBuffer(graphicsDevice->queue, buffer.buffer, 0, buffer.map, buffer.size);
1097 if(uploadStatisticsEnabled) uploadStatisticsBufferUpload(buffer.size);
1098 }
1099 delete [] (char*)buffer.map;
1100 buffer.map = nullptr;
1101 }
1102 }
1103
1104 void ContextWebGPU::unmap(TextureHandle /*textureHandle*/)
1105 {
1106 assert(false); // TODO
1107 }
1108
1109 void ContextWebGPU::updateBuffer(BufferHandle handle, const void* data, const size_t size)
1110 {
1111 BufferWebGPU &buffer = graphicsDevice->buffers.buffers[handle];
1112 assert(!buffer.is_read_buffer);
1113 if(size == 0) return;
1114 if(size > buffer.size){
1115 LOG_ERROR(logger, "updateBuffer: write size (%zu) exceeds buffer size (%zu)", size, buffer.size);
1116 return;
1117 }
1118 buffer.NextInstance(graphicsDevice, graphicsDevice->buffers);
1119 wgpuQueueWriteBuffer(graphicsDevice->queue, buffer.buffer, 0, data, size);
1120 if(uploadStatisticsEnabled) uploadStatisticsBufferUpload(size);
1121 }
1122
1123 void ContextWebGPU::updateSubTexture(TextureHandle textureHandle, const size_t level, const void* data)
1124 {
1125 assert(HandleIsValid(textureHandle) && "Texture handle is invalid.");
1126
1127 TextureWebGPU & texture = graphicsDevice->textures.textures[textureHandle];
1128
1129 const uint32_t width = Cogs::getMipSize(texture.width, static_cast<uint32_t>(level));
1130 const uint32_t height = Cogs::getMipSize(texture.height, static_cast<uint32_t>(level));
1131
1132 WGPUTexelCopyTextureInfo destinationTexture;
1133 destinationTexture.texture = texture.texture;
1134 destinationTexture.mipLevel = static_cast<uint32_t>(level);
1135 destinationTexture.aspect = WGPUTextureAspect_All;
1136 destinationTexture.origin = { 0, 0, 0 };
1137
1138 const Cogs::FormatInfo *textureFormat = Cogs::getFormatInfo(texture.format);
1139
1140 WGPUTexelCopyBufferLayout sourceLayout;
1141 sourceLayout.offset = 0;
1142 sourceLayout.bytesPerRow = textureFormat->blockSize * ((width + textureFormat->blockExtent.width - 1) / textureFormat->blockExtent.width);
1143 sourceLayout.rowsPerImage = (height + textureFormat->blockExtent.height - 1) / textureFormat->blockExtent.height;
1144
1145 size_t size = sourceLayout.bytesPerRow * sourceLayout.rowsPerImage;
1146
1147 WGPUExtent3D extent;
1148 extent.width = width;
1149 extent.height = height;
1150 extent.depthOrArrayLayers = 1;
1151
1152 wgpuQueueWriteTexture(graphicsDevice->queue, &destinationTexture, data, static_cast<size_t>(size), &sourceLayout, &extent);
1153
1154 if(uploadStatisticsEnabled) uploadStatisticsTextureUpload(texture.estimatedByteSize); // TODO size?
1155 }
1156
1157 void ContextWebGPU::updateSubBuffer(BufferHandle handle, const size_t offset, const size_t size, const void* data)
1158 {
1159 BufferWebGPU &buffer = graphicsDevice->buffers.buffers[handle];
1160 if(size == 0) return;
1161 assert(!buffer.is_read_buffer);
1162 if(offset + size > buffer.size){
1163 LOG_ERROR(logger, "updateSubBuffer: write range (offset: %zu, size: %zu) exceeds buffer size (%zu)", offset, size, buffer.size);
1164 return;
1165 }
1166 assert(buffer.alias_idx == 0); // TODO Copy over alias buffer data...
1167 buffer.alias_idx++;
1168 wgpuQueueWriteBuffer(graphicsDevice->queue, buffer.buffer, offset, data, size);
1169 if(uploadStatisticsEnabled) uploadStatisticsBufferUpload(size);
1170 }
1171
1173 {
1174 assert(false); // TODO
1175 }
1176
1177 void ContextWebGPU::copyResource(BufferHandle /*destinationHandle*/, BufferHandle /*sourceHandle*/)
1178 {
1179 assert(false); // TODO
1180 }
1181
1182 void ContextWebGPU::copyResource(TextureHandle /*destinationHandle*/, TextureHandle /*sourceHandle*/)
1183 {
1184 assert(false); // TODO
1185 }
1186
1187 void ContextWebGPU::copyTexture(TextureHandle dstHandle, unsigned dstSub, unsigned dstX, unsigned dstY, unsigned dstZ, TextureHandle srcHandle, unsigned srcSub)
1188 {
1189 graphicsDevice->maybeCreateUploadCommandEncoder();
1190 const WGPUCommandEncoder &commandEncoder = graphicsDevice->commandEncoder;
1191 TexturesWebGPU &textures = graphicsDevice->textures;
1192
1193 TextureWebGPU &src = textures.textures[srcHandle];
1194 TextureWebGPU &dst = textures.textures[dstHandle];
1195
1196 uint32_t srcLevel = 0; // TODO srcSub%LevelCount
1197 uint32_t srcLayer = srcSub; // TODO srcSub/LevelCount
1198
1199 WGPUTexelCopyTextureInfo copy_src = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT;
1200 copy_src.texture = src.texture;
1201 copy_src.mipLevel = srcLevel;
1202 copy_src.origin = {0, 0, srcLayer};
1203 copy_src.aspect = WGPUTextureAspect_All;
1204
1205 uint32_t dstLevel = 0; // TODO dstSub%LevelCount
1206 uint32_t dstLayer = dstSub; // TODO dstSub/LevelCount
1207
1208 WGPUTexelCopyTextureInfo copy_dst = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT;
1209 copy_dst.texture = dst.texture;
1210 copy_dst.mipLevel = dstLevel;
1211 copy_dst.origin = {dstX, dstY, dstZ+dstLayer};
1212 copy_dst.aspect = WGPUTextureAspect_All;
1213
1214 WGPUExtent3D size = WGPU_EXTENT_3D_INIT;
1215 size.width = src.width;
1216 size.height = src.height;
1217 size.depthOrArrayLayers = 1;
1218 wgpuCommandEncoderCopyTextureToTexture(commandEncoder, &copy_src, &copy_dst, &size);
1219 }
1220
1221 void ContextWebGPU::clearResource(BufferHandle /*destinationHandle*/, uint32_t* /*values*/)
1222 {
1223 assert(false); // TODO
1224 }
1225
1226 void ContextWebGPU::clearResource(BufferHandle /*destinationHandle*/, float* /*values*/)
1227 {
1228 assert(false); // TODO
1229 }
1230
1231 void ContextWebGPU::beginFrame() {
1232 prevStats = currStats;
1233 currStats = FrameStatistics{};
1234
1235 prevUploadStats = currUploadStats;
1236 currUploadStats = UploadStatistics{};
1237 }
1238
1239 void ContextWebGPU::frameStatisticsAccountDrawCall(size_t count, bool indexed)
1240 {
1241 // bucket is position of most significant bit, zero is zero.
1242 unsigned bucket = 0;
1243 size_t t = count;
1244 if ((t & 0xFFFF0000) != 0) { bucket += 16; t = t >> 16u; }
1245 if ((t & 0x0000FF00) != 0) { bucket += 8; t = t >> 8u; }
1246 if ((t & 0x000000F0) != 0) { bucket += 4; t = t >> 4u; }
1247 if ((t & 0x0000000C) != 0) { bucket += 2; t = t >> 2u; }
1248 if ((t & 0x00000002) != 0) { bucket += 1; }
1249
1250#ifdef _DEBUG
1251 assert(uint64_t(count) < (uint64_t(1) << (bucket + 1)));
1252 if (count) {
1253 assert((uint64_t(1) << bucket) <= uint64_t(count));
1254 }
1255 else {
1256 assert(bucket == 0);
1257 }
1258#endif
1259
1260 currStats.drawCallHistogram[bucket]++;
1261 currStats.vertices += count;
1262 if (indexed) currStats.indices += count;
1263 }
1264
1265 void ContextWebGPU::uploadStatisticsBufferUpload(size_t size)
1266 {
1267 currUploadStats.bufferUploads++;
1268 currUploadStats.bufferUploadSize += size;
1269 }
1270
1271 void ContextWebGPU::uploadStatisticsTextureUpload(size_t size)
1272 {
1273 currUploadStats.textureUploads++;
1274 currUploadStats.textureUploadSize += size;
1275 }
1276
1277 EffectHandle ContextWebGPU::getCurrentEffect()
1278 {
1279 return effect;
1280 }
1281
1282 EffectsWebGPU *ContextWebGPU::getEffects()
1283 {
1284 return &graphicsDevice->effects;
1285 }
1286
1287 BindGroupsWebGPU* ContextWebGPU::getBindGroups()
1288 {
1289 return &graphicsDevice->bindGroups;
1290 }
1291
1292 void ContextWebGPU::ClearBindGroups()
1293 {
1294 for (uint32_t g = 0; g < MAX_BIND_GROUPS; g++) {
1295 if (bind_group_owned_by_context[g]) {
1296 getBindGroups()->releaseBindGroup(bind_group_handles[g]);
1297 bind_group_owned_by_context[g] = false;
1298 }
1299 bind_group_handles[g] = BindGroupHandle::NoHandle;
1300 }
1301 }
1302}
virtual void setSamplerState(const StringView &, unsigned int, SamplerStateHandle) override
Sets the sampler slot given by unit with the given name to contain the given sampler state.
virtual void pushCommandGroupAnnotation(const StringView &name) override
Begin to tag a sequence of commands as a group in graphics debugger.
virtual void getBufferCounter(BufferHandle, BufferHandle) override
Get the associated counter of a buffer.
virtual void setAnnotationMarker(const StringView &name) override
Add a tag in the sequence of commands in graphics debugger.
virtual void setEffect(EffectHandle) override
Set the current effect.
virtual void draw(PrimitiveType, const size_t, const size_t) override
Draws non-indexed, non-instanced primitives.
virtual void readColorBuffer(BufferHandle, int, int, int, int, Framebuffer) override
Reads data from the current render target into the given bufferHandle.
virtual void resolveResource(TextureHandle, TextureHandle) override
Resolves the given source resource target into the given destination texture.
virtual void clearDepth(const float=1.0f) override
Clear the currently set depth/stencil target to the given depth.
virtual void setBlendState(const BlendStateHandle, const float *) override
Set the current blend state.
virtual void updateSubBuffer(BufferHandle, const size_t, const size_t, const void *) override
Update a region of data in a buffer.
virtual void setVertexBuffers(const VertexBufferHandle *, const size_t, const uint32_t *, const uint32_t *) override
Sets the current vertex buffers.
virtual void readDepthBuffer(BufferHandle, int, int, int, int, Framebuffer) override
Reads data from the current depth target into the given bufferHandle.
virtual void drawInstanced(PrimitiveType, const size_t, const size_t, const size_t, const size_t) override
Draws non-indexed, instanced primitives.
virtual void setBuffer(const StringView &, BufferHandle) override
Sets the given buffer to the buffer binding slot with the given name.
virtual void popCommandGroupAnnotation() override
End to tag a sequence of commands as a group in graphics debugger.
virtual void dispatchCompute(const unsigned int, const unsigned int, const unsigned int) override
Dispatch computing work on the graphics device using the desired thread group count.
virtual void setConstantBuffer(const StringView &, const BufferHandle, const uint32_t=0, const uint32_t=~0u) override
Sets a constant buffer to be bound to the given name and slot.
virtual void * map(BufferHandle, MapMode::EMapMode, uint32_t *=nullptr) override
Maps the given buffer so it can be accessed.
virtual void setBufferCounter(BufferHandle, uint32_t) override
Set the associated counter of a buffer.
virtual void unmap(BufferHandle) override
Unmaps the given buffer, applying any synchronization necessary to reflect changes in the mapped memo...
virtual void drawIndexed(PrimitiveType, const size_t, const size_t, const size_t=0) override
Draws indexed, non-instanced primitives.
virtual void setDepthStencilState(const DepthStencilStateHandle) override
Set the current depth stencil state.
virtual void setScissor(const int, const int, const int, const int) override
Sets the current scissor rectangle.
virtual void setRasterizerState(const RasterizerStateHandle) override
Set the current rasterizer state.
virtual void endRenderPass() override
End a render pass.
virtual void drawInstancedIndexed(PrimitiveType, const size_t, const size_t, const size_t, const size_t) override
Draws indexed, instanced primitives.
virtual void updateSubTexture(TextureHandle, const size_t, const void *) override
Update the data of a level in the given texture.
virtual void beginRenderPass(const RenderPassInfo &info) override
Begin a render pass.
virtual void setVertexArrayObject(VertexArrayObjectHandle) override
Sets vertexBuffers and index buffers using a prevalidated vertex array object.
virtual void signal(FenceHandle) override
Insert a fence in the command stream that will signal when all commands before the fence are complete...
virtual void setIndexBuffer(IndexBufferHandle, uint32_t=4, uint32_t=0) override
Sets the current index buffer.
virtual void clearRenderTarget(const float *) override
Clear the currently set render target to the given value (4 component floating point RGBA).
virtual void setTexture(const StringView &, unsigned int, TextureHandle) override
Sets the texture slot given by unit with the given name to contain the given texture.
virtual void setInputLayout(const InputLayoutHandle) override
Sets the current input layout.
virtual void setRenderTarget(const RenderTargetHandle handle, const DepthStencilHandle depthStencilHandle) override
Sets the current render target and an associated depth stencil target.
virtual void setViewport(const float, const float, const float, const float) override
Sets the current viewport to the given location and dimensions.
virtual void updateBuffer(BufferHandle, const void *, const size_t) override
Replace contents of buffer with new data.
uint32_t getNumBindGroups(EffectHandle effectHandle) override
Query the number of bind-group layouts exposed by the given effect.
void releaseBufferBinding(BufferBindingHandle) override
Release a handle to a buffer binding.
BufferBindingHandle getBufferBinding(EffectHandle effectHandle, const StringView &name) override
Get a handle to a buffer binding.
virtual bool getSize(int &w, int &h) const override
Retrieve the size previously set by setSize.
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
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62
Framebuffer
Framebuffers to select from when doing framebuffer operations.
Definition: Common.h:147
PrimitiveType
Primitive types for interpreting vertex data sent to the graphics pipeline.
Definition: Common.h:112
uint8_t blockSize
Bytesize of one block of data.
Definition: DataFormat.h:261
TextureExtent blockExtent
Number of data items in a block.
Definition: DataFormat.h:262
Handle template class used to provide opaque, non-converting handles.
Definition: Common.h:23
static const Handle_t NoHandle
Represents a handle to nothing.
Definition: Common.h:78
handle_type handle
Internal resource handle.
Definition: Common.h:75
static const Handle_t InvalidHandle
Represents an invalid handle.
Definition: Common.h:81
EMapMode
Mapping mode enumeration.
Definition: Flags.h:93
@ WriteDiscard
Write access. When unmapping the graphics system will discard the old contents of the resource.
Definition: Flags.h:103