Skip to content

Commit 849342f

Browse files
author
Patrick Komon
authored
Implement wgpuAdapterGetLimits, add test that limit requests are applied (#21799)
- Implement the wgpuAdapterGetLimits entry point. - Add a test which verifies that limit requests are correctly passed through to requestDevice(), by requesting all of the adapter's max limits, then ensuring that the device has the same limits. This verifies that wgpuAdapterGetLimits and wgpuDeviceGetLimits work, and that limit requests are applied (though it can only verify limits for which the current system supports a value better than the default). Fixes #21798
1 parent c54609c commit 849342f

File tree

3 files changed

+153
-46
lines changed

3 files changed

+153
-46
lines changed

src/library_webgpu.js

Lines changed: 54 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,54 @@ var LibraryWebGPU = {
339339
return desc;
340340
},
341341

342+
fillLimitStruct: (limits, supportedLimitsOutPtr) => {
343+
var limitsOutPtr = supportedLimitsOutPtr + {{{ C_STRUCTS.WGPUSupportedLimits.limits }}};
344+
345+
function setLimitValueU32(name, limitOffset) {
346+
var limitValue = limits[name];
347+
{{{ makeSetValue('limitsOutPtr', 'limitOffset', 'limitValue', 'i32') }}};
348+
}
349+
function setLimitValueU64(name, limitOffset) {
350+
var limitValue = limits[name];
351+
{{{ makeSetValue('limitsOutPtr', 'limitOffset', 'limitValue', 'i64') }}};
352+
}
353+
354+
setLimitValueU32('maxTextureDimension1D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension1D }}});
355+
setLimitValueU32('maxTextureDimension2D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension2D }}});
356+
setLimitValueU32('maxTextureDimension3D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension3D }}});
357+
setLimitValueU32('maxTextureArrayLayers', {{{ C_STRUCTS.WGPULimits.maxTextureArrayLayers }}});
358+
setLimitValueU32('maxBindGroups', {{{ C_STRUCTS.WGPULimits.maxBindGroups }}});
359+
setLimitValueU32('maxBindGroupsPlusVertexBuffers', {{{ C_STRUCTS.WGPULimits.maxBindGroupsPlusVertexBuffers }}});
360+
setLimitValueU32('maxBindingsPerBindGroup', {{{ C_STRUCTS.WGPULimits.maxBindingsPerBindGroup }}});
361+
setLimitValueU32('maxDynamicUniformBuffersPerPipelineLayout', {{{ C_STRUCTS.WGPULimits.maxDynamicUniformBuffersPerPipelineLayout }}});
362+
setLimitValueU32('maxDynamicStorageBuffersPerPipelineLayout', {{{ C_STRUCTS.WGPULimits.maxDynamicStorageBuffersPerPipelineLayout }}});
363+
setLimitValueU32('maxSampledTexturesPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxSampledTexturesPerShaderStage }}});
364+
setLimitValueU32('maxSamplersPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxSamplersPerShaderStage }}});
365+
setLimitValueU32('maxStorageBuffersPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxStorageBuffersPerShaderStage }}});
366+
setLimitValueU32('maxStorageTexturesPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxStorageTexturesPerShaderStage }}});
367+
setLimitValueU32('maxUniformBuffersPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxUniformBuffersPerShaderStage }}});
368+
setLimitValueU32('minUniformBufferOffsetAlignment', {{{ C_STRUCTS.WGPULimits.minUniformBufferOffsetAlignment }}});
369+
setLimitValueU32('minStorageBufferOffsetAlignment', {{{ C_STRUCTS.WGPULimits.minStorageBufferOffsetAlignment }}});
370+
371+
setLimitValueU64('maxUniformBufferBindingSize', {{{ C_STRUCTS.WGPULimits.maxUniformBufferBindingSize }}});
372+
setLimitValueU64('maxStorageBufferBindingSize', {{{ C_STRUCTS.WGPULimits.maxStorageBufferBindingSize }}});
373+
374+
setLimitValueU32('maxVertexBuffers', {{{ C_STRUCTS.WGPULimits.maxVertexBuffers }}});
375+
setLimitValueU32('maxBufferSize', {{{ C_STRUCTS.WGPULimits.maxBufferSize }}});
376+
setLimitValueU32('maxVertexAttributes', {{{ C_STRUCTS.WGPULimits.maxVertexAttributes }}});
377+
setLimitValueU32('maxVertexBufferArrayStride', {{{ C_STRUCTS.WGPULimits.maxVertexBufferArrayStride }}});
378+
setLimitValueU32('maxInterStageShaderComponents', {{{ C_STRUCTS.WGPULimits.maxInterStageShaderComponents }}});
379+
setLimitValueU32('maxInterStageShaderVariables', {{{ C_STRUCTS.WGPULimits.maxInterStageShaderVariables }}});
380+
setLimitValueU32('maxColorAttachments', {{{ C_STRUCTS.WGPULimits.maxColorAttachments }}});
381+
setLimitValueU32('maxColorAttachmentBytesPerSample', {{{ C_STRUCTS.WGPULimits.maxColorAttachmentBytesPerSample }}});
382+
setLimitValueU32('maxComputeWorkgroupStorageSize', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupStorageSize }}});
383+
setLimitValueU32('maxComputeInvocationsPerWorkgroup', {{{ C_STRUCTS.WGPULimits.maxComputeInvocationsPerWorkgroup }}});
384+
setLimitValueU32('maxComputeWorkgroupSizeX', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupSizeX }}});
385+
setLimitValueU32('maxComputeWorkgroupSizeY', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupSizeY }}});
386+
setLimitValueU32('maxComputeWorkgroupSizeZ', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupSizeZ }}});
387+
setLimitValueU32('maxComputeWorkgroupsPerDimension', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupsPerDimension }}});
388+
},
389+
342390
// Map from enum string back to enum number, for callbacks.
343391
Int_BufferMapState: {
344392
'unmapped': 0,
@@ -768,50 +816,7 @@ var LibraryWebGPU = {
768816

769817
wgpuDeviceGetLimits: (deviceId, limitsOutPtr) => {
770818
var device = WebGPU.mgrDevice.objects[deviceId].object;
771-
var limitsPtr = {{{ C_STRUCTS.WGPUSupportedLimits.limits }}};
772-
function setLimitValueU32(name, limitOffset) {
773-
var limitValue = device.limits[name];
774-
{{{ makeSetValue('limitsOutPtr', 'limitsPtr + limitOffset', 'limitValue', 'i32') }}};
775-
}
776-
function setLimitValueU64(name, limitOffset) {
777-
var limitValue = device.limits[name];
778-
{{{ makeSetValue('limitsOutPtr', 'limitsPtr + limitOffset', 'limitValue', 'i64') }}};
779-
}
780-
781-
setLimitValueU32('maxTextureDimension1D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension1D }}});
782-
setLimitValueU32('maxTextureDimension2D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension2D }}});
783-
setLimitValueU32('maxTextureDimension3D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension3D }}});
784-
setLimitValueU32('maxTextureArrayLayers', {{{ C_STRUCTS.WGPULimits.maxTextureArrayLayers }}});
785-
setLimitValueU32('maxBindGroups', {{{ C_STRUCTS.WGPULimits.maxBindGroups }}});
786-
setLimitValueU32('maxBindGroupsPlusVertexBuffers', {{{ C_STRUCTS.WGPULimits.maxBindGroupsPlusVertexBuffers }}});
787-
setLimitValueU32('maxBindingsPerBindGroup', {{{ C_STRUCTS.WGPULimits.maxBindingsPerBindGroup }}});
788-
setLimitValueU32('maxDynamicUniformBuffersPerPipelineLayout', {{{ C_STRUCTS.WGPULimits.maxDynamicUniformBuffersPerPipelineLayout }}});
789-
setLimitValueU32('maxDynamicStorageBuffersPerPipelineLayout', {{{ C_STRUCTS.WGPULimits.maxDynamicStorageBuffersPerPipelineLayout }}});
790-
setLimitValueU32('maxSampledTexturesPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxSampledTexturesPerShaderStage }}});
791-
setLimitValueU32('maxSamplersPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxSamplersPerShaderStage }}});
792-
setLimitValueU32('maxStorageBuffersPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxStorageBuffersPerShaderStage }}});
793-
setLimitValueU32('maxStorageTexturesPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxStorageTexturesPerShaderStage }}});
794-
setLimitValueU32('maxUniformBuffersPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxUniformBuffersPerShaderStage }}});
795-
setLimitValueU32('minUniformBufferOffsetAlignment', {{{ C_STRUCTS.WGPULimits.minUniformBufferOffsetAlignment }}});
796-
setLimitValueU32('minStorageBufferOffsetAlignment', {{{ C_STRUCTS.WGPULimits.minStorageBufferOffsetAlignment }}});
797-
798-
setLimitValueU64('maxUniformBufferBindingSize', {{{ C_STRUCTS.WGPULimits.maxUniformBufferBindingSize }}});
799-
setLimitValueU64('maxStorageBufferBindingSize', {{{ C_STRUCTS.WGPULimits.maxStorageBufferBindingSize }}});
800-
801-
setLimitValueU32('maxVertexBuffers', {{{ C_STRUCTS.WGPULimits.maxVertexBuffers }}});
802-
setLimitValueU32('maxBufferSize', {{{ C_STRUCTS.WGPULimits.maxBufferSize }}});
803-
setLimitValueU32('maxVertexAttributes', {{{ C_STRUCTS.WGPULimits.maxVertexAttributes }}});
804-
setLimitValueU32('maxVertexBufferArrayStride', {{{ C_STRUCTS.WGPULimits.maxVertexBufferArrayStride }}});
805-
setLimitValueU32('maxInterStageShaderComponents', {{{ C_STRUCTS.WGPULimits.maxInterStageShaderComponents }}});
806-
setLimitValueU32('maxInterStageShaderVariables', {{{ C_STRUCTS.WGPULimits.maxInterStageShaderVariables }}});
807-
setLimitValueU32('maxColorAttachments', {{{ C_STRUCTS.WGPULimits.maxColorAttachments }}});
808-
setLimitValueU32('maxColorAttachmentBytesPerSample', {{{ C_STRUCTS.WGPULimits.maxColorAttachmentBytesPerSample }}});
809-
setLimitValueU32('maxComputeWorkgroupStorageSize', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupStorageSize }}});
810-
setLimitValueU32('maxComputeInvocationsPerWorkgroup', {{{ C_STRUCTS.WGPULimits.maxComputeInvocationsPerWorkgroup }}});
811-
setLimitValueU32('maxComputeWorkgroupSizeX', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupSizeX }}});
812-
setLimitValueU32('maxComputeWorkgroupSizeY', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupSizeY }}});
813-
setLimitValueU32('maxComputeWorkgroupSizeZ', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupSizeZ }}});
814-
setLimitValueU32('maxComputeWorkgroupsPerDimension', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupsPerDimension }}});
819+
WebGPU.fillLimitStruct(device.limits, limitsOutPtr);
815820
return 1;
816821
},
817822

@@ -2537,8 +2542,9 @@ var LibraryWebGPU = {
25372542
},
25382543

25392544
wgpuAdapterGetLimits: (adapterId, limitsOutPtr) => {
2540-
abort('TODO: wgpuAdapterGetLimits unimplemented');
2541-
return 0;
2545+
var adapter = WebGPU.mgrAdapter.get(adapterId);
2546+
WebGPU.fillLimitStruct(adapter.limits, limitsOutPtr);
2547+
return 1;
25422548
},
25432549

25442550
wgpuAdapterHasFeature: (adapterId, featureEnumValue) => {
@@ -2599,11 +2605,13 @@ var LibraryWebGPU = {
25992605
setLimitU64IfDefined("maxUniformBufferBindingSize", {{{ C_STRUCTS.WGPULimits.maxUniformBufferBindingSize }}});
26002606
setLimitU64IfDefined("maxStorageBufferBindingSize", {{{ C_STRUCTS.WGPULimits.maxStorageBufferBindingSize }}});
26012607
setLimitU32IfDefined("maxVertexBuffers", {{{ C_STRUCTS.WGPULimits.maxVertexBuffers }}});
2608+
setLimitU32IfDefined("maxBufferSize", {{{ C_STRUCTS.WGPULimits.maxBufferSize }}});
26022609
setLimitU32IfDefined("maxVertexAttributes", {{{ C_STRUCTS.WGPULimits.maxVertexAttributes }}});
26032610
setLimitU32IfDefined("maxVertexBufferArrayStride", {{{ C_STRUCTS.WGPULimits.maxVertexBufferArrayStride }}});
26042611
setLimitU32IfDefined("maxInterStageShaderComponents", {{{ C_STRUCTS.WGPULimits.maxInterStageShaderComponents }}});
26052612
setLimitU32IfDefined("maxInterStageShaderVariables", {{{ C_STRUCTS.WGPULimits.maxInterStageShaderVariables }}});
26062613
setLimitU32IfDefined("maxColorAttachments", {{{ C_STRUCTS.WGPULimits.maxColorAttachments }}});
2614+
setLimitU32IfDefined("maxColorAttachmentBytesPerSample", {{{ C_STRUCTS.WGPULimits.maxColorAttachmentBytesPerSample }}});
26072615
setLimitU32IfDefined("maxComputeWorkgroupStorageSize", {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupStorageSize }}});
26082616
setLimitU32IfDefined("maxComputeInvocationsPerWorkgroup", {{{ C_STRUCTS.WGPULimits.maxComputeInvocationsPerWorkgroup }}});
26092617
setLimitU32IfDefined("maxComputeWorkgroupSizeX", {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupSizeX }}});

test/test_browser.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4522,6 +4522,10 @@ def test_webgl_simple_extensions(self, simple_enable_extensions, webgl_version):
45224522
def test_webgpu_basic_rendering(self, args):
45234523
self.btest_exit('webgpu_basic_rendering.cpp', args=['-sUSE_WEBGPU'] + args)
45244524

4525+
@requires_graphics_hardware
4526+
def test_webgpu_required_limits(self):
4527+
self.btest_exit('webgpu_required_limits.c', args=['-sUSE_WEBGPU', '-sASYNCIFY'])
4528+
45254529
# TODO(#19645): Extend this test to proxied WebGPU when it's re-enabled.
45264530
@requires_graphics_hardware
45274531
def test_webgpu_basic_rendering_pthreads(self):

test/webgpu_required_limits.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#include <assert.h>
2+
#include <emscripten.h>
3+
#include <stdio.h>
4+
#include <webgpu/webgpu.h>
5+
6+
WGPUSupportedLimits adapter_supported_limits = {
7+
0,
8+
};
9+
10+
void assertLimitsCompatible(WGPULimits required_limits,
11+
WGPULimits supported_limits) {
12+
#define ASSERT_LIMITS_COMPATIBLE(limitName) \
13+
assert(required_limits.limitName == supported_limits.limitName)
14+
ASSERT_LIMITS_COMPATIBLE(maxTextureDimension1D);
15+
ASSERT_LIMITS_COMPATIBLE(maxTextureDimension2D);
16+
ASSERT_LIMITS_COMPATIBLE(maxTextureDimension3D);
17+
ASSERT_LIMITS_COMPATIBLE(maxTextureArrayLayers);
18+
ASSERT_LIMITS_COMPATIBLE(maxBindGroups);
19+
ASSERT_LIMITS_COMPATIBLE(maxBindGroupsPlusVertexBuffers);
20+
ASSERT_LIMITS_COMPATIBLE(maxBindingsPerBindGroup);
21+
ASSERT_LIMITS_COMPATIBLE(maxDynamicUniformBuffersPerPipelineLayout);
22+
ASSERT_LIMITS_COMPATIBLE(maxDynamicStorageBuffersPerPipelineLayout);
23+
ASSERT_LIMITS_COMPATIBLE(maxSampledTexturesPerShaderStage);
24+
ASSERT_LIMITS_COMPATIBLE(maxSamplersPerShaderStage);
25+
ASSERT_LIMITS_COMPATIBLE(maxStorageBuffersPerShaderStage);
26+
ASSERT_LIMITS_COMPATIBLE(maxStorageTexturesPerShaderStage);
27+
ASSERT_LIMITS_COMPATIBLE(maxUniformBuffersPerShaderStage);
28+
ASSERT_LIMITS_COMPATIBLE(minUniformBufferOffsetAlignment);
29+
ASSERT_LIMITS_COMPATIBLE(minStorageBufferOffsetAlignment);
30+
ASSERT_LIMITS_COMPATIBLE(maxUniformBufferBindingSize);
31+
ASSERT_LIMITS_COMPATIBLE(maxStorageBufferBindingSize);
32+
ASSERT_LIMITS_COMPATIBLE(maxVertexBuffers);
33+
ASSERT_LIMITS_COMPATIBLE(maxBufferSize);
34+
ASSERT_LIMITS_COMPATIBLE(maxVertexAttributes);
35+
ASSERT_LIMITS_COMPATIBLE(maxVertexBufferArrayStride);
36+
ASSERT_LIMITS_COMPATIBLE(maxInterStageShaderComponents);
37+
ASSERT_LIMITS_COMPATIBLE(maxInterStageShaderVariables);
38+
ASSERT_LIMITS_COMPATIBLE(maxColorAttachments);
39+
ASSERT_LIMITS_COMPATIBLE(maxColorAttachmentBytesPerSample);
40+
ASSERT_LIMITS_COMPATIBLE(maxComputeWorkgroupStorageSize);
41+
ASSERT_LIMITS_COMPATIBLE(maxComputeInvocationsPerWorkgroup);
42+
ASSERT_LIMITS_COMPATIBLE(maxComputeWorkgroupSizeX);
43+
ASSERT_LIMITS_COMPATIBLE(maxComputeWorkgroupSizeY);
44+
ASSERT_LIMITS_COMPATIBLE(maxComputeWorkgroupSizeZ);
45+
ASSERT_LIMITS_COMPATIBLE(maxComputeWorkgroupsPerDimension);
46+
#undef ASSERT_LIMITS_COMPATIBLE
47+
}
48+
49+
void on_device_request_ended(WGPURequestDeviceStatus status,
50+
WGPUDevice device,
51+
char const* message,
52+
void* userdata) {
53+
assert(status == WGPURequestDeviceStatus_Success);
54+
55+
WGPUSupportedLimits device_supported_limits;
56+
wgpuDeviceGetLimits(device, &device_supported_limits);
57+
58+
// verify that the obtained device fullfils required limits
59+
assertLimitsCompatible(adapter_supported_limits.limits,
60+
device_supported_limits.limits);
61+
}
62+
63+
void on_adapter_request_ended(WGPURequestAdapterStatus status,
64+
WGPUAdapter adapter,
65+
char const* message,
66+
void* userdata) {
67+
if (status == WGPURequestAdapterStatus_Unavailable) {
68+
printf("WebGPU unavailable; exiting cleanly\n");
69+
exit(0);
70+
}
71+
72+
assert(status == WGPURequestAdapterStatus_Success);
73+
74+
wgpuAdapterGetLimits(adapter, &adapter_supported_limits);
75+
76+
// for device limits, require the limits supported by adapter
77+
WGPURequiredLimits device_required_limits = {0,};
78+
device_required_limits.limits = adapter_supported_limits.limits;
79+
80+
WGPUDeviceDescriptor device_desc = {0,};
81+
device_desc.requiredFeatureCount = 0;
82+
device_desc.requiredLimits = &device_required_limits;
83+
wgpuAdapterRequestDevice(adapter, &device_desc, on_device_request_ended, NULL);
84+
}
85+
86+
int main() {
87+
const WGPUInstance instance = wgpuCreateInstance(NULL);
88+
89+
WGPURequestAdapterOptions adapter_options = {0,};
90+
wgpuInstanceRequestAdapter(instance, &adapter_options, on_adapter_request_ended, NULL);
91+
92+
// This code is returned when the runtime exits unless something else sets
93+
// it, like exit(0).
94+
return 99;
95+
}

0 commit comments

Comments
 (0)