Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement wgpuAdapterGetLimits, add test that limit requests are applied #21799

Merged
merged 9 commits into from
May 1, 2024
100 changes: 54 additions & 46 deletions src/library_webgpu.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,54 @@ var LibraryWebGPU = {
return desc;
},

fillLimitStruct: (limits, limitsOutPtr) => {
var limitsPtr = {{{ C_STRUCTS.WGPUSupportedLimits.limits }}};

function setLimitValueU32(name, limitOffset) {
var limitValue = limits[name];
{{{ makeSetValue('limitsOutPtr', 'limitsPtr + limitOffset', 'limitValue', 'i32') }}};
}
function setLimitValueU64(name, limitOffset) {
var limitValue = limits[name];
{{{ makeSetValue('limitsOutPtr', 'limitsPtr + limitOffset', 'limitValue', 'i64') }}};
}
kainino0x marked this conversation as resolved.
Show resolved Hide resolved

setLimitValueU32('maxTextureDimension1D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension1D }}});
setLimitValueU32('maxTextureDimension2D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension2D }}});
setLimitValueU32('maxTextureDimension3D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension3D }}});
setLimitValueU32('maxTextureArrayLayers', {{{ C_STRUCTS.WGPULimits.maxTextureArrayLayers }}});
setLimitValueU32('maxBindGroups', {{{ C_STRUCTS.WGPULimits.maxBindGroups }}});
setLimitValueU32('maxBindGroupsPlusVertexBuffers', {{{ C_STRUCTS.WGPULimits.maxBindGroupsPlusVertexBuffers }}});
setLimitValueU32('maxBindingsPerBindGroup', {{{ C_STRUCTS.WGPULimits.maxBindingsPerBindGroup }}});
setLimitValueU32('maxDynamicUniformBuffersPerPipelineLayout', {{{ C_STRUCTS.WGPULimits.maxDynamicUniformBuffersPerPipelineLayout }}});
setLimitValueU32('maxDynamicStorageBuffersPerPipelineLayout', {{{ C_STRUCTS.WGPULimits.maxDynamicStorageBuffersPerPipelineLayout }}});
setLimitValueU32('maxSampledTexturesPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxSampledTexturesPerShaderStage }}});
setLimitValueU32('maxSamplersPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxSamplersPerShaderStage }}});
setLimitValueU32('maxStorageBuffersPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxStorageBuffersPerShaderStage }}});
setLimitValueU32('maxStorageTexturesPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxStorageTexturesPerShaderStage }}});
setLimitValueU32('maxUniformBuffersPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxUniformBuffersPerShaderStage }}});
setLimitValueU32('minUniformBufferOffsetAlignment', {{{ C_STRUCTS.WGPULimits.minUniformBufferOffsetAlignment }}});
setLimitValueU32('minStorageBufferOffsetAlignment', {{{ C_STRUCTS.WGPULimits.minStorageBufferOffsetAlignment }}});

setLimitValueU64('maxUniformBufferBindingSize', {{{ C_STRUCTS.WGPULimits.maxUniformBufferBindingSize }}});
setLimitValueU64('maxStorageBufferBindingSize', {{{ C_STRUCTS.WGPULimits.maxStorageBufferBindingSize }}});

setLimitValueU32('maxVertexBuffers', {{{ C_STRUCTS.WGPULimits.maxVertexBuffers }}});
setLimitValueU32('maxBufferSize', {{{ C_STRUCTS.WGPULimits.maxBufferSize }}});
setLimitValueU32('maxVertexAttributes', {{{ C_STRUCTS.WGPULimits.maxVertexAttributes }}});
setLimitValueU32('maxVertexBufferArrayStride', {{{ C_STRUCTS.WGPULimits.maxVertexBufferArrayStride }}});
setLimitValueU32('maxInterStageShaderComponents', {{{ C_STRUCTS.WGPULimits.maxInterStageShaderComponents }}});
setLimitValueU32('maxInterStageShaderVariables', {{{ C_STRUCTS.WGPULimits.maxInterStageShaderVariables }}});
setLimitValueU32('maxColorAttachments', {{{ C_STRUCTS.WGPULimits.maxColorAttachments }}});
setLimitValueU32('maxColorAttachmentBytesPerSample', {{{ C_STRUCTS.WGPULimits.maxColorAttachmentBytesPerSample }}});
setLimitValueU32('maxComputeWorkgroupStorageSize', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupStorageSize }}});
setLimitValueU32('maxComputeInvocationsPerWorkgroup', {{{ C_STRUCTS.WGPULimits.maxComputeInvocationsPerWorkgroup }}});
setLimitValueU32('maxComputeWorkgroupSizeX', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupSizeX }}});
setLimitValueU32('maxComputeWorkgroupSizeY', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupSizeY }}});
setLimitValueU32('maxComputeWorkgroupSizeZ', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupSizeZ }}});
setLimitValueU32('maxComputeWorkgroupsPerDimension', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupsPerDimension }}});
},

// Map from enum string back to enum number, for callbacks.
Int_BufferMapState: {
'unmapped': 0,
Expand Down Expand Up @@ -768,50 +816,7 @@ var LibraryWebGPU = {

wgpuDeviceGetLimits: (deviceId, limitsOutPtr) => {
var device = WebGPU.mgrDevice.objects[deviceId].object;
var limitsPtr = {{{ C_STRUCTS.WGPUSupportedLimits.limits }}};
function setLimitValueU32(name, limitOffset) {
var limitValue = device.limits[name];
{{{ makeSetValue('limitsOutPtr', 'limitsPtr + limitOffset', 'limitValue', 'i32') }}};
}
function setLimitValueU64(name, limitOffset) {
var limitValue = device.limits[name];
{{{ makeSetValue('limitsOutPtr', 'limitsPtr + limitOffset', 'limitValue', 'i64') }}};
}

setLimitValueU32('maxTextureDimension1D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension1D }}});
setLimitValueU32('maxTextureDimension2D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension2D }}});
setLimitValueU32('maxTextureDimension3D', {{{ C_STRUCTS.WGPULimits.maxTextureDimension3D }}});
setLimitValueU32('maxTextureArrayLayers', {{{ C_STRUCTS.WGPULimits.maxTextureArrayLayers }}});
setLimitValueU32('maxBindGroups', {{{ C_STRUCTS.WGPULimits.maxBindGroups }}});
setLimitValueU32('maxBindGroupsPlusVertexBuffers', {{{ C_STRUCTS.WGPULimits.maxBindGroupsPlusVertexBuffers }}});
setLimitValueU32('maxBindingsPerBindGroup', {{{ C_STRUCTS.WGPULimits.maxBindingsPerBindGroup }}});
setLimitValueU32('maxDynamicUniformBuffersPerPipelineLayout', {{{ C_STRUCTS.WGPULimits.maxDynamicUniformBuffersPerPipelineLayout }}});
setLimitValueU32('maxDynamicStorageBuffersPerPipelineLayout', {{{ C_STRUCTS.WGPULimits.maxDynamicStorageBuffersPerPipelineLayout }}});
setLimitValueU32('maxSampledTexturesPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxSampledTexturesPerShaderStage }}});
setLimitValueU32('maxSamplersPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxSamplersPerShaderStage }}});
setLimitValueU32('maxStorageBuffersPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxStorageBuffersPerShaderStage }}});
setLimitValueU32('maxStorageTexturesPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxStorageTexturesPerShaderStage }}});
setLimitValueU32('maxUniformBuffersPerShaderStage', {{{ C_STRUCTS.WGPULimits.maxUniformBuffersPerShaderStage }}});
setLimitValueU32('minUniformBufferOffsetAlignment', {{{ C_STRUCTS.WGPULimits.minUniformBufferOffsetAlignment }}});
setLimitValueU32('minStorageBufferOffsetAlignment', {{{ C_STRUCTS.WGPULimits.minStorageBufferOffsetAlignment }}});

setLimitValueU64('maxUniformBufferBindingSize', {{{ C_STRUCTS.WGPULimits.maxUniformBufferBindingSize }}});
setLimitValueU64('maxStorageBufferBindingSize', {{{ C_STRUCTS.WGPULimits.maxStorageBufferBindingSize }}});

setLimitValueU32('maxVertexBuffers', {{{ C_STRUCTS.WGPULimits.maxVertexBuffers }}});
setLimitValueU32('maxBufferSize', {{{ C_STRUCTS.WGPULimits.maxBufferSize }}});
setLimitValueU32('maxVertexAttributes', {{{ C_STRUCTS.WGPULimits.maxVertexAttributes }}});
setLimitValueU32('maxVertexBufferArrayStride', {{{ C_STRUCTS.WGPULimits.maxVertexBufferArrayStride }}});
setLimitValueU32('maxInterStageShaderComponents', {{{ C_STRUCTS.WGPULimits.maxInterStageShaderComponents }}});
setLimitValueU32('maxInterStageShaderVariables', {{{ C_STRUCTS.WGPULimits.maxInterStageShaderVariables }}});
setLimitValueU32('maxColorAttachments', {{{ C_STRUCTS.WGPULimits.maxColorAttachments }}});
setLimitValueU32('maxColorAttachmentBytesPerSample', {{{ C_STRUCTS.WGPULimits.maxColorAttachmentBytesPerSample }}});
setLimitValueU32('maxComputeWorkgroupStorageSize', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupStorageSize }}});
setLimitValueU32('maxComputeInvocationsPerWorkgroup', {{{ C_STRUCTS.WGPULimits.maxComputeInvocationsPerWorkgroup }}});
setLimitValueU32('maxComputeWorkgroupSizeX', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupSizeX }}});
setLimitValueU32('maxComputeWorkgroupSizeY', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupSizeY }}});
setLimitValueU32('maxComputeWorkgroupSizeZ', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupSizeZ }}});
setLimitValueU32('maxComputeWorkgroupsPerDimension', {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupsPerDimension }}});
WebGPU.fillLimitStruct(device.limits, limitsOutPtr);
return 1;
},

Expand Down Expand Up @@ -2537,8 +2542,9 @@ var LibraryWebGPU = {
},

wgpuAdapterGetLimits: (adapterId, limitsOutPtr) => {
abort('TODO: wgpuAdapterGetLimits unimplemented');
return 0;
var adapter = WebGPU.mgrAdapter.get(adapterId);
WebGPU.fillLimitStruct(adapter.limits, limitsOutPtr);
return 1;
},

wgpuAdapterHasFeature: (adapterId, featureEnumValue) => {
Expand Down Expand Up @@ -2599,11 +2605,13 @@ var LibraryWebGPU = {
setLimitU64IfDefined("maxUniformBufferBindingSize", {{{ C_STRUCTS.WGPULimits.maxUniformBufferBindingSize }}});
setLimitU64IfDefined("maxStorageBufferBindingSize", {{{ C_STRUCTS.WGPULimits.maxStorageBufferBindingSize }}});
setLimitU32IfDefined("maxVertexBuffers", {{{ C_STRUCTS.WGPULimits.maxVertexBuffers }}});
setLimitU32IfDefined("maxBufferSize", {{{ C_STRUCTS.WGPULimits.maxBufferSize }}});
setLimitU32IfDefined("maxVertexAttributes", {{{ C_STRUCTS.WGPULimits.maxVertexAttributes }}});
setLimitU32IfDefined("maxVertexBufferArrayStride", {{{ C_STRUCTS.WGPULimits.maxVertexBufferArrayStride }}});
setLimitU32IfDefined("maxInterStageShaderComponents", {{{ C_STRUCTS.WGPULimits.maxInterStageShaderComponents }}});
setLimitU32IfDefined("maxInterStageShaderVariables", {{{ C_STRUCTS.WGPULimits.maxInterStageShaderVariables }}});
setLimitU32IfDefined("maxColorAttachments", {{{ C_STRUCTS.WGPULimits.maxColorAttachments }}});
setLimitU32IfDefined("maxColorAttachmentBytesPerSample", {{{ C_STRUCTS.WGPULimits.maxColorAttachmentBytesPerSample }}});
setLimitU32IfDefined("maxComputeWorkgroupStorageSize", {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupStorageSize }}});
setLimitU32IfDefined("maxComputeInvocationsPerWorkgroup", {{{ C_STRUCTS.WGPULimits.maxComputeInvocationsPerWorkgroup }}});
setLimitU32IfDefined("maxComputeWorkgroupSizeX", {{{ C_STRUCTS.WGPULimits.maxComputeWorkgroupSizeX }}});
Expand Down
4 changes: 4 additions & 0 deletions test/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4524,6 +4524,10 @@ def test_webgl_simple_extensions(self, simple_enable_extensions, webgl_version):
def test_webgpu_basic_rendering(self, args):
self.btest_exit('webgpu_basic_rendering.cpp', args=['-sUSE_WEBGPU'] + args)

@requires_graphics_hardware
def test_webgpu_required_limits(self):
self.btest_exit('webgpu_required_limits.c', args=['-sUSE_WEBGPU', '-sASYNCIFY'])

# TODO(#19645): Extend this test to proxied WebGPU when it's re-enabled.
@requires_graphics_hardware
def test_webgpu_basic_rendering_pthreads(self):
Expand Down
93 changes: 93 additions & 0 deletions test/webgpu_required_limits.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include <assert.h>
#include <emscripten.h>
#include <stdio.h>
#include <webgpu/webgpu.h>

WGPUSupportedLimits adapter_supported_limits = {
0,
};

void assertLimitsCompatible(WGPULimits required_limits,
WGPULimits supported_limits) {
#define ASSERT_LIMITS_COMPATIBLE(limitName) \
assert(required_limits.limitName == supported_limits.limitName)
ASSERT_LIMITS_COMPATIBLE(maxTextureDimension1D);
ASSERT_LIMITS_COMPATIBLE(maxTextureDimension2D);
ASSERT_LIMITS_COMPATIBLE(maxTextureDimension3D);
ASSERT_LIMITS_COMPATIBLE(maxTextureArrayLayers);
ASSERT_LIMITS_COMPATIBLE(maxBindGroups);
ASSERT_LIMITS_COMPATIBLE(maxBindGroupsPlusVertexBuffers);
ASSERT_LIMITS_COMPATIBLE(maxBindingsPerBindGroup);
ASSERT_LIMITS_COMPATIBLE(maxDynamicUniformBuffersPerPipelineLayout);
ASSERT_LIMITS_COMPATIBLE(maxDynamicStorageBuffersPerPipelineLayout);
ASSERT_LIMITS_COMPATIBLE(maxSampledTexturesPerShaderStage);
ASSERT_LIMITS_COMPATIBLE(maxSamplersPerShaderStage);
ASSERT_LIMITS_COMPATIBLE(maxStorageBuffersPerShaderStage);
ASSERT_LIMITS_COMPATIBLE(maxStorageTexturesPerShaderStage);
ASSERT_LIMITS_COMPATIBLE(maxUniformBuffersPerShaderStage);
ASSERT_LIMITS_COMPATIBLE(minUniformBufferOffsetAlignment);
ASSERT_LIMITS_COMPATIBLE(minStorageBufferOffsetAlignment);
ASSERT_LIMITS_COMPATIBLE(maxUniformBufferBindingSize);
ASSERT_LIMITS_COMPATIBLE(maxStorageBufferBindingSize);
ASSERT_LIMITS_COMPATIBLE(maxVertexBuffers);
ASSERT_LIMITS_COMPATIBLE(maxBufferSize);
ASSERT_LIMITS_COMPATIBLE(maxVertexAttributes);
ASSERT_LIMITS_COMPATIBLE(maxVertexBufferArrayStride);
ASSERT_LIMITS_COMPATIBLE(maxInterStageShaderComponents);
ASSERT_LIMITS_COMPATIBLE(maxInterStageShaderVariables);
ASSERT_LIMITS_COMPATIBLE(maxColorAttachments);
ASSERT_LIMITS_COMPATIBLE(maxColorAttachmentBytesPerSample);
ASSERT_LIMITS_COMPATIBLE(maxComputeWorkgroupStorageSize);
ASSERT_LIMITS_COMPATIBLE(maxComputeInvocationsPerWorkgroup);
ASSERT_LIMITS_COMPATIBLE(maxComputeWorkgroupSizeX);
ASSERT_LIMITS_COMPATIBLE(maxComputeWorkgroupSizeY);
ASSERT_LIMITS_COMPATIBLE(maxComputeWorkgroupSizeZ);
ASSERT_LIMITS_COMPATIBLE(maxComputeWorkgroupsPerDimension);
#undef assertLimitCompatible
kainino0x marked this conversation as resolved.
Show resolved Hide resolved
}

void on_device_request_ended(WGPURequestDeviceStatus status,
WGPUDevice device,
char const* message,
void* userdata) {
assert(status == WGPURequestDeviceStatus_Success);

WGPUSupportedLimits device_supported_limits;
wgpuDeviceGetLimits(device, &device_supported_limits);

// verify that the obtained device fullfils required limits
assertLimitsCompatible(adapter_supported_limits.limits,
device_supported_limits.limits);
}

void on_adapter_request_ended(WGPURequestAdapterStatus status,
WGPUAdapter adapter,
char const* message,
void* userdata) {
if (status == WGPURequestAdapterStatus_Unavailable) {
printf("WebGPU unavailable; exiting cleanly\n");
exit(0);
}

assert(status == WGPURequestAdapterStatus_Success);

wgpuAdapterGetLimits(adapter, &adapter_supported_limits);

// for device limits, require the limits supported by adapter
WGPURequiredLimits device_required_limits = {0,};
device_required_limits.limits = adapter_supported_limits.limits;

WGPUDeviceDescriptor device_desc = {0,};
device_desc.requiredFeatureCount = 0;
device_desc.requiredLimits = &device_required_limits;
wgpuAdapterRequestDevice(adapter, &device_desc, on_device_request_ended, NULL);
}

int main() {
const WGPUInstance instance = wgpuCreateInstance(NULL);

WGPURequestAdapterOptions adapter_options = {0,};
wgpuInstanceRequestAdapter(instance, &adapter_options, on_adapter_request_ended, NULL);

return 0;
kainino0x marked this conversation as resolved.
Show resolved Hide resolved
}