diff --git a/BUILD.gn b/BUILD.gn index 368a8b9ac3..e03aa8da4f 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -208,6 +208,8 @@ source_set("libdawn_native_sources") { "src/dawn_native/Error.h", "src/dawn_native/ErrorData.cpp", "src/dawn_native/ErrorData.h", + "src/dawn_native/ErrorInjector.cpp", + "src/dawn_native/ErrorInjector.h", "src/dawn_native/ErrorScope.cpp", "src/dawn_native/ErrorScope.h", "src/dawn_native/ErrorScopeTracker.cpp", @@ -958,6 +960,10 @@ source_set("dawn_white_box_tests_sources") { if (is_linux) { sources += [ "src/tests/white_box/VulkanImageWrappingTests.cpp" ] } + + if (dawn_enable_error_injection) { + sources += [ "src/tests/white_box/VulkanErrorInjectorTests.cpp" ] + } } if (dawn_enable_d3d12) { diff --git a/scripts/dawn_features.gni b/scripts/dawn_features.gni index ab1d595f6b..7cbb82f444 100644 --- a/scripts/dawn_features.gni +++ b/scripts/dawn_features.gni @@ -12,6 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +import("//build_overrides/build.gni") + +if (build_with_chromium) { + import("//build/config/sanitizers/sanitizers.gni") +} + declare_args() { # Enable Dawn's ASSERTs even in release builds dawn_always_assert = false @@ -51,6 +57,10 @@ declare_args() { # Because of how the Vulkan loader works, setting this make Dawn only able # to find the Swiftshader ICD and not the others. dawn_use_swiftshader = false + + # Enables error injection for faking failures to native API calls + dawn_enable_error_injection = + is_debug || (build_with_chromium && use_fuzzing_engine) } # GN does not allow reading a variable defined in the same declare_args(). diff --git a/src/common/BUILD.gn b/src/common/BUILD.gn index 4c06976bbd..80ee2c25e0 100644 --- a/src/common/BUILD.gn +++ b/src/common/BUILD.gn @@ -67,6 +67,10 @@ config("dawn_internal") { defines += [ "DAWN_USE_X11" ] } + if (dawn_enable_error_injection) { + defines += [ "DAWN_ENABLE_ERROR_INJECTION" ] + } + # Only internal Dawn targets can use this config, this means only targets in # this BUILD.gn file. visibility = [ ":*" ] diff --git a/src/common/vulkan_platform.h b/src/common/vulkan_platform.h index 0011a3103c..35742a70d8 100644 --- a/src/common/vulkan_platform.h +++ b/src/common/vulkan_platform.h @@ -78,18 +78,18 @@ class alignas(kNativeVkHandleAlignment) VkNonDispatchableHandle { VkNonDispatchableHandle& operator=(const VkNonDispatchableHandle&) = default; // Comparisons between handles - bool operator==(VkNonDispatchableHandle other) { + bool operator==(VkNonDispatchableHandle other) const { return mHandle == other.mHandle; } - bool operator!=(VkNonDispatchableHandle other) { + bool operator!=(VkNonDispatchableHandle other) const { return mHandle != other.mHandle; } // Comparisons between handles and VK_NULL_HANDLE - bool operator==(std::nullptr_t) { + bool operator==(std::nullptr_t) const { return mHandle == 0; } - bool operator!=(std::nullptr_t) { + bool operator!=(std::nullptr_t) const { return mHandle != 0; } diff --git a/src/dawn_native/Error.h b/src/dawn_native/Error.h index 172263e0ac..b4b968ae13 100644 --- a/src/dawn_native/Error.h +++ b/src/dawn_native/Error.h @@ -61,7 +61,7 @@ namespace dawn_native { { \ auto DAWN_LOCAL_VAR = EXPR; \ if (DAWN_UNLIKELY(DAWN_LOCAL_VAR.IsError())) { \ - ErrorData* error = DAWN_LOCAL_VAR.AcquireError(); \ + ::dawn_native::ErrorData* error = DAWN_LOCAL_VAR.AcquireError(); \ ::dawn_native::AppendBacktrace(error, __FILE__, __func__, __LINE__); \ return {std::move(error)}; \ } \ diff --git a/src/dawn_native/ErrorInjector.cpp b/src/dawn_native/ErrorInjector.cpp new file mode 100644 index 0000000000..d82102816e --- /dev/null +++ b/src/dawn_native/ErrorInjector.cpp @@ -0,0 +1,69 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/ErrorInjector.h" + +#include "common/Assert.h" + +namespace dawn_native { + + namespace { + + bool sIsEnabled = false; + uint64_t sNextIndex = 0; + uint64_t sInjectedFailureIndex = 0; + bool sHasPendingInjectedError = false; + + } // anonymous namespace + + void EnableErrorInjector() { + sIsEnabled = true; + } + + void DisableErrorInjector() { + sIsEnabled = false; + } + + void ClearErrorInjector() { + sNextIndex = 0; + sHasPendingInjectedError = false; + } + + bool ErrorInjectorEnabled() { + return sIsEnabled; + } + + uint64_t AcquireErrorInjectorCallCount() { + uint64_t count = sNextIndex; + ClearErrorInjector(); + return count; + } + + bool ShouldInjectError() { + uint64_t index = sNextIndex++; + if (sHasPendingInjectedError && index == sInjectedFailureIndex) { + sHasPendingInjectedError = false; + return true; + } + return false; + } + + void InjectErrorAt(uint64_t index) { + // Only one error can be injected at a time. + ASSERT(!sHasPendingInjectedError); + sInjectedFailureIndex = index; + sHasPendingInjectedError = true; + } + +} // namespace dawn_native diff --git a/src/dawn_native/ErrorInjector.h b/src/dawn_native/ErrorInjector.h new file mode 100644 index 0000000000..7fe3581f31 --- /dev/null +++ b/src/dawn_native/ErrorInjector.h @@ -0,0 +1,75 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_ERRORINJECTOR_H_ +#define DAWNNATIVE_ERRORINJECTOR_H_ + +#include +#include + +namespace dawn_native { + + template + struct InjectedErrorResult { + ErrorType error; + bool injected; + }; + + void EnableErrorInjector(); + void DisableErrorInjector(); + void ClearErrorInjector(); + + bool ErrorInjectorEnabled(); + uint64_t AcquireErrorInjectorCallCount(); + + bool ShouldInjectError(); + + template + InjectedErrorResult MaybeInjectError(ErrorType errorType) { + return InjectedErrorResult{errorType, ShouldInjectError()}; + } + + template + InjectedErrorResult MaybeInjectError(ErrorType errorType, ErrorTypes... errorTypes) { + if (ShouldInjectError()) { + return InjectedErrorResult{errorType, true}; + } + return MaybeInjectError(errorTypes...); + } + + void InjectErrorAt(uint64_t index); + +} // namespace dawn_native + +#if defined(DAWN_ENABLE_ERROR_INJECTION) + +# define INJECT_ERROR_OR_RUN(stmt, ...) \ + [&]() { \ + if (DAWN_UNLIKELY(::dawn_native::ErrorInjectorEnabled())) { \ + /* Only used for testing and fuzzing, so it's okay if this is deoptimized */ \ + auto injectedError = ::dawn_native::MaybeInjectError(__VA_ARGS__); \ + if (injectedError.injected) { \ + return injectedError.error; \ + } \ + } \ + return (stmt); \ + }() + +#else + +# define INJECT_ERROR_OR_RUN(stmt, ...) stmt + +#endif + +#endif // DAWNNATIVE_ERRORINJECTOR_H_ diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp index 1b998145a1..487e7efac5 100644 --- a/src/dawn_native/vulkan/DeviceVk.cpp +++ b/src/dawn_native/vulkan/DeviceVk.cpp @@ -459,7 +459,9 @@ namespace dawn_native { namespace vulkan { VkFence fence = mFencesInFlight.front().first; Serial fenceSerial = mFencesInFlight.front().second; - VkResult result = fn.GetFenceStatus(mVkDevice, fence); + VkResult result = VkResult::WrapUnsafe( + INJECT_ERROR_OR_RUN(fn.GetFenceStatus(mVkDevice, fence), VK_ERROR_DEVICE_LOST)); + // TODO: Handle DeviceLost error. ASSERT(result == VK_SUCCESS || result == VK_NOT_READY); // Fence are added in order, so we can stop searching as soon @@ -699,7 +701,7 @@ namespace dawn_native { namespace vulkan { } MaybeError Device::WaitForIdleForDestruction() { - VkResult waitIdleResult = fn.QueueWaitIdle(mQueue); + VkResult waitIdleResult = VkResult::WrapUnsafe(fn.QueueWaitIdle(mQueue)); // Ignore the result of QueueWaitIdle: it can return OOM which we can't really do anything // about, Device lost, which means workloads running on the GPU are no longer accessible // (so they are as good as waited on) or success. @@ -713,10 +715,15 @@ namespace dawn_native { namespace vulkan { Serial fenceSerial = mFencesInFlight.front().second; ASSERT(fenceSerial > mCompletedSerial); - VkResult result = VK_TIMEOUT; + VkResult result = VkResult::WrapUnsafe(VK_TIMEOUT); do { - result = fn.WaitForFences(mVkDevice, 1, &fence, true, UINT64_MAX); + result = VkResult::WrapUnsafe( + INJECT_ERROR_OR_RUN(fn.WaitForFences(mVkDevice, 1, &fence, true, UINT64_MAX), + VK_ERROR_DEVICE_LOST)); } while (result == VK_TIMEOUT); + + // TODO: Handle errors + ASSERT(result == VK_SUCCESS); fn.DestroyFence(mVkDevice, fence, nullptr); mFencesInFlight.pop(); diff --git a/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp b/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp index 58dcd666d6..25fa5ee0db 100644 --- a/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp +++ b/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp @@ -69,15 +69,12 @@ namespace dawn_native { namespace vulkan { allocateInfo.memoryTypeIndex = mMemoryTypeIndex; VkDeviceMemory allocatedMemory = VK_NULL_HANDLE; - VkResult allocationResult = mDevice->fn.AllocateMemory( - mDevice->GetVkDevice(), &allocateInfo, nullptr, &allocatedMemory); - // Handle vkAllocateMemory error but differentiate OOM that we want to surface to - // the application. - if (allocationResult == VK_ERROR_OUT_OF_DEVICE_MEMORY) { - return DAWN_OUT_OF_MEMORY_ERROR("OOM while creating the Vkmemory"); - } - DAWN_TRY(CheckVkSuccess(allocationResult, "vkAllocateMemory")); + // First check OOM that we want to surface to the application. + DAWN_TRY(CheckVkOOMThenSuccess( + mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo, nullptr, + &allocatedMemory), + "vkAllocateMemory")); ASSERT(allocatedMemory != VK_NULL_HANDLE); return {std::make_unique(allocatedMemory, mMemoryTypeIndex)}; diff --git a/src/dawn_native/vulkan/VulkanError.cpp b/src/dawn_native/vulkan/VulkanError.cpp index 543c9b0964..76f4a9a977 100644 --- a/src/dawn_native/vulkan/VulkanError.cpp +++ b/src/dawn_native/vulkan/VulkanError.cpp @@ -56,17 +56,32 @@ namespace dawn_native { namespace vulkan { return "VK_ERROR_FORMAT_NOT_SUPPORTED"; case VK_ERROR_FRAGMENTED_POOL: return "VK_ERROR_FRAGMENTED_POOL"; + case VK_FAKE_DEVICE_OOM_FOR_TESTING: + return "VK_FAKE_DEVICE_OOM_FOR_TESTING"; + case VK_FAKE_ERROR_FOR_TESTING: + return "VK_FAKE_ERROR_FOR_TESTING"; default: return ""; } } - MaybeError CheckVkSuccess(VkResult result, const char* context) { + MaybeError CheckVkSuccessImpl(VkResult result, const char* context) { + if (DAWN_LIKELY(result == VK_SUCCESS)) { + return {}; + } + std::string message = std::string(context) + " failed with " + VkResultAsString(result); + return DAWN_DEVICE_LOST_ERROR(message); + } + + MaybeError CheckVkOOMThenSuccessImpl(VkResult result, const char* context) { if (DAWN_LIKELY(result == VK_SUCCESS)) { return {}; } std::string message = std::string(context) + " failed with " + VkResultAsString(result); + if (result == VK_ERROR_OUT_OF_DEVICE_MEMORY || result == VK_FAKE_DEVICE_OOM_FOR_TESTING) { + return DAWN_OUT_OF_MEMORY_ERROR(message); + } return DAWN_DEVICE_LOST_ERROR(message); } diff --git a/src/dawn_native/vulkan/VulkanError.h b/src/dawn_native/vulkan/VulkanError.h index 3dedece1ad..c4ae5e7acf 100644 --- a/src/dawn_native/vulkan/VulkanError.h +++ b/src/dawn_native/vulkan/VulkanError.h @@ -15,19 +15,35 @@ #ifndef DAWNNATIVE_VULKAN_VULKANERROR_H_ #define DAWNNATIVE_VULKAN_VULKANERROR_H_ -#include "common/vulkan_platform.h" -#include "dawn_native/Error.h" +#include "dawn_native/ErrorInjector.h" +#include "dawn_native/vulkan/VulkanFunctions.h" + +constexpr VkResult VK_FAKE_ERROR_FOR_TESTING = VK_RESULT_MAX_ENUM; +constexpr VkResult VK_FAKE_DEVICE_OOM_FOR_TESTING = static_cast(VK_RESULT_MAX_ENUM - 1); namespace dawn_native { namespace vulkan { // Returns a string version of the result. const char* VkResultAsString(VkResult result); - // Returns a success only if result if VK_SUCCESS, an error with the context and stringified - // result value instead. Can be used like this: - // - // DAWN_TRY(CheckVkSuccess(vkDoSomething, "doing something")); - MaybeError CheckVkSuccess(VkResult result, const char* context); + MaybeError CheckVkSuccessImpl(VkResult result, const char* context); + MaybeError CheckVkOOMThenSuccessImpl(VkResult result, const char* context); + +// Returns a success only if result if VK_SUCCESS, an error with the context and stringified +// result value instead. Can be used like this: +// +// DAWN_TRY(CheckVkSuccess(vkDoSomething, "doing something")); +#define CheckVkSuccess(resultIn, contextIn) \ + ::dawn_native::vulkan::CheckVkSuccessImpl( \ + ::dawn_native::vulkan::VkResult::WrapUnsafe( \ + INJECT_ERROR_OR_RUN(resultIn, VK_FAKE_ERROR_FOR_TESTING)), \ + contextIn) + +#define CheckVkOOMThenSuccess(resultIn, contextIn) \ + ::dawn_native::vulkan::CheckVkOOMThenSuccessImpl( \ + ::dawn_native::vulkan::VkResult::WrapUnsafe(INJECT_ERROR_OR_RUN( \ + resultIn, VK_FAKE_DEVICE_OOM_FOR_TESTING, VK_FAKE_ERROR_FOR_TESTING)), \ + contextIn) }} // namespace dawn_native::vulkan diff --git a/src/dawn_native/vulkan/VulkanFunctions.h b/src/dawn_native/vulkan/VulkanFunctions.h index eb5a4725d7..cb6960c049 100644 --- a/src/dawn_native/vulkan/VulkanFunctions.h +++ b/src/dawn_native/vulkan/VulkanFunctions.h @@ -266,6 +266,28 @@ namespace dawn_native { namespace vulkan { #endif }; + // Create a wrapper around VkResult in the dawn_native::vulkan namespace. This shadows the + // default VkResult (::VkResult). This ensures that assigning or creating a VkResult from a raw + // ::VkResult uses WrapUnsafe. This makes it clear that users of VkResult must be intentional + // about handling error cases. + class VkResult { + public: + constexpr static VkResult WrapUnsafe(::VkResult value) { + return VkResult(value); + } + + constexpr operator ::VkResult() const { + return mValue; + } + + private: + // Private. Use VkResult::WrapUnsafe instead. + constexpr VkResult(::VkResult value) : mValue(value) { + } + + ::VkResult mValue; + }; + }} // namespace dawn_native::vulkan #endif // DAWNNATIVE_VULKAN_VULKANFUNCTIONS_H_ diff --git a/src/dawn_native/vulkan/VulkanInfo.cpp b/src/dawn_native/vulkan/VulkanInfo.cpp index ecaa20e706..0ac551422f 100644 --- a/src/dawn_native/vulkan/VulkanInfo.cpp +++ b/src/dawn_native/vulkan/VulkanInfo.cpp @@ -16,37 +16,38 @@ #include "dawn_native/vulkan/AdapterVk.h" #include "dawn_native/vulkan/BackendVk.h" +#include "dawn_native/vulkan/VulkanError.h" #include -namespace { - bool IsLayerName(const VkLayerProperties& layer, const char* name) { - return strncmp(layer.layerName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0; - } - - bool IsExtensionName(const VkExtensionProperties& extension, const char* name) { - return strncmp(extension.extensionName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0; - } - - bool EnumerateInstanceExtensions(const char* layerName, - const dawn_native::vulkan::VulkanFunctions& vkFunctions, - std::vector* extensions) { - uint32_t count = 0; - VkResult result = - vkFunctions.EnumerateInstanceExtensionProperties(layerName, &count, nullptr); - if (result != VK_SUCCESS && result != VK_INCOMPLETE) { - return false; - } - extensions->resize(count); - result = - vkFunctions.EnumerateInstanceExtensionProperties(layerName, &count, extensions->data()); - return (result == VK_SUCCESS); - } - -} // namespace - namespace dawn_native { namespace vulkan { + namespace { + bool IsLayerName(const VkLayerProperties& layer, const char* name) { + return strncmp(layer.layerName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0; + } + + bool IsExtensionName(const VkExtensionProperties& extension, const char* name) { + return strncmp(extension.extensionName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0; + } + + bool EnumerateInstanceExtensions(const char* layerName, + const dawn_native::vulkan::VulkanFunctions& vkFunctions, + std::vector* extensions) { + uint32_t count = 0; + VkResult result = VkResult::WrapUnsafe( + vkFunctions.EnumerateInstanceExtensionProperties(layerName, &count, nullptr)); + if (result != VK_SUCCESS && result != VK_INCOMPLETE) { + return false; + } + extensions->resize(count); + result = VkResult::WrapUnsafe(vkFunctions.EnumerateInstanceExtensionProperties( + layerName, &count, extensions->data())); + return (result == VK_SUCCESS); + } + + } // namespace + const char kLayerNameLunargStandardValidation[] = "VK_LAYER_LUNARG_standard_validation"; const char kLayerNameLunargVKTrace[] = "VK_LAYER_LUNARG_vktrace"; const char kLayerNameRenderDocCapture[] = "VK_LAYER_RENDERDOC_Capture"; @@ -85,7 +86,8 @@ namespace dawn_native { namespace vulkan { // Gather the info about the instance layers { uint32_t count = 0; - VkResult result = vkFunctions.EnumerateInstanceLayerProperties(&count, nullptr); + VkResult result = + VkResult::WrapUnsafe(vkFunctions.EnumerateInstanceLayerProperties(&count, nullptr)); // From the Vulkan spec result should be success if there are 0 layers, // incomplete otherwise. This means that both values represent a success. // This is the same for all Enumarte functions @@ -94,10 +96,9 @@ namespace dawn_native { namespace vulkan { } info.layers.resize(count); - result = vkFunctions.EnumerateInstanceLayerProperties(&count, info.layers.data()); - if (result != VK_SUCCESS) { - return DAWN_DEVICE_LOST_ERROR("vkEnumerateInstanceLayerProperties"); - } + DAWN_TRY(CheckVkSuccess( + vkFunctions.EnumerateInstanceLayerProperties(&count, info.layers.data()), + "vkEnumerateInstanceLayerProperties")); for (const auto& layer : info.layers) { if (IsLayerName(layer, kLayerNameLunargStandardValidation)) { @@ -202,16 +203,16 @@ namespace dawn_native { namespace vulkan { const VulkanFunctions& vkFunctions = backend.GetFunctions(); uint32_t count = 0; - VkResult result = vkFunctions.EnumeratePhysicalDevices(instance, &count, nullptr); + VkResult result = + VkResult::WrapUnsafe(vkFunctions.EnumeratePhysicalDevices(instance, &count, nullptr)); if (result != VK_SUCCESS && result != VK_INCOMPLETE) { return DAWN_DEVICE_LOST_ERROR("vkEnumeratePhysicalDevices"); } std::vector physicalDevices(count); - result = vkFunctions.EnumeratePhysicalDevices(instance, &count, physicalDevices.data()); - if (result != VK_SUCCESS) { - return DAWN_DEVICE_LOST_ERROR("vkEnumeratePhysicalDevices"); - } + DAWN_TRY(CheckVkSuccess( + vkFunctions.EnumeratePhysicalDevices(instance, &count, physicalDevices.data()), + "vkEnumeratePhysicalDevices")); return physicalDevices; } @@ -249,35 +250,31 @@ namespace dawn_native { namespace vulkan { // Gather the info about the device layers { uint32_t count = 0; - VkResult result = - vkFunctions.EnumerateDeviceLayerProperties(physicalDevice, &count, nullptr); + VkResult result = VkResult::WrapUnsafe( + vkFunctions.EnumerateDeviceLayerProperties(physicalDevice, &count, nullptr)); if (result != VK_SUCCESS && result != VK_INCOMPLETE) { return DAWN_DEVICE_LOST_ERROR("vkEnumerateDeviceLayerProperties"); } info.layers.resize(count); - result = vkFunctions.EnumerateDeviceLayerProperties(physicalDevice, &count, - info.layers.data()); - if (result != VK_SUCCESS) { - return DAWN_DEVICE_LOST_ERROR("vkEnumerateDeviceLayerProperties"); - } + DAWN_TRY(CheckVkSuccess(vkFunctions.EnumerateDeviceLayerProperties( + physicalDevice, &count, info.layers.data()), + "vkEnumerateDeviceLayerProperties")); } // Gather the info about the device extensions { uint32_t count = 0; - VkResult result = vkFunctions.EnumerateDeviceExtensionProperties( - physicalDevice, nullptr, &count, nullptr); + VkResult result = VkResult::WrapUnsafe(vkFunctions.EnumerateDeviceExtensionProperties( + physicalDevice, nullptr, &count, nullptr)); if (result != VK_SUCCESS && result != VK_INCOMPLETE) { return DAWN_DEVICE_LOST_ERROR("vkEnumerateDeviceExtensionProperties"); } info.extensions.resize(count); - result = vkFunctions.EnumerateDeviceExtensionProperties(physicalDevice, nullptr, &count, - info.extensions.data()); - if (result != VK_SUCCESS) { - return DAWN_DEVICE_LOST_ERROR("vkEnumerateDeviceExtensionProperties"); - } + DAWN_TRY(CheckVkSuccess(vkFunctions.EnumerateDeviceExtensionProperties( + physicalDevice, nullptr, &count, info.extensions.data()), + "vkEnumerateDeviceExtensionProperties")); for (const auto& extension : info.extensions) { if (IsExtensionName(extension, kExtensionNameExtDebugMarker)) { @@ -328,13 +325,9 @@ namespace dawn_native { namespace vulkan { const VulkanFunctions& vkFunctions = adapter.GetBackend()->GetFunctions(); // Get the surface capabilities - { - VkResult result = vkFunctions.GetPhysicalDeviceSurfaceCapabilitiesKHR( - physicalDevice, surface, &info->capabilities); - if (result != VK_SUCCESS) { - return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); - } - } + DAWN_TRY(CheckVkSuccess(vkFunctions.GetPhysicalDeviceSurfaceCapabilitiesKHR( + physicalDevice, surface, &info->capabilities), + "vkGetPhysicalDeviceSurfaceCapabilitiesKHR")); // Query which queue families support presenting this surface { @@ -343,12 +336,9 @@ namespace dawn_native { namespace vulkan { for (uint32_t i = 0; i < nQueueFamilies; ++i) { VkBool32 supported = VK_FALSE; - VkResult result = vkFunctions.GetPhysicalDeviceSurfaceSupportKHR( - physicalDevice, i, surface, &supported); - - if (result != VK_SUCCESS) { - return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfaceSupportKHR"); - } + DAWN_TRY(CheckVkSuccess(vkFunctions.GetPhysicalDeviceSurfaceSupportKHR( + physicalDevice, i, surface, &supported), + "vkGetPhysicalDeviceSurfaceSupportKHR")); info->supportedQueueFamilies[i] = (supported == VK_TRUE); } @@ -357,35 +347,32 @@ namespace dawn_native { namespace vulkan { // Gather supported formats { uint32_t count = 0; - VkResult result = vkFunctions.GetPhysicalDeviceSurfaceFormatsKHR( - physicalDevice, surface, &count, nullptr); + VkResult result = VkResult::WrapUnsafe(vkFunctions.GetPhysicalDeviceSurfaceFormatsKHR( + physicalDevice, surface, &count, nullptr)); if (result != VK_SUCCESS && result != VK_INCOMPLETE) { return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfaceFormatsKHR"); } info->formats.resize(count); - result = vkFunctions.GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &count, - info->formats.data()); - if (result != VK_SUCCESS) { - return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfaceFormatsKHR"); - } + DAWN_TRY(CheckVkSuccess(vkFunctions.GetPhysicalDeviceSurfaceFormatsKHR( + physicalDevice, surface, &count, info->formats.data()), + "vkGetPhysicalDeviceSurfaceFormatsKHR")); } // Gather supported presents modes { uint32_t count = 0; - VkResult result = vkFunctions.GetPhysicalDeviceSurfacePresentModesKHR( - physicalDevice, surface, &count, nullptr); + VkResult result = + VkResult::WrapUnsafe(vkFunctions.GetPhysicalDeviceSurfacePresentModesKHR( + physicalDevice, surface, &count, nullptr)); if (result != VK_SUCCESS && result != VK_INCOMPLETE) { return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfacePresentModesKHR"); } info->presentModes.resize(count); - result = vkFunctions.GetPhysicalDeviceSurfacePresentModesKHR( - physicalDevice, surface, &count, info->presentModes.data()); - if (result != VK_SUCCESS) { - return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfacePresentModesKHR"); - } + DAWN_TRY(CheckVkSuccess(vkFunctions.GetPhysicalDeviceSurfacePresentModesKHR( + physicalDevice, surface, &count, info->presentModes.data()), + "vkGetPhysicalDeviceSurfacePresentModesKHR")); } return {}; diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp index 2a31b31187..a6bb2fadce 100644 --- a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp +++ b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp @@ -66,8 +66,10 @@ namespace dawn_native { namespace vulkan { namespace external_memory { formatProperties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR; formatProperties.pNext = &externalFormatProperties; - VkResult result = mDevice->fn.GetPhysicalDeviceImageFormatProperties2KHR( - ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &formatInfo, &formatProperties); + VkResult result = + VkResult::WrapUnsafe(mDevice->fn.GetPhysicalDeviceImageFormatProperties2KHR( + ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &formatInfo, + &formatProperties)); // If handle not supported, result == VK_ERROR_FORMAT_NOT_SUPPORTED if (result != VK_SUCCESS) { diff --git a/src/tests/white_box/VulkanErrorInjectorTests.cpp b/src/tests/white_box/VulkanErrorInjectorTests.cpp new file mode 100644 index 0000000000..938bd5dd72 --- /dev/null +++ b/src/tests/white_box/VulkanErrorInjectorTests.cpp @@ -0,0 +1,123 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Math.h" +#include "common/vulkan_platform.h" +#include "dawn_native/ErrorData.h" +#include "dawn_native/VulkanBackend.h" +#include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/VulkanError.h" + +namespace { + + class VulkanErrorInjectorTests : public DawnTest { + public: + void TestSetUp() override { + DAWN_SKIP_TEST_IF(UsesWire()); + + mDeviceVk = reinterpret_cast(device.Get()); + } + + protected: + dawn_native::vulkan::Device* mDeviceVk; + }; + +} // anonymous namespace + +TEST_P(VulkanErrorInjectorTests, InjectErrorOnCreateBuffer) { + VkBufferCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.size = 16; + createInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + + // Check that making a buffer works. + { + VkBuffer buffer = VK_NULL_HANDLE; + EXPECT_EQ( + mDeviceVk->fn.CreateBuffer(mDeviceVk->GetVkDevice(), &createInfo, nullptr, &buffer), + VK_SUCCESS); + mDeviceVk->fn.DestroyBuffer(mDeviceVk->GetVkDevice(), buffer, nullptr); + } + + auto CreateTestBuffer = [&]() -> bool { + VkBuffer buffer = VK_NULL_HANDLE; + dawn_native::MaybeError err = CheckVkSuccess( + mDeviceVk->fn.CreateBuffer(mDeviceVk->GetVkDevice(), &createInfo, nullptr, &buffer), + "vkCreateBuffer"); + if (err.IsError()) { + // The handle should never be written to, even for mock failures. + EXPECT_EQ(buffer, VK_NULL_HANDLE); + delete err.AcquireError(); + return false; + } + EXPECT_NE(buffer, VK_NULL_HANDLE); + + // We never use the buffer, only test mocking errors on creation. Cleanup now. + mDeviceVk->fn.DestroyBuffer(mDeviceVk->GetVkDevice(), buffer, nullptr); + + return true; + }; + + // Check that making a buffer inside CheckVkSuccess works. + { + EXPECT_TRUE(CreateTestBuffer()); + + // The error injector call count should be empty + EXPECT_EQ(dawn_native::AcquireErrorInjectorCallCount(), 0u); + } + + // Test error injection works. + dawn_native::EnableErrorInjector(); + { + EXPECT_TRUE(CreateTestBuffer()); + EXPECT_TRUE(CreateTestBuffer()); + + // The error injector call count should be two. + EXPECT_EQ(dawn_native::AcquireErrorInjectorCallCount(), 2u); + + // Inject an error at index 0. The first should fail, the second succeed. + { + dawn_native::InjectErrorAt(0u); + EXPECT_FALSE(CreateTestBuffer()); + EXPECT_TRUE(CreateTestBuffer()); + + dawn_native::ClearErrorInjector(); + } + + // Inject an error at index 1. The second should fail, the first succeed. + { + dawn_native::InjectErrorAt(1u); + EXPECT_TRUE(CreateTestBuffer()); + EXPECT_FALSE(CreateTestBuffer()); + + dawn_native::ClearErrorInjector(); + } + + // Inject an error and then clear the injector. Calls should be successful. + { + dawn_native::InjectErrorAt(0u); + dawn_native::DisableErrorInjector(); + + EXPECT_TRUE(CreateTestBuffer()); + EXPECT_TRUE(CreateTestBuffer()); + + dawn_native::ClearErrorInjector(); + } + } +} + +DAWN_INSTANTIATE_TEST(VulkanErrorInjectorTests, VulkanBackend);