| # 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]).` |