Skip to content

Commit

Permalink
Fix min device limits
Browse files Browse the repository at this point in the history
  • Loading branch information
eliemichel committed Jul 9, 2024
1 parent 2afde78 commit 595f101
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 86 deletions.
60 changes: 36 additions & 24 deletions basic-3d-rendering/input-geometry/a-first-vertex-attribute.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,22 +139,6 @@ And we get nothing more than required, so that if we forget to update the initia

This initial check is done by specifying a non null `requiredLimits` pointer in the device descriptor. I suggest that we create a method `GetRequiredLimits(Adapter adapter)` dedicated to setting our app requirements:

````{tab} With webgpu.hpp
```{lit} C++, Build device descriptor (append)
// Before adapter.requestDevice(deviceDesc)
RequiredLimits requiredLimits = GetRequiredLimits(adapter);
deviceDesc.requiredLimits = &requiredLimits;
```
````

````{tab} Vanilla webgpu.h
```{lit} C++, Build device descriptor (append, for tangle root "Vanilla")
// Before requestDeviceSync(adapter, &deviceDesc)
WGPURequiredLimits requiredLimits = GetRequiredLimits(adapter);
deviceDesc.requiredLimits = &requiredLimits;
```
````

````{tab} With webgpu.hpp
```{lit} C++, Private methods (append)
// In Application class
Expand All @@ -171,9 +155,23 @@ private:
```
````

```{note}
We do not really need the adapter to determine the required limits in our case, but I pass it as an argument because it **can become useful** in a scenario where a **different quality of device** is requested depending on the **adapter's capabilities**.
We then call this method while building the **device descriptor**:

````{tab} With webgpu.hpp
```{lit} C++, Build device descriptor (append)
// Before adapter.requestDevice(deviceDesc)
RequiredLimits requiredLimits = GetRequiredLimits(adapter);
deviceDesc.requiredLimits = &requiredLimits;
```
````

````{tab} Vanilla webgpu.h
```{lit} C++, Build device descriptor (append, for tangle root "Vanilla")
// Before requestDeviceSync(adapter, &deviceDesc)
WGPURequiredLimits requiredLimits = GetRequiredLimits(adapter);
deviceDesc.requiredLimits = &requiredLimits;
```
````

The required limits follow the **same structure** than the supported limits, we can customize some of them for our vertex attribute:

Expand All @@ -195,8 +193,8 @@ RequiredLimits Application::GetRequiredLimits(Adapter adapter) const {
requiredLimits.limits.maxBufferSize = 6 * 2 * sizeof(float);
// Maximum stride between 2 consecutive vertices in the vertex buffer
requiredLimits.limits.maxVertexBufferArrayStride = 2 * sizeof(float);
// This must be set even if we do not use storage buffers for now
requiredLimits.limits.minStorageBufferOffsetAlignment = supportedLimits.limits.minStorageBufferOffsetAlignment;

{{Other device limits}}

return requiredLimits;
}
Expand Down Expand Up @@ -232,6 +230,8 @@ WGPURequiredLimits Application::GetRequiredLimits(WGPUAdapter adapter) const {
// Maximum stride between 2 consecutive vertices in the vertex buffer
requiredLimits.limits.maxVertexBufferArrayStride = 2 * sizeof(float);

{{Other device limits}}

return requiredLimits;
}
```
Expand Down Expand Up @@ -269,14 +269,14 @@ limits.maxComputeWorkgroupsPerDimension = WGPU_LIMIT_U32_UNDEFINED;
```
````
```{important}
Notice how I **initialized the required limits** object with `= Default` above. This is a syntactic helper provided by the `webgpu.hpp` wrapper for all structs to prevent us from manually setting default values. In this case it sets all limits to `WGPU_LIMIT_U32_UNDEFINED` or `WGPU_LIMIT_U64_UNDEFINED` to mean that there is no requirement.
```
```{lit} C++, Application implementation (hidden, append, also for tangle root "Vanilla")
{{GetRequiredLimits method}}
```
```{important}
Notice how I **initialized the required limits** object with `= Default` above. This is a syntactic helper provided by the `webgpu.hpp` wrapper for all structs to prevent us from manually setting default values. In this case it sets all limits to `WGPU_LIMIT_U32_UNDEFINED` or `WGPU_LIMIT_U64_UNDEFINED` to mean that there is no requirement.
```
I now get these more secure supported limits:
```
Expand All @@ -286,6 +286,18 @@ device.maxVertexBuffers: 1
I recommend you have a look at all the fields of the `WGPULimits` structure in `webgpu.h` so that you know when to add something to the required limits.
````{note}
There are two limits that may cause issue even if set to `WGPU_LIMIT_U32_UNDEFINED`, namely the one that set **minimums** rather than maximums. The default "undefined" value may not be supported by the adapter, so **in this case only** I suggest we forward values from the **supported limits**:
```{lit} C++, Other device limits (also for tangle root "Vanilla")
// These two limits are different because they are "minimum" limits,
// they are the only ones we are may forward from the adapter's supported
// limits.
requiredLimits.limits.minUniformBufferOffsetAlignment = supportedLimits.limits.minUniformBufferOffsetAlignment;
requiredLimits.limits.minStorageBufferOffsetAlignment = supportedLimits.limits.minStorageBufferOffsetAlignment;
```
````

### Default capabilities

The official default capabilities can be found in [the specification](https://www.w3.org/TR/webgpu/#limit-default). This also mentions that:
Expand Down
67 changes: 5 additions & 62 deletions basic-3d-rendering/input-geometry/multiple-attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,68 +165,11 @@ return vec4f(in.color, 1.0); // use the interpolated color coming from the verte

There is a **limit on the number of components** that can be forwarded from vertex to fragment shader. In our case, we ask for 3 (float) components:

```{lit} C++, List required limits (hidden, also for tangle root "Vanilla")
// NB: In this auto-generated ("tangled") source code, updates to limits get
// appended at the end of this block, so pay attention to the last line that
// affects a given limit field.
// We use at most 1 vertex attribute for now
requiredLimits.limits.maxVertexAttributes = 1;
// We should also tell that we use 1 vertex buffers
requiredLimits.limits.maxVertexBuffers = 1;
// Maximum size of a buffer is 6 vertices of 2 float each
requiredLimits.limits.maxBufferSize = 6 * 2 * sizeof(float);
// Maximum stride between 2 consecutive vertices in the vertex buffer
requiredLimits.limits.maxVertexBufferArrayStride = 2 * sizeof(float);
// This must be set even if we do not use storage buffers for now
requiredLimits.limits.minStorageBufferOffsetAlignment = supportedLimits.limits.minStorageBufferOffsetAlignment;
```

```{lit} C++, List required limits (append, also for tangle root "Vanilla")
```{lit} C++, Other device limits (append, also for tangle root "Vanilla")
// There is a maximum of 3 float forwarded from vertex to fragment shader
requiredLimits.limits.maxInterStageShaderComponents = 3;
```

```{lit} C++, GetRequiredLimits method (hidden, replace)
RequiredLimits Application::GetRequiredLimits(Adapter adapter) const {
// Get adapter supported limits, in case we need them
SupportedLimits supportedLimits;
adapter.getLimits(&supportedLimits);
// Don't forget to = Default
RequiredLimits requiredLimits = Default;
{{List required limits}}
return requiredLimits;
}
```

```{lit} C++, GetRequiredLimits method (hidden, replace, for tangle root "Vanilla")
// If you do not use webgpu.hpp, I suggest you create a function to init the
// WGPULimits structure:
void setDefault(WGPULimits &limits) {
limits.maxTextureDimension1D = WGPU_LIMIT_U32_UNDEFINED;
limits.maxTextureDimension2D = WGPU_LIMIT_U32_UNDEFINED;
limits.maxTextureDimension3D = WGPU_LIMIT_U32_UNDEFINED;
{{Set everything to WGPU_LIMIT_U32_UNDEFINED or WGPU_LIMIT_U64_UNDEFINED to mean no limit}}
}
WGPURequiredLimits Application::GetRequiredLimits(WGPUAdapter adapter) const {
// Get adapter supported limits, in case we need them
WGPUSupportedLimits supportedLimits;
supportedLimits.nextInChain = nullptr;
wgpuAdapterGetLimits(adapter, &supportedLimits);
WGPURequiredLimits requiredLimits{};
setDefault(requiredLimits.limits);
{{List required limits}}
return requiredLimits;
}
```

Vertex Buffer Layout
--------------------

Expand All @@ -237,7 +180,7 @@ There are **different ways** of feeding multiple attributes to the vertex fetch
````{admonition} Device limits
Before anything, do not forget to **increase the vertex attribute limit** of your device:
```{lit} C++, List required limits (append, also for tangle root "Vanilla")
```{lit} C++, Other device limits (append, also for tangle root "Vanilla")
requiredLimits.limits.maxVertexAttributes = 2;
// ^ This was 1
```
Expand Down Expand Up @@ -337,7 +280,7 @@ vertexBufferLayout.stepMode = WGPUVertexStepMode_Vertex;
````{admonition} Device limits
We thus need to update the **buffer size and stride limits**:
```{lit} C++, List required limits (append, also for tangle root "Vanilla")
```{lit} C++, Other device limits (append, also for tangle root "Vanilla")
requiredLimits.limits.maxBufferSize = 6 * 5 * sizeof(float);
requiredLimits.limits.maxVertexBufferArrayStride = 5 * sizeof(float);
```
Expand Down Expand Up @@ -417,7 +360,7 @@ Another possible data layout is to have **two different buffers** for the two at
````{admonition} Device limits
Make sure to change the device limit to support this:
```{lit} C++, List required limits (append, also for tangle root "Vanilla")
```{lit} C++, Other device limits (append, also for tangle root "Vanilla")
requiredLimits.limits.maxVertexBuffers = 2;
```
````
Expand Down Expand Up @@ -454,7 +397,7 @@ assert(vertexCount == static_cast<uint32_t>(colorData.size() / 3));
````{note}
This time, the maximum buffer size/stride can be lower:
```{lit} C++, List required limits (append, also for tangle root "Vanilla")
```{lit} C++, Other device limits (append, also for tangle root "Vanilla")
requiredLimits.limits.maxBufferSize = 6 * 3 * sizeof(float);
requiredLimits.limits.maxVertexBufferArrayStride = 3 * sizeof(float);
```
Expand Down

0 comments on commit 595f101

Please sign in to comment.