Skip to content

Commit d570ca5

Browse files
committed
Feat: Implemented indirect drawing support for WebGPU.
1 parent 58e6140 commit d570ca5

File tree

2 files changed

+173
-46
lines changed

2 files changed

+173
-46
lines changed

src/gpu/webgpu/SDL_WebGPU_Checklist.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,17 @@ Note: WebGPU has no exposed fence API.
5858
- [x] DownloadFromTexture (needs to be tested)
5959
- [x] CopyTextureToTexture (needs to be tested)
6060
- [ ] GenerateMipmaps
61+
- Requires custom compute pipeline implementation.
62+
- https://eliemichel.github.io/LearnWebGPU/basic-compute/image-processing/mipmap-generation.html
6163

6264
## Samplers
6365
- [x] CreateSampler
6466
- [x] ReleaseSampler
6567

6668
## Debugging
67-
- [ ] InsertDebugLabel
68-
- [ ] PushDebugGroup
69-
- [ ] PopDebugGroup
69+
- [x] InsertDebugLabel
70+
- [x] PushDebugGroup
71+
- [x] PopDebugGroup
7072

7173
## Graphics Pipelines
7274
- [x] CreateGraphicsPipeline
@@ -85,10 +87,10 @@ Note: WebGPU has no exposed fence API.
8587
## Rendering
8688
- [x] BeginRenderPass
8789
- [x] EndRenderPass
88-
- [ ] DrawPrimitivesIndirect
90+
- [x] DrawPrimitivesIndirect
8991
- [x] DrawPrimitives
9092
- [x] DrawIndexedPrimitives
91-
- [ ] DrawIndexedPrimitivesIndirect
93+
- [x] DrawIndexedPrimitivesIndirect
9294

9395
## Copy Passes
9496
- [x] BeginCopyPass
@@ -108,21 +110,27 @@ Note: WebGPU has no exposed fence API.
108110
- [x] BindFragmentSamplers
109111
- [ ] BindFragmentStorageTextures
110112
- [ ] BindFragmentStorageBuffers
111-
- [ ] PushFragmentUniformData (NEEDS TO BE REVISITED)
113+
- [ ] PushFragmentUniformData
114+
- Needs to be rewritten.
112115

113116
## Vertex Stage
114117
- [x] BindVertexBuffers
115118
- [x] BindIndexBuffer
116119
- [x] BindVertexSamplers
117120
- [ ] BindVertexStorageTextures
118121
- [ ] BindVertexStorageBuffers
119-
- [ ] PushVertexUniformData (NEEDS TO BE REVISITED)
122+
- [ ] PushVertexUniformData
123+
- Needs to be rewritten.
120124

121125
## Rendering States
122126
- [x] SetViewport
123127
- [x] SetScissor
124-
- [ ] SetBlendConstants
128+
- [x] SetBlendConstants
125129
- [x] SetStencilReference
126130

127131
## Composition
128-
- [ ] Blit (Bug: Example "Blit2DArray" has a sampler issue where the RHS is not downsampled)
132+
- [ ] Blit
133+
- Mostly functional.
134+
- Bug: Example "Blit2DArray.c" has a sampler issue where the RHS is not downsampled.
135+
- Bug: Example "TriangleMSAA.c" does not cycle between different sample counts.
136+

src/gpu/webgpu/SDL_gpu_webgpu.c

Lines changed: 156 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -649,13 +649,13 @@ static WGPUTextureUsageFlags SDLToWGPUTextureUsageFlags(SDL_GPUTextureUsageFlags
649649
wgpuFlags |= WGPUTextureUsage_RenderAttachment;
650650
}
651651
if (usageFlags & SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ) {
652-
wgpuFlags |= WGPUTextureUsage_StorageBinding;
652+
wgpuFlags |= WGPUTextureUsage_StorageBinding | WGPUTextureUsage_CopyDst;
653653
}
654654
if (usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ) {
655-
wgpuFlags |= WGPUTextureUsage_StorageBinding | WGPUTextureUsage_CopySrc;
655+
wgpuFlags |= WGPUTextureUsage_StorageBinding | WGPUTextureUsage_CopyDst;
656656
}
657657
if (usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) {
658-
wgpuFlags |= WGPUTextureUsage_StorageBinding | WGPUTextureUsage_CopyDst;
658+
wgpuFlags |= WGPUTextureUsage_StorageBinding | WGPUTextureUsage_CopySrc;
659659
}
660660
if (usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE) {
661661
wgpuFlags |= WGPUTextureUsage_StorageBinding | WGPUTextureUsage_CopySrc | WGPUTextureUsage_CopyDst;
@@ -829,11 +829,9 @@ static Uint32 SDLToWGPUSampleCount(SDL_GPUSampleCount samples)
829829
case SDL_GPU_SAMPLECOUNT_1:
830830
return 1;
831831
case SDL_GPU_SAMPLECOUNT_2:
832-
return 2;
833832
case SDL_GPU_SAMPLECOUNT_4:
834-
return 4;
835833
case SDL_GPU_SAMPLECOUNT_8:
836-
return 8;
834+
return 4;
837835
default:
838836
return 1;
839837
}
@@ -1278,7 +1276,6 @@ static void WebGPU_SetBufferName(SDL_GPURenderer *driverData,
12781276
wgpuBufferSetLabel(webgpuBuffer->buffer, text);
12791277
}
12801278

1281-
12821279
static SDL_GPUBuffer *WebGPU_CreateGPUBuffer(SDL_GPURenderer *driverData,
12831280
SDL_GPUBufferUsageFlags usageFlags,
12841281
Uint32 size,
@@ -1311,7 +1308,7 @@ static SDL_GPUTransferBuffer *WebGPU_CreateTransferBuffer(
13111308
{
13121309
SDL_GPUBuffer *buffer = WebGPU_INTERNAL_CreateGPUBuffer(driverData, &usage, size, WEBGPU_BUFFER_TYPE_TRANSFER);
13131310
if (debugName) {
1314-
WebGPU_SetBufferName(driverData, buffer, debugName);
1311+
WebGPU_SetBufferName(driverData, buffer, debugName);
13151312
} else {
13161313
WebGPU_SetBufferName(driverData, buffer, "SDLGPU Transfer Buffer");
13171314
}
@@ -2357,15 +2354,15 @@ static void WebGPU_CreateSwapchain(WebGPURenderer *renderer, WindowData *windowD
23572354

23582355
swapchainData->format = wgpuSurfaceGetPreferredFormat(swapchainData->surface, renderer->adapter);
23592356
swapchainData->presentMode = SDLToWGPUPresentMode(windowData->presentMode);
2360-
wgpuSurfaceConfigure(swapchainData->surface, &(WGPUSurfaceConfiguration){
2361-
.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_CopySrc | WGPUTextureUsage_CopyDst,
2362-
.format = swapchainData->format,
2363-
.width = windowData->window->w,
2364-
.height = windowData->window->h,
2365-
.presentMode = swapchainData->presentMode,
2366-
.alphaMode = WGPUCompositeAlphaMode_Opaque,
2367-
.device = renderer->device,
2368-
});
2357+
wgpuSurfaceConfigure(swapchainData->surface, &(WGPUSurfaceConfiguration){
2358+
.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_CopySrc | WGPUTextureUsage_CopyDst,
2359+
.format = swapchainData->format,
2360+
.width = windowData->window->w,
2361+
.height = windowData->window->h,
2362+
.presentMode = swapchainData->presentMode,
2363+
.alphaMode = WGPUCompositeAlphaMode_Opaque,
2364+
.device = renderer->device,
2365+
});
23692366

23702367
// Swapchain should be the size of whatever SDL_Window it is attached to
23712368
swapchainData->width = windowData->window->w;
@@ -2469,21 +2466,21 @@ static WGPUTexture WebGPU_INTERNAL_AcquireSurfaceTexture(WebGPURenderer *rendere
24692466
wgpuSurfaceGetCurrentTexture(windowData->swapchainData.surface, &surfaceTexture);
24702467

24712468
switch (surfaceTexture.status) {
2472-
case WGPUSurfaceGetCurrentTextureStatus_Success:
2473-
break;
2474-
case WGPUSurfaceGetCurrentTextureStatus_DeviceLost:
2475-
SDL_LogError(SDL_LOG_CATEGORY_GPU, "GPU DEVICE LOST");
2476-
SDL_SetError("GPU DEVICE LOST");
2477-
return NULL;
2478-
case WGPUSurfaceGetCurrentTextureStatus_OutOfMemory:
2479-
SDL_OutOfMemory();
2480-
return NULL;
2481-
case WGPUSurfaceGetCurrentTextureStatus_Timeout:
2482-
case WGPUSurfaceGetCurrentTextureStatus_Outdated:
2483-
case WGPUSurfaceGetCurrentTextureStatus_Lost:
2484-
default:
2485-
WebGPU_RecreateSwapchain(renderer, windowData);
2486-
return NULL;
2469+
case WGPUSurfaceGetCurrentTextureStatus_Success:
2470+
break;
2471+
case WGPUSurfaceGetCurrentTextureStatus_DeviceLost:
2472+
SDL_LogError(SDL_LOG_CATEGORY_GPU, "GPU DEVICE LOST");
2473+
SDL_SetError("GPU DEVICE LOST");
2474+
return NULL;
2475+
case WGPUSurfaceGetCurrentTextureStatus_OutOfMemory:
2476+
SDL_OutOfMemory();
2477+
return NULL;
2478+
case WGPUSurfaceGetCurrentTextureStatus_Timeout:
2479+
case WGPUSurfaceGetCurrentTextureStatus_Outdated:
2480+
case WGPUSurfaceGetCurrentTextureStatus_Lost:
2481+
default:
2482+
WebGPU_RecreateSwapchain(renderer, windowData);
2483+
return NULL;
24872484
}
24882485

24892486
return surfaceTexture.texture;
@@ -2610,18 +2607,20 @@ static bool WebGPU_SupportsSampleCount(SDL_GPURenderer *driverData,
26102607
{
26112608
(void)driverData;
26122609
WGPUTextureFormat wgpuFormat = SDLToWGPUTextureFormat(format);
2613-
Uint32 wgpuSampleCount = SDLToWGPUSampleCount(desiredSampleCount);
2614-
26152610
// Verify that the format and sample count are considered valid
26162611
if (wgpuFormat == WGPUTextureFormat_Undefined) {
26172612
return false;
26182613
}
26192614

2615+
SDL_Log("Desired sample count %u", desiredSampleCount);
2616+
26202617
// WebGPU only supports 1 and 4.
2621-
if (wgpuSampleCount != 1 && wgpuSampleCount != 4) {
2618+
if (desiredSampleCount != SDL_GPU_SAMPLECOUNT_1 && desiredSampleCount != SDL_GPU_SAMPLECOUNT_4) {
26222619
return false;
26232620
}
26242621

2622+
/*Uint32 wgpuSampleCount = SDLToWGPUSampleCount(desiredSampleCount);*/
2623+
26252624
// Sample count is valid.
26262625
return true;
26272626
}
@@ -3297,6 +3296,13 @@ static SDL_GPUGraphicsPipeline *WebGPU_CreateGraphicsPipeline(
32973296
SDL_Log("TODO: Implement specific pipeline setup to emulate line fill mode.");
32983297
}
32993298

3299+
Uint32 sampleCount = pipelineCreateInfo->multisample_state.sample_count == 0 ? 1 : pipelineCreateInfo->multisample_state.sample_count;
3300+
Uint32 sampleMask = pipelineCreateInfo->multisample_state.sample_mask == 0 ? 0xFFFF : pipelineCreateInfo->multisample_state.sample_mask;
3301+
if (sampleCount != SDL_GPU_SAMPLECOUNT_1 && sampleCount != SDL_GPU_SAMPLECOUNT_4) {
3302+
SDL_Log("Sample count not supported in WebGPU. Defaulting to 1.");
3303+
sampleCount = SDL_GPU_SAMPLECOUNT_1;
3304+
}
3305+
33003306
// Create the render pipeline descriptor
33013307
WGPURenderPipelineDescriptor pipelineDesc = {
33023308
.nextInChain = NULL,
@@ -3312,8 +3318,8 @@ static SDL_GPUGraphicsPipeline *WebGPU_CreateGraphicsPipeline(
33123318
// Needs to be set up
33133319
.depthStencil = pipelineCreateInfo->target_info.has_depth_stencil_target ? &depthStencil : NULL,
33143320
.multisample = {
3315-
.count = pipelineCreateInfo->multisample_state.sample_count == 0 ? 1 : pipelineCreateInfo->multisample_state.sample_count,
3316-
.mask = pipelineCreateInfo->multisample_state.sample_mask == 0 ? 0xFFFF : pipelineCreateInfo->multisample_state.sample_mask,
3321+
.count = SDLToWGPUSampleCount(sampleCount),
3322+
.mask = sampleMask,
33173323
.alphaToCoverageEnabled = false,
33183324
},
33193325
.fragment = &fragmentState,
@@ -3827,9 +3833,29 @@ void WebGPU_SetScissorRect(SDL_GPUCommandBuffer *renderPass, const SDL_Rect *sci
38273833
static void WebGPU_SetStencilReference(SDL_GPUCommandBuffer *commandBuffer,
38283834
Uint8 reference)
38293835
{
3836+
if (commandBuffer == NULL) {
3837+
return;
3838+
}
3839+
38303840
wgpuRenderPassEncoderSetStencilReference(((WebGPUCommandBuffer *)commandBuffer)->renderPassEncoder, reference);
38313841
}
38323842

3843+
static void WebGPU_SetBlendConstants(
3844+
SDL_GPUCommandBuffer *commandBuffer,
3845+
SDL_FColor blendConstants)
3846+
{
3847+
if (commandBuffer == NULL) {
3848+
return;
3849+
}
3850+
3851+
wgpuRenderPassEncoderSetBlendConstant(((WebGPUCommandBuffer *)commandBuffer)->renderPassEncoder, &(WGPUColor){
3852+
.r = blendConstants.r,
3853+
.g = blendConstants.g,
3854+
.b = blendConstants.b,
3855+
.a = blendConstants.a,
3856+
});
3857+
}
3858+
38333859
static void WebGPU_BindGraphicsPipeline(
38343860
SDL_GPUCommandBuffer *commandBuffer,
38353861
SDL_GPUGraphicsPipeline *graphicsPipeline)
@@ -3949,6 +3975,7 @@ static void WebGPU_DrawPrimitives(
39493975
wgpuRenderPassEncoderDraw(wgpu_cmd_buf->renderPassEncoder, vertexCount, instanceCount, firstVertex, firstInstance);
39503976
}
39513977

3978+
39523979
static void WebGPU_DrawIndexedPrimitives(
39533980
SDL_GPUCommandBuffer *commandBuffer,
39543981
Uint32 numIndices,
@@ -3957,11 +3984,50 @@ static void WebGPU_DrawIndexedPrimitives(
39573984
Sint32 vertexOffset,
39583985
Uint32 firstInstance)
39593986
{
3987+
if (commandBuffer == NULL) {
3988+
return;
3989+
}
39603990
WebGPUCommandBuffer *wgpu_cmd_buf = (WebGPUCommandBuffer *)commandBuffer;
39613991
WebGPU_INTERNAL_SetBindGroups(commandBuffer);
39623992
wgpuRenderPassEncoderDrawIndexed(wgpu_cmd_buf->renderPassEncoder, numIndices, numInstances, firstIndex, vertexOffset, firstInstance);
39633993
}
39643994

3995+
static void WebGPU_DrawPrimitivesIndirect(
3996+
SDL_GPUCommandBuffer *commandBuffer,
3997+
SDL_GPUBuffer *buffer,
3998+
Uint32 offset,
3999+
Uint32 drawCount)
4000+
{
4001+
if (commandBuffer == NULL) {
4002+
return;
4003+
}
4004+
WebGPUCommandBuffer *wgpu_cmd_buf = (WebGPUCommandBuffer *)commandBuffer;
4005+
WebGPUBuffer *wgpuBuffer = (WebGPUBuffer *)buffer;
4006+
Uint32 pitch = sizeof(SDL_GPUIndirectDrawCommand);
4007+
WebGPU_INTERNAL_SetBindGroups(commandBuffer);
4008+
for (Uint32 i = 0; i < drawCount; i += 1) {
4009+
wgpuRenderPassEncoderDrawIndirect(wgpu_cmd_buf->renderPassEncoder, wgpuBuffer->buffer, offset + (i * pitch));
4010+
}
4011+
}
4012+
4013+
static void WebGPU_DrawIndexedIndirect(
4014+
SDL_GPUCommandBuffer *commandBuffer,
4015+
SDL_GPUBuffer *buffer,
4016+
Uint32 offset,
4017+
Uint32 drawCount)
4018+
{
4019+
if (commandBuffer == NULL) {
4020+
return;
4021+
}
4022+
WebGPUCommandBuffer *wgpu_cmd_buf = (WebGPUCommandBuffer *)commandBuffer;
4023+
WebGPUBuffer *wgpuBuffer = (WebGPUBuffer *)buffer;
4024+
Uint32 pitch = sizeof(SDL_GPUIndexedIndirectDrawCommand);
4025+
WebGPU_INTERNAL_SetBindGroups(commandBuffer);
4026+
for (Uint32 i = 0; i < drawCount; i += 1) {
4027+
wgpuRenderPassEncoderDrawIndexedIndirect(wgpu_cmd_buf->renderPassEncoder, wgpuBuffer->buffer, offset + (i * pitch));
4028+
}
4029+
}
4030+
39654031
// Blit Functionality Workaround
39664032
// ---------------------------------------------------
39674033

@@ -4308,6 +4374,52 @@ static void WebGPU_Blit(SDL_GPUCommandBuffer *commandBuffer, const SDL_GPUBlitIn
43084374

43094375
// ---------------------------------------------------
43104376

4377+
void WebGPU_GenerateMipmaps(SDL_GPUCommandBuffer *commandBuffer,
4378+
SDL_GPUTexture *texture)
4379+
{
4380+
// We will have to implement our own mipmapping pipeline here.
4381+
// I suggest Elie Michel's guide on mipmapping: https://eliemichel.github.io/LearnWebGPU/basic-compute/image-processing/mipmap-generation.html
4382+
SDL_LogError(SDL_LOG_CATEGORY_GPU, "WebGPU mipmapping is not yet implemented");
4383+
}
4384+
4385+
// ---------------------------------------------------
4386+
4387+
// Debugging Functionality
4388+
4389+
void WebGPU_InsertDebugLabel(
4390+
SDL_GPUCommandBuffer *commandBuffer,
4391+
const char *text)
4392+
{
4393+
if (!commandBuffer || !text) {
4394+
return;
4395+
}
4396+
WebGPUCommandBuffer *wgpu_cmd_buf = (WebGPUCommandBuffer *)commandBuffer;
4397+
wgpuCommandEncoderInsertDebugMarker(wgpu_cmd_buf->commandEncoder, text);
4398+
}
4399+
4400+
void WebGPU_PushDebugGroup(
4401+
SDL_GPUCommandBuffer *commandBuffer,
4402+
const char *text)
4403+
{
4404+
if (!commandBuffer || !text) {
4405+
return;
4406+
}
4407+
WebGPUCommandBuffer *wgpu_cmd_buf = (WebGPUCommandBuffer *)commandBuffer;
4408+
wgpuCommandEncoderPushDebugGroup(wgpu_cmd_buf->commandEncoder, text);
4409+
}
4410+
4411+
void WebGPU_PopDebugGroup(
4412+
SDL_GPUCommandBuffer *commandBuffer)
4413+
{
4414+
if (!commandBuffer) {
4415+
return;
4416+
}
4417+
WebGPUCommandBuffer *wgpu_cmd_buf = (WebGPUCommandBuffer *)commandBuffer;
4418+
wgpuCommandEncoderPopDebugGroup(wgpu_cmd_buf->commandEncoder);
4419+
}
4420+
4421+
// ---------------------------------------------------
4422+
43114423
static bool WebGPU_PrepareDriver(SDL_VideoDevice *_this)
43124424
{
43134425
// Realistically, we should check if the browser supports WebGPU here and return false if it doesn't
@@ -4443,11 +4555,16 @@ static SDL_GPUDevice *WebGPU_CreateDevice(bool debug, bool preferLowPower, SDL_P
44434555
result->BindGraphicsPipeline = WebGPU_BindGraphicsPipeline;
44444556
result->ReleaseGraphicsPipeline = WebGPU_ReleaseGraphicsPipeline;
44454557
result->DrawPrimitives = WebGPU_DrawPrimitives;
4558+
result->DrawPrimitivesIndirect = WebGPU_DrawPrimitivesIndirect;
44464559
result->DrawIndexedPrimitives = WebGPU_DrawIndexedPrimitives;
4560+
result->DrawIndexedPrimitivesIndirect = WebGPU_DrawIndexedIndirect;
44474561

44484562
result->SetScissor = WebGPU_SetScissorRect;
44494563
result->SetViewport = WebGPU_SetViewport;
44504564
result->SetStencilReference = WebGPU_SetStencilReference;
4565+
result->SetBlendConstants = WebGPU_SetBlendConstants;
4566+
4567+
result->GenerateMipmaps = WebGPU_GenerateMipmaps;
44514568

44524569
result->Submit = WebGPU_Submit;
44534570
result->SubmitAndAcquireFence = WebGPU_SubmitAndAcquireFence;
@@ -4461,6 +4578,8 @@ static SDL_GPUDevice *WebGPU_CreateDevice(bool debug, bool preferLowPower, SDL_P
44614578
result->BeginCopyPass = WebGPU_BeginCopyPass;
44624579
result->EndCopyPass = WebGPU_EndCopyPass;
44634580

4581+
result->InsertDebugLabel = WebGPU_InsertDebugLabel;
4582+
44644583
renderer->sdlDevice = result;
44654584

44664585
return result;

0 commit comments

Comments
 (0)