| # Dawn Errors | |
| Dawn produces errors for several reasons. The most common is validation errors, indicating that a | |
| given descriptor, configuration, state, or action is not valid according to the WebGPU spec. Errors | |
| can also be produced during exceptional circumstances such as the system running out of GPU memory | |
| or the device being lost. | |
| The messages attached to these errors will frequently be one of the primary tools developers use to | |
| debug problems their applications, so it is important that the messages Dawn returns are useful. | |
| Following the guidelines in document will help ensure that Dawn's errors are clear, informative, and | |
| consistent. | |
| ## Returning Errors | |
| Since errors are expected to be an exceptional case, it's important that code that produces an error | |
| doesn't adversely impact the performance of the error-free path. The best way to ensure that is to | |
| make sure that all errors are returned from within an `if` statement that uses the `DAWN_UNLIKELY()` | |
| macro to indicate that the expression is not expected to evaluate to true. For example: | |
| ```C++ | |
| if (DAWN_UNLIKELY(offset > buffer.size)) { | |
| return DAWN_VALIDATION_ERROR("Offset (%u) is larger than the size (%u) of %s." | |
| offset, buffer.size, buffer); | |
| } | |
| ``` | |
| To simplify producing validation errors, it's strongly suggested that the `DAWN_INVALID_IF()` macro | |
| is used, which will wrap the expression in the `DAWN_UNLIKELY()` macro for you: | |
| ```C++ | |
| // This is equivalent to the previous example. | |
| DAWN_INVALID_IF(offset > buffer.size, "Offset (%u) is larger than the size (%u) of %s." | |
| offset, buffer.size, buffer); | |
| ``` | |
| // TODO: Cover `MaybeError`, `ResultOrError<T>`, `DAWN_TRY(_ASSIGN)`, `DAWN_TRY_CONTEXT`, etc... | |
| ## Error message formatting | |
| Errors returned from `DAWN_INVALID_IF()` or `DAWN_VALIDATION_ERROR()` should follow these guidelines: | |
| **Write error messages as complete sentences. (First word capitalized, ends with a period, etc.)** | |
| * Example: `Command encoding has already finished.` | |
| * Instead of: `encoder finished` | |
| **Error messages should be in the present tense.** | |
| * Example: `Buffer is not large enough...` | |
| * Instead of: `Buffer was not large enough...` | |
| **When possible any values mentioned should be immediately followed in parentheses by the given value.** | |
| * Example: `("Array stride (%u) is not...", stride)` | |
| * Output: `Array stride (16) is not...` | |
| **When possible any object or descriptors should be represented by the object formatted as a string.** | |
| * Example: `("The %s size (%s) is...", buffer, buffer.size)` | |
| * Output: `The [Buffer] size (512) is...` or `The [Buffer "Label"] size (512) is...` | |
| **Enum and bitmask values should be formatted as strings rather than integers or hex values.** | |
| * Example: `("The %s format (%s) is...", texture, texture.format)` | |
| * Output: `The [Texture "Label"] format (TextureFormat::RGBA8Unorm) is...` | |
| **When possible state both the given value and the expected value or limit.** | |
| * Example: `("Offset (%u) is larger than the size (%u) of %s.", offset, buffer.size, buffer)` | |
| * Output: `Offset (256) is larger than the size (144) of [Buffer "Label"].` | |
| **State errors in terms of what failed, rather than how to satisfy the rule.** | |
| * Example: `Binding size (3) is less than the minimum binding size (32).` | |
| * Instead of: `Binding size (3) must not be less than the minimum binding size (32).` | |
| **Don't repeat information given in context.** | |
| * See next section for details | |
| ## Error Context | |
| When calling functions that perform validation consider if calling `DAWN_TRY_CONTEXT()` rather than | |
| `DAWN_TRY()` is appropriate. Context messages, when provided, will be appended to any validation | |
| errors as a type of human readable "callstack". An error with context messages appears will be | |
| formatted as: | |
| ``` | |
| <Primary error message.> | |
| - While <context message lvl 2> | |
| - While <context message lvl 1> | |
| - While <context message lvl 0> | |
| ``` | |
| For example, if a validation error occurs while validating the creation of a BindGroup, the message | |
| may be: | |
| ``` | |
| Binding size (256) is larger than the size (80) of [Buffer "View Matrix"]. | |
| - While validating entries[1] as a Buffer | |
| - While validating [BindGroupDescriptor "Frame Bind Group"] against [BindGroupLayout] | |
| - While calling CreateBindGroup | |
| ``` | |
| // TODO: Guidelines about when to include context | |
| ## Context message formatting | |
| Context messages should follow these guidelines: | |
| **Begin with the action being taken, starting with a lower case. `- While ` will be appended by Dawn.** | |
| * Example: `("validating primitive state")` | |
| * Output: `- While validating primitive state` | |
| **When looping through arrays, indicate the array name and index.** | |
| * Example: `("validating buffers[%u]", i)` | |
| * Output: `- While validating buffers[2]` | |
| **Indicate which descriptors or objects are being examined in as high-level a context as possible.** | |
| * Example: `("validating % against %", descriptor, descriptor->layout)` | |
| * Output: `- While validating [BindGroupDescriptor "Label"] against [BindGroupLayout]` | |
| **When possible, indicate the function call being made as the top-level context, as well as the parameters passed.** | |
| * Example: `("calling %s.CreatePipelineLayout(%s).", this, descriptor)` | |
| * Output: `- While calling [Device].CreatePipelineLayout([PipelineLayoutDescriptor]).` |