1#include "ContextWebGPU.h"
3#include "GraphicsDeviceWebGPU.h"
4#include "PipelineStatesWebGPU.h"
5#include "BindGroupsWebGPU.h"
6#include "Foundation/Logging/Logger.h"
11 void context_error_callback(WGPUPopErrorScopeStatus status, WGPUErrorType type, WGPUStringView message,
void* userdata1,
void* userdata2)
17 if(status == WGPUPopErrorScopeStatus_CallbackCancelled){
18 LOG_ERROR(logger,
"WebGPU context err status: Cancelled");
20 if(status == WGPUPopErrorScopeStatus_Error){
21 LOG_ERROR(logger,
"WebGPU context err status: Error");
23 if(message.data && message.length){
24 LOG_ERROR(logger,
"WebGPU context err (%d) %.*s", type, WGPUStringViewFormat(message));
28 WGPULoadOp ConvWebGPULoadOp(Cogs::LoadOp 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;
35 return WGPULoadOp_Undefined;
37 WGPUStoreOp ConvWebGPUStoreOp(Cogs::StoreOp 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;
44 return WGPUStoreOp_Undefined;
47 void decodeConstantBufferBindingHandle(int64_t handle, uint32_t &group, uint32_t &binding) {
49 group =
static_cast<uint32_t
>(i >> 16);
50 binding = (i & 0xFFFF) - 1;
53 bool operator==(
const WGPUBindGroupEntry &a,
const WGPUBindGroupEntry &b)
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;
65 bool operator!=(
const WGPUBindGroupEntry &a,
const WGPUBindGroupEntry &b)
74 void ContextWebGPU::initialize(GraphicsDeviceWebGPU *graphicsDeviceIn)
76 graphicsDevice = graphicsDeviceIn;
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];
93 do_clear_render_target =
true;
94 update_render_target =
true;
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];
106 for(
size_t i=numvalues; i<
sizeof(clearColor)/
sizeof(clearColor[0]); i++){
107 clearColor[i] = WGPU_COLOR_INIT;
109 do_clear_render_target =
true;
110 update_render_target =
true;
115 assert(!inRenderPass);
116 clearDepthVal = depth;
117 do_clear_depth =
true;
118 update_render_target =
true;
171 WGPUStringView label = {name.
data(), name.
length()};
172 if(renderPassEncoder != 0){
173 wgpuRenderPassEncoderInsertDebugMarker(renderPassEncoder, label);
175 else if(computePassEncoder != 0){
176 wgpuComputePassEncoderInsertDebugMarker(computePassEncoder, label);
182 wgpuCommandEncoderInsertDebugMarker(graphicsDevice->commandEncoder, label);
188 assert(renderPassEncoder == 0);
189 assert(computePassEncoder == 0);
190 assert(!inRenderPass);
192 update_render_target =
false;
194 renderTargetHandle = info.renderTargetHandle;
195 depthStencilHandle = info.depthStencilHandle;
197 WGPURenderPassColorAttachment color_attachment[8] = {};
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;
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;
215 att.resolveTarget =
nullptr;
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];
224 color_attachment_count = target.count;
226 if(HandleIsValid(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;
242 name =
"Default Render Pass";
244 if(defaultSwapChain.samples > 1){
245 color_attachment[0].view = defaultSwapChain.colorBufferView;
246 color_attachment[0].resolveTarget = defaultSwapChain.resolveView;
249 color_attachment[0].view = defaultSwapChain.resolveView;
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;
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;
272 if(graphicsDevice->use_error_scope){
273 wgpuDevicePushErrorScope(graphicsDevice->device, graphicsDevice->filter);
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;
282 if(ds_attachment_count){
283 render_pass_desc.depthStencilAttachment = &ds_attachment;
285 render_pass_desc.occlusionQuerySet =
nullptr;
287 render_pass_desc.timestampWrites =
nullptr;
288 renderPassEncoder = wgpuCommandEncoderBeginRenderPass(graphicsDevice->commandEncoder, &render_pass_desc);
292 assert(renderPassEncoder);
293 assert(inRenderPass);
294 inRenderPass =
false;
295 wgpuRenderPassEncoderEnd(renderPassEncoder);
296 wgpuRenderPassEncoderRelease(renderPassEncoder);
297 renderPassEncoder = 0;
298 update_render_target =
true;
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);
319 if(HandleIsValid(rt_handle))
320 renderTargetHandle = rt_handle;
322 renderTargetHandle = {};
323 if(HandleIsValid(ds_handle))
324 depthStencilHandle = ds_handle;
326 depthStencilHandle = {};
327 rasterizeStateHandle = {};
328 blendStateHandle = {};
329 assert(do_clear_render_target ==
false);
330 assert(do_clear_depth ==
false);
331 do_clear_render_target =
false;
332 do_clear_depth =
false;
333 update_render_target =
true;
339 float minDepth = 0.0f;
340 float maxDepth = 1.0f;
341 wgpuRenderPassEncoderSetViewport(renderPassEncoder, x, y, width, height, minDepth, maxDepth);
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);
358 if(HandleIsValid(handle))
359 depthStencilStateHandle = handle;
361 depthStencilStateHandle = {};
367 if(HandleIsValid(handle))
368 blendStateHandle = handle;
370 blendStateHandle = {};
373 color.r = constants[0];
374 color.g = constants[1];
375 color.b = constants[2];
376 color.a = constants[3];
384 wgpuRenderPassEncoderSetBlendConstant(renderPassEncoder, &color);
389 if(HandleIsValid(handle))
390 rasterizeStateHandle = handle;
392 rasterizeStateHandle = {};
395 void ContextWebGPU::setDefaults() {
396 rasterizeStateHandle = {};
397 depthStencilStateHandle = {};
398 blendStateHandle = {};
403 if(HandleIsValid(handle))
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])) {
412 bind_group_owned_by_context[g] =
false;
415 if (getEffects()->getBindGroupDescription(handle, g).numEntries == 0)
continue;
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]);
422 bind_group_owned_by_context[g] =
false;
432 setTexture(getEffects()->getTextureBinding(getCurrentEffect(), name, unit), textureHandle);
437 if (!HandleIsValid(binding)) {
438 LOG_DEBUG_ONCE(logger,
"Invalid texture binding handle");
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);
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);
456 getBindGroups()->setTexture(bind_group_handles[group], bindGroupBindingHandle, handle);
461 setTexture(getEffects()->getTextureBinding(getCurrentEffect(), name, 1), textureViewHandle);
466 if (!HandleIsValid(binding)) {
467 LOG_DEBUG_ONCE(logger,
"Invalid texture binding handle");
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);
479 if (!createOwnedBindGroup(group)) {
480 LOG_ERROR_ONCE(logger,
"Failed to create owned bindgroup for group %u", group);
484 BindGroupTextureBindingHandle bindGroupBindingHandle =
static_cast<BindGroupTextureBindingHandle
>(decoded_binding + 1);
485 getBindGroups()->setTexture(bind_group_handles[group], bindGroupBindingHandle, handle);
490 setSamplerState(getEffects()->getSamplerStateBinding(getCurrentEffect(), name, unit), handle);
495 if (!HandleIsValid(binding)) {
496 LOG_DEBUG_ONCE(logger,
"Invalid sampler state binding handle");
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);
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);
514 getBindGroups()->setSamplerState(bind_group_handles[group], bindGroupBindingHandle, handle);
519 if(HandleIsValid(handle))
520 inputLayoutHandle = handle;
522 inputLayoutHandle = {};
528 for(
size_t i=0; i<count; i++){
530 uint32_t slot = (uint32_t)i;
532 uint32_t offset = offsets ? offsets[i] : 0;
533 wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, slot, buffer.buffer, offset, buffer.size-offset);
548 WGPUIndexFormat format = (stride == 2) ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32;
549 currentIndexFormat = format;
550 uint64_t offset = offset_in;
552 wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, buffer.buffer, format, offset, buffer.size);
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);
565 if (bind_group_owned_by_context[groupIdx]) {
566 getBindGroups()->releaseBindGroup(bind_group_handles[groupIdx]);
568 bind_group_owned_by_context[groupIdx] =
false;
573 bind_group_handles[groupIdx] = bindGroupHandle;
576 LOG_ERROR_ONCE(logger,
"Failed to set bindgroup");
583 setConstantBuffer(getEffects()->getConstantBufferBinding(getCurrentEffect(), name), bufferHandle, offset, size);
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);
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);
597 BindGroupHandle newBindGroupHandle = getBindGroups()->createBindGroup(&desc);
599 bind_group_handles[group] = newBindGroupHandle;
600 bind_group_owned_by_context[group] =
true;
603 LOG_ERROR_ONCE(logger,
"Failed to create bindgroup for group %u", group);
612 if (!HandleIsValid(binding)) {
613 LOG_DEBUG_ONCE(logger,
"Invalid constant buffer binding handle");
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);
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);
631 getBindGroups()->setConstantBuffer(bind_group_handles[group], bindGroupBindingHandle, handle, offset, size);
663 drawInstanced(primitiveType, startVertex, numVertexes, 0, 1);
669 if (updateRenderPipeline(primitiveType)) {
670 wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, (uint32_t)numIndexes, 1, (uint32_t)startIndex, (int32_t)startVertex, 0);
671 if (frameStatisticsEnabled) { frameStatisticsAccountDrawCall(numIndexes,
true); }
674 LOG_ERROR_ONCE(logger,
"skip drawIndexed(), updateRenderPipeline failed.");
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); }
686 LOG_ERROR_ONCE(logger,
"skip draw()/drawInstanced(), updateRenderPipeline failed.");
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); }
698 LOG_ERROR_ONCE(logger,
"skip drawInstancedIndexed(), updateRenderPipeline failed.");
702 void ContextWebGPU::beginRenderPassInt()
704 assert(renderPassEncoder == 0);
705 assert(computePassEncoder == 0);
706 WGPURenderPassColorAttachment color_attachment[8] = {};
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;
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;
726 att.storeOp = WGPUStoreOp_Store;
727 att.clearValue = clearColor[i];
729 color_attachment_count = target.count;
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;
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;
754 color_attachment[0].view = defaultSwapChain.resolveView;
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;
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;
774 if(graphicsDevice->use_error_scope){
775 wgpuDevicePushErrorScope(graphicsDevice->device, graphicsDevice->filter);
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;
784 if(ds_attachment_count){
785 render_pass_desc.depthStencilAttachment = &ds_attachment;
787 render_pass_desc.occlusionQuerySet =
nullptr;
789 render_pass_desc.timestampWrites =
nullptr;
790 renderPassEncoder = wgpuCommandEncoderBeginRenderPass(graphicsDevice->commandEncoder, &render_pass_desc);
793 void ContextWebGPU::endRenderPassInt()
795 assert(!inRenderPass);
796 if(!renderPassEncoder)
return;
797 wgpuRenderPassEncoderEnd(renderPassEncoder);
798 wgpuRenderPassEncoderRelease(renderPassEncoder);
799 renderPassEncoder = 0;
800 update_render_target =
true;
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);
820 void ContextWebGPU::updateRenderPass()
822 if(update_render_target){
825 beginRenderPassInt();
826 update_render_target =
false;
830 bool ContextWebGPU::updateRenderPipeline(
PrimitiveType primitiveType)
832 assert(renderPassEncoder);
833 PipelineStatesWebGPU &pipeline_states = graphicsDevice->pipeline_states;
836 const RenderPipelineWebGPU& pipelineState = pipeline_states.renderPipeline[currentRenderPipeline];
837 size_t hash = pipeline_states.renderPipelineHash(effect,
840 rasterizeStateHandle,
841 depthStencilStateHandle,
846 if(
hash != pipelineState.hash) reload =
true;
852 RenderPipelineHandle pipelineStateHandle = pipeline_states.loadRenderPipeline
857 rasterizeStateHandle,
858 depthStencilStateHandle,
865 currentRenderPipeline = pipelineStateHandle;
868 RenderPipelineWebGPU &pipelineState = pipeline_states.renderPipeline[currentRenderPipeline];
869 wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelineState.pipeline);
874 if (getEffects()->getBindGroupDescription(effect, g).numEntries == 0)
continue;
876 WGPUBindGroup nativeGroup = getBindGroups()->getNativeBindGroup(bind_group_handles[g]);
878 LOG_ERROR_ONCE(logger,
"Failed to get native bind group for group %d", g);
881 wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, g, nativeGroup, 0,
nullptr);
883 LOG_ERROR_ONCE(logger,
"Required bind group %d is missing for effect", g);
893 if (!updateComputePipeline())
return;
894 wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, threadGroupsX, threadGroupsY, threadGroupsZ);
897 void ContextWebGPU::beginComputePass()
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};
904 compute_pass_desc.timestampWrites =
nullptr;
905 computePassEncoder = wgpuCommandEncoderBeginComputePass(graphicsDevice->commandEncoder, &compute_pass_desc);
908 void ContextWebGPU::endComputePass()
910 if(!computePassEncoder)
return;
911 wgpuComputePassEncoderEnd(computePassEncoder);
912 wgpuComputePassEncoderRelease(computePassEncoder);
913 computePassEncoder = 0;
916 void ContextWebGPU::updateComputePass()
918 if(!computePassEncoder){
921 update_render_target =
true;
925 bool ContextWebGPU::updateComputePipeline()
927 assert(computePassEncoder);
928 PipelineStatesWebGPU &pipeline_states = graphicsDevice->pipeline_states;
931 const ComputePipelineWebGPU& pipelineState = pipeline_states.computePipeline[currentComputePipeline];
932 if(pipelineState.effect != effect) reload =
true;
938 ComputePipelineHandle pipelineStateHandle = pipeline_states.loadComputePipeline(effect);
940 currentComputePipeline = pipelineStateHandle;
943 ComputePipelineWebGPU &pipelineState = pipeline_states.computePipeline[currentComputePipeline];
944 wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelineState.pipeline);
947 LOG_ERROR_ONCE(logger,
"Failed to load compute pipeline");
953 if (getEffects()->getBindGroupDescription(effect, g).numEntries == 0)
continue;
955 WGPUBindGroup nativeGroup = getBindGroups()->getNativeBindGroup(bind_group_handles[g]);
957 LOG_ERROR_ONCE(logger,
"Failed to get native bind group for group %d", g);
960 wgpuComputePassEncoderSetBindGroup(computePassEncoder, g, nativeGroup, 0,
nullptr);
962 LOG_ERROR_ONCE(logger,
"Required bind group %d is missing for compute effect", g);
971 const WGPUCommandEncoder &commandEncoder = graphicsDevice->commandEncoder;
973 BufferWebGPU& buffer = graphicsDevice->buffers.buffers[bufferHandle];
977 WGPUTexelCopyTextureInfo src = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT;
978 if (HandleIsValid(renderTargetHandle) || HandleIsValid(depthStencilHandle)) {
979 if(!HandleIsValid(depthStencilHandle))
return;
981 TextureWebGPU &src_tex = textures.textures[target.textureHandle];
982 src.texture = src_tex.texture;
986 src.texture = defaultSwapChain.depthBufferTexture;
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;
998 size.height = height;
999 size.depthOrArrayLayers = 1;
1000 wgpuCommandEncoderCopyTextureToBuffer(commandEncoder, &src, &dst, &size);
1005 const WGPUCommandEncoder &commandEncoder = graphicsDevice->commandEncoder;
1007 BufferWebGPU& buffer = graphicsDevice->buffers.buffers[bufferHandle];
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;
1020#ifdef __EMSCRIPTEN__
1022 src.texture = defaultSwapChain.colorBufferTexture;
1024 src.texture = defaultSwapChain.resolveTexture;
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;
1037 size.height = height;
1038 size.depthOrArrayLayers = 1;
1039 wgpuCommandEncoderCopyTextureToBuffer(commandEncoder, &src, &dst, &size);
1044 assert(stride ==
nullptr || stride[0] == 0);
1045 BufferWebGPU &buffer = graphicsDevice->buffers.buffers[handle];
1046 if(buffer.is_read_buffer){
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){
1061 assert(state == WGPUBufferMapState_Pending);
1062 wgpuInstanceProcessEvents(graphicsDevice->instance);
1063 wgpuDeviceTick(graphicsDevice->device);
1064 state = wgpuBufferGetMapState(buffer.buffer);
1070 return (
void*)wgpuBufferGetConstMappedRange(buffer.buffer, 0, buffer.size);
1074 buffer.NextInstance(graphicsDevice, graphicsDevice->buffers);
1075 buffer.map =
new char[buffer.size];
1088 BufferWebGPU &buffer = graphicsDevice->buffers.buffers[handle];
1089 if(buffer.is_read_buffer){
1092 wgpuBufferUnmap(buffer.buffer);
1095 if(buffer.size > 0){
1096 wgpuQueueWriteBuffer(graphicsDevice->queue, buffer.buffer, 0, buffer.map, buffer.size);
1097 if(uploadStatisticsEnabled) uploadStatisticsBufferUpload(buffer.size);
1099 delete [] (
char*)buffer.map;
1100 buffer.map =
nullptr;
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);
1118 buffer.NextInstance(graphicsDevice, graphicsDevice->buffers);
1119 wgpuQueueWriteBuffer(graphicsDevice->queue, buffer.buffer, 0, data, size);
1120 if(uploadStatisticsEnabled) uploadStatisticsBufferUpload(size);
1125 assert(HandleIsValid(textureHandle) &&
"Texture handle is invalid.");
1127 TextureWebGPU & texture = graphicsDevice->textures.textures[textureHandle];
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));
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 };
1138 const Cogs::FormatInfo *textureFormat = Cogs::getFormatInfo(texture.format);
1140 WGPUTexelCopyBufferLayout sourceLayout;
1141 sourceLayout.offset = 0;
1143 sourceLayout.rowsPerImage = (height + textureFormat->
blockExtent.height - 1) / textureFormat->
blockExtent.height;
1145 size_t size = sourceLayout.bytesPerRow * sourceLayout.rowsPerImage;
1147 WGPUExtent3D extent;
1148 extent.width = width;
1149 extent.height = height;
1150 extent.depthOrArrayLayers = 1;
1152 wgpuQueueWriteTexture(graphicsDevice->queue, &destinationTexture, data,
static_cast<size_t>(size), &sourceLayout, &extent);
1154 if(uploadStatisticsEnabled) uploadStatisticsTextureUpload(texture.estimatedByteSize);
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);
1166 assert(buffer.alias_idx == 0);
1168 wgpuQueueWriteBuffer(graphicsDevice->queue, buffer.buffer, offset, data, size);
1169 if(uploadStatisticsEnabled) uploadStatisticsBufferUpload(size);
1182 void ContextWebGPU::copyResource(TextureHandle , TextureHandle )
1187 void ContextWebGPU::copyTexture(TextureHandle dstHandle,
unsigned dstSub,
unsigned dstX,
unsigned dstY,
unsigned dstZ, TextureHandle srcHandle,
unsigned srcSub)
1189 graphicsDevice->maybeCreateUploadCommandEncoder();
1190 const WGPUCommandEncoder &commandEncoder = graphicsDevice->commandEncoder;
1191 TexturesWebGPU &textures = graphicsDevice->textures;
1193 TextureWebGPU &src = textures.textures[srcHandle];
1194 TextureWebGPU &dst = textures.textures[dstHandle];
1196 uint32_t srcLevel = 0;
1197 uint32_t srcLayer = srcSub;
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;
1205 uint32_t dstLevel = 0;
1206 uint32_t dstLayer = dstSub;
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;
1214 WGPUExtent3D size = WGPU_EXTENT_3D_INIT;
1215 size.width = src.width;
1216 size.height = src.height;
1217 size.depthOrArrayLayers = 1;
1218 wgpuCommandEncoderCopyTextureToTexture(commandEncoder, ©_src, ©_dst, &size);
1221 void ContextWebGPU::clearResource(BufferHandle , uint32_t* )
1226 void ContextWebGPU::clearResource(BufferHandle ,
float* )
1231 void ContextWebGPU::beginFrame() {
1232 prevStats = currStats;
1233 currStats = FrameStatistics{};
1235 prevUploadStats = currUploadStats;
1236 currUploadStats = UploadStatistics{};
1239 void ContextWebGPU::frameStatisticsAccountDrawCall(
size_t count,
bool indexed)
1242 unsigned bucket = 0;
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; }
1251 assert(uint64_t(count) < (uint64_t(1) << (bucket + 1)));
1253 assert((uint64_t(1) << bucket) <= uint64_t(count));
1256 assert(bucket == 0);
1260 currStats.drawCallHistogram[bucket]++;
1261 currStats.vertices += count;
1262 if (indexed) currStats.indices += count;
1265 void ContextWebGPU::uploadStatisticsBufferUpload(
size_t size)
1267 currUploadStats.bufferUploads++;
1268 currUploadStats.bufferUploadSize += size;
1271 void ContextWebGPU::uploadStatisticsTextureUpload(
size_t size)
1273 currUploadStats.textureUploads++;
1274 currUploadStats.textureUploadSize += size;
1277 EffectHandle ContextWebGPU::getCurrentEffect()
1282 EffectsWebGPU *ContextWebGPU::getEffects()
1284 return &graphicsDevice->effects;
1287 BindGroupsWebGPU* ContextWebGPU::getBindGroups()
1289 return &graphicsDevice->bindGroups;
1292 void ContextWebGPU::ClearBindGroups()
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;
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.
Provides a weakly referenced view over the contents of a string.
constexpr const char * data() const noexcept
Get the sequence of characters referenced by the string view.
constexpr size_t length() const noexcept
Get the length of the string.
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
Contains all Cogs related functionality.
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Framebuffer
Framebuffers to select from when doing framebuffer operations.
PrimitiveType
Primitive types for interpreting vertex data sent to the graphics pipeline.
Handle template class used to provide opaque, non-converting handles.
static const Handle_t NoHandle
Represents a handle to nothing.
handle_type handle
Internal resource handle.
static const Handle_t InvalidHandle
Represents an invalid handle.
EMapMode
Mapping mode enumeration.
@ WriteDiscard
Write access. When unmapping the graphics system will discard the old contents of the resource.