• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef DAWNNATIVE_ERROR_H_
16 #define DAWNNATIVE_ERROR_H_
17 
18 #include "absl/strings/str_format.h"
19 #include "common/Result.h"
20 #include "dawn_native/ErrorData.h"
21 #include "dawn_native/webgpu_absl_format_autogen.h"
22 
23 #include <string>
24 
25 namespace dawn_native {
26 
27     enum class InternalErrorType : uint32_t {
28         Validation,
29         DeviceLost,
30         Internal,
31         OutOfMemory
32     };
33 
34     // MaybeError and ResultOrError are meant to be used as return value for function that are not
35     // expected to, but might fail. The handling of error is potentially much slower than successes.
36     using MaybeError = Result<void, ErrorData>;
37 
38     template <typename T>
39     using ResultOrError = Result<T, ErrorData>;
40 
41     // Returning a success is done like so:
42     //   return {}; // for Error
43     //   return SomethingOfTypeT; // for ResultOrError<T>
44     //
45     // Returning an error is done via:
46     //   return DAWN_MAKE_ERROR(errorType, "My error message");
47     //
48     // but shorthand version for specific error types are preferred:
49     //   return DAWN_VALIDATION_ERROR("My error message");
50     //
51     // There are different types of errors that should be used for different purpose:
52     //
53     //   - Validation: these are errors that show the user did something bad, which causes the
54     //     whole call to be a no-op. It's most commonly found in the frontend but there can be some
55     //     backend specific validation in non-conformant backends too.
56     //
57     //   - Out of memory: creation of a Buffer or Texture failed because there isn't enough memory.
58     //     This is similar to validation errors in that the call becomes a no-op and returns an
59     //     error object, but is reported separated from validation to the user.
60     //
61     //   - Device loss: the backend driver reported that the GPU has been lost, which means all
62     //     previous commands magically disappeared and the only thing left to do is clean up.
63     //     Note: Device loss should be used rarely and in most case you want to use Internal
64     //     instead.
65     //
66     //   - Internal: something happened that the backend didn't expect, and it doesn't know
67     //     how to recover from that situation. This causes the device to be lost, but is separate
68     //     from device loss, because the GPU execution is still happening so we need to clean up
69     //     more gracefully.
70     //
71     //   - Unimplemented: same as Internal except it puts "unimplemented" in the error message for
72     //     more clarity.
73 
74 #define DAWN_MAKE_ERROR(TYPE, MESSAGE) \
75     ::dawn_native::ErrorData::Create(TYPE, MESSAGE, __FILE__, __func__, __LINE__)
76 
77 #define DAWN_VALIDATION_ERROR(MESSAGE) DAWN_MAKE_ERROR(InternalErrorType::Validation, MESSAGE)
78 
79 // TODO(dawn:563): Rename to DAWN_VALIDATION_ERROR once all message format strings have been
80 // converted to constexpr.
81 #define DAWN_FORMAT_VALIDATION_ERROR(...) \
82     DAWN_MAKE_ERROR(InternalErrorType::Validation, absl::StrFormat(__VA_ARGS__))
83 
84 #define DAWN_INVALID_IF(EXPR, ...)                                                           \
85     if (DAWN_UNLIKELY(EXPR)) {                                                               \
86         return DAWN_MAKE_ERROR(InternalErrorType::Validation, absl::StrFormat(__VA_ARGS__)); \
87     }                                                                                        \
88     for (;;)                                                                                 \
89     break
90 
91 // DAWN_DEVICE_LOST_ERROR means that there was a real unrecoverable native device lost error.
92 // We can't even do a graceful shutdown because the Device is gone.
93 #define DAWN_DEVICE_LOST_ERROR(MESSAGE) DAWN_MAKE_ERROR(InternalErrorType::DeviceLost, MESSAGE)
94 
95 // DAWN_INTERNAL_ERROR means Dawn hit an unexpected error in the backend and should try to
96 // gracefully shut down.
97 #define DAWN_INTERNAL_ERROR(MESSAGE) DAWN_MAKE_ERROR(InternalErrorType::Internal, MESSAGE)
98 
99 #define DAWN_FORMAT_INTERNAL_ERROR(...) \
100     DAWN_MAKE_ERROR(InternalErrorType::Internal, absl::StrFormat(__VA_ARGS__))
101 
102 #define DAWN_UNIMPLEMENTED_ERROR(MESSAGE) \
103     DAWN_MAKE_ERROR(InternalErrorType::Internal, std::string("Unimplemented: ") + MESSAGE)
104 
105 // DAWN_OUT_OF_MEMORY_ERROR means we ran out of memory. It may be used as a signal internally in
106 // Dawn to free up unused resources. Or, it may bubble up to the application to signal an allocation
107 // was too large or they should free some existing resources.
108 #define DAWN_OUT_OF_MEMORY_ERROR(MESSAGE) DAWN_MAKE_ERROR(InternalErrorType::OutOfMemory, MESSAGE)
109 
110 #define DAWN_CONCAT1(x, y) x##y
111 #define DAWN_CONCAT2(x, y) DAWN_CONCAT1(x, y)
112 #define DAWN_LOCAL_VAR DAWN_CONCAT2(_localVar, __LINE__)
113 
114     // When Errors aren't handled explicitly, calls to functions returning errors should be
115     // wrapped in an DAWN_TRY. It will return the error if any, otherwise keep executing
116     // the current function.
117 #define DAWN_TRY(EXPR) DAWN_TRY_WITH_CLEANUP(EXPR, {})
118 
119 #define DAWN_TRY_CONTEXT(EXPR, ...) \
120     DAWN_TRY_WITH_CLEANUP(EXPR, { error->AppendContext(absl::StrFormat(__VA_ARGS__)); })
121 
122 #define DAWN_TRY_WITH_CLEANUP(EXPR, BODY)                                                    \
123     {                                                                                        \
124         auto DAWN_LOCAL_VAR = EXPR;                                                          \
125         if (DAWN_UNLIKELY(DAWN_LOCAL_VAR.IsError())) {                                       \
126             std::unique_ptr<::dawn_native::ErrorData> error = DAWN_LOCAL_VAR.AcquireError(); \
127             {BODY} /* comment to force the formatter to insert a newline */                  \
128             error->AppendBacktrace(__FILE__, __func__, __LINE__);                            \
129             return {std::move(error)};                                                       \
130         }                                                                                    \
131     }                                                                                        \
132     for (;;)                                                                                 \
133     break
134 
135     // DAWN_TRY_ASSIGN is the same as DAWN_TRY for ResultOrError and assigns the success value, if
136     // any, to VAR.
137 #define DAWN_TRY_ASSIGN(VAR, EXPR) DAWN_TRY_ASSIGN_WITH_CLEANUP(VAR, EXPR, {})
138 
139     // Argument helpers are used to determine which macro implementations should be called when
140     // overloading with different number of variables.
141 #define DAWN_ERROR_UNIMPLEMENTED_MACRO_(...) UNREACHABLE()
142 #define DAWN_ERROR_GET_5TH_ARG_HELPER_(_1, _2, _3, _4, NAME, ...) NAME
143 #define DAWN_ERROR_GET_5TH_ARG_(args) DAWN_ERROR_GET_5TH_ARG_HELPER_ args
144 
145     // DAWN_TRY_ASSIGN_WITH_CLEANUP is overloaded with 2 version so that users can override the
146     // return value of the macro when necessary. This is particularly useful if the function
147     // calling the macro may want to return void instead of the error, i.e. in a test where we may
148     // just want to assert and fail if the assign cannot go through. In both the cleanup and return
149     // clauses, users can use the `error` variable to access the pointer to the acquired error.
150     //
151     // Example usages:
152     //     3 Argument Case:
153     //          Result res;
154     //          DAWN_TRY_ASSIGN_WITH_CLEANUP(
155     //              res, GetResultOrErrorFunction(), { AddAdditionalErrorInformation(error.get()); }
156     //          );
157     //
158     //     4 Argument Case:
159     //          bool FunctionThatReturnsBool() {
160     //              DAWN_TRY_ASSIGN_WITH_CLEANUP(
161     //                  res, GetResultOrErrorFunction(),
162     //                  { AddAdditionalErrorInformation(error.get()); },
163     //                  false
164     //              );
165     //          }
166 #define DAWN_TRY_ASSIGN_WITH_CLEANUP(...)                                       \
167     DAWN_ERROR_GET_5TH_ARG_((__VA_ARGS__, DAWN_TRY_ASSIGN_WITH_CLEANUP_IMPL_4_, \
168                              DAWN_TRY_ASSIGN_WITH_CLEANUP_IMPL_3_,              \
169                              DAWN_ERROR_UNIMPLEMENTED_MACRO_))                  \
170     (__VA_ARGS__)
171 
172 #define DAWN_TRY_ASSIGN_WITH_CLEANUP_IMPL_3_(VAR, EXPR, BODY) \
173     DAWN_TRY_ASSIGN_WITH_CLEANUP_IMPL_4_(VAR, EXPR, BODY, std::move(error))
174 
175 #define DAWN_TRY_ASSIGN_WITH_CLEANUP_IMPL_4_(VAR, EXPR, BODY, RET)            \
176     {                                                                         \
177         auto DAWN_LOCAL_VAR = EXPR;                                           \
178         if (DAWN_UNLIKELY(DAWN_LOCAL_VAR.IsError())) {                        \
179             std::unique_ptr<ErrorData> error = DAWN_LOCAL_VAR.AcquireError(); \
180             {BODY} /* comment to force the formatter to insert a newline */   \
181             error->AppendBacktrace(__FILE__, __func__, __LINE__);             \
182             return (RET);                                                     \
183         }                                                                     \
184         VAR = DAWN_LOCAL_VAR.AcquireSuccess();                                \
185     }                                                                         \
186     for (;;)                                                                  \
187     break
188 
189     // Assert that errors are device loss so that we can continue with destruction
190     void IgnoreErrors(MaybeError maybeError);
191 
192     wgpu::ErrorType ToWGPUErrorType(InternalErrorType type);
193     InternalErrorType FromWGPUErrorType(wgpu::ErrorType type);
194 
195 }  // namespace dawn_native
196 
197 #endif  // DAWNNATIVE_ERROR_H_
198