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