fuzzing: Add error injection macros to the Vulkan backend

This will enable fuzzing the Vulkan backend with randomly injected
errors to help ensure the backend properly handles all errors. It also
redefines VkResult in the dawn_native::vulkan namespace such that a
VkResult cannot be used unless it is explicitly wrapped.

Bug: dawn:295
Change-Id: I3ab2f98702a67a61afe06315658a9ab76ed4ccc3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/14520
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
This commit is contained in:
Austin Eng 2019-12-17 00:47:40 +00:00 committed by Commit Bot service account
parent 2c8a17ecc7
commit 6ea362cae0
15 changed files with 437 additions and 104 deletions

View File

@ -208,6 +208,8 @@ source_set("libdawn_native_sources") {
"src/dawn_native/Error.h", "src/dawn_native/Error.h",
"src/dawn_native/ErrorData.cpp", "src/dawn_native/ErrorData.cpp",
"src/dawn_native/ErrorData.h", "src/dawn_native/ErrorData.h",
"src/dawn_native/ErrorInjector.cpp",
"src/dawn_native/ErrorInjector.h",
"src/dawn_native/ErrorScope.cpp", "src/dawn_native/ErrorScope.cpp",
"src/dawn_native/ErrorScope.h", "src/dawn_native/ErrorScope.h",
"src/dawn_native/ErrorScopeTracker.cpp", "src/dawn_native/ErrorScopeTracker.cpp",
@ -958,6 +960,10 @@ source_set("dawn_white_box_tests_sources") {
if (is_linux) { if (is_linux) {
sources += [ "src/tests/white_box/VulkanImageWrappingTests.cpp" ] sources += [ "src/tests/white_box/VulkanImageWrappingTests.cpp" ]
} }
if (dawn_enable_error_injection) {
sources += [ "src/tests/white_box/VulkanErrorInjectorTests.cpp" ]
}
} }
if (dawn_enable_d3d12) { if (dawn_enable_d3d12) {

View File

@ -12,6 +12,12 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import("//build_overrides/build.gni")
if (build_with_chromium) {
import("//build/config/sanitizers/sanitizers.gni")
}
declare_args() { declare_args() {
# Enable Dawn's ASSERTs even in release builds # Enable Dawn's ASSERTs even in release builds
dawn_always_assert = false dawn_always_assert = false
@ -51,6 +57,10 @@ declare_args() {
# Because of how the Vulkan loader works, setting this make Dawn only able # Because of how the Vulkan loader works, setting this make Dawn only able
# to find the Swiftshader ICD and not the others. # to find the Swiftshader ICD and not the others.
dawn_use_swiftshader = false 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(). # GN does not allow reading a variable defined in the same declare_args().

View File

@ -67,6 +67,10 @@ config("dawn_internal") {
defines += [ "DAWN_USE_X11" ] 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 # Only internal Dawn targets can use this config, this means only targets in
# this BUILD.gn file. # this BUILD.gn file.
visibility = [ ":*" ] visibility = [ ":*" ]

View File

@ -78,18 +78,18 @@ class alignas(kNativeVkHandleAlignment) VkNonDispatchableHandle {
VkNonDispatchableHandle& operator=(const VkNonDispatchableHandle<Tag, HandleType>&) = default; VkNonDispatchableHandle& operator=(const VkNonDispatchableHandle<Tag, HandleType>&) = default;
// Comparisons between handles // Comparisons between handles
bool operator==(VkNonDispatchableHandle<Tag, HandleType> other) { bool operator==(VkNonDispatchableHandle<Tag, HandleType> other) const {
return mHandle == other.mHandle; return mHandle == other.mHandle;
} }
bool operator!=(VkNonDispatchableHandle<Tag, HandleType> other) { bool operator!=(VkNonDispatchableHandle<Tag, HandleType> other) const {
return mHandle != other.mHandle; return mHandle != other.mHandle;
} }
// Comparisons between handles and VK_NULL_HANDLE // Comparisons between handles and VK_NULL_HANDLE
bool operator==(std::nullptr_t) { bool operator==(std::nullptr_t) const {
return mHandle == 0; return mHandle == 0;
} }
bool operator!=(std::nullptr_t) { bool operator!=(std::nullptr_t) const {
return mHandle != 0; return mHandle != 0;
} }

View File

@ -61,7 +61,7 @@ namespace dawn_native {
{ \ { \
auto DAWN_LOCAL_VAR = EXPR; \ auto DAWN_LOCAL_VAR = EXPR; \
if (DAWN_UNLIKELY(DAWN_LOCAL_VAR.IsError())) { \ 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__); \ ::dawn_native::AppendBacktrace(error, __FILE__, __func__, __LINE__); \
return {std::move(error)}; \ return {std::move(error)}; \
} \ } \

View File

@ -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

View File

@ -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 <stdint.h>
#include <type_traits>
namespace dawn_native {
template <typename ErrorType>
struct InjectedErrorResult {
ErrorType error;
bool injected;
};
void EnableErrorInjector();
void DisableErrorInjector();
void ClearErrorInjector();
bool ErrorInjectorEnabled();
uint64_t AcquireErrorInjectorCallCount();
bool ShouldInjectError();
template <typename ErrorType>
InjectedErrorResult<ErrorType> MaybeInjectError(ErrorType errorType) {
return InjectedErrorResult<ErrorType>{errorType, ShouldInjectError()};
}
template <typename ErrorType, typename... ErrorTypes>
InjectedErrorResult<ErrorType> MaybeInjectError(ErrorType errorType, ErrorTypes... errorTypes) {
if (ShouldInjectError()) {
return InjectedErrorResult<ErrorType>{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_

View File

@ -459,7 +459,9 @@ namespace dawn_native { namespace vulkan {
VkFence fence = mFencesInFlight.front().first; VkFence fence = mFencesInFlight.front().first;
Serial fenceSerial = mFencesInFlight.front().second; 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); ASSERT(result == VK_SUCCESS || result == VK_NOT_READY);
// Fence are added in order, so we can stop searching as soon // Fence are added in order, so we can stop searching as soon
@ -699,7 +701,7 @@ namespace dawn_native { namespace vulkan {
} }
MaybeError Device::WaitForIdleForDestruction() { 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 // 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 // about, Device lost, which means workloads running on the GPU are no longer accessible
// (so they are as good as waited on) or success. // (so they are as good as waited on) or success.
@ -713,10 +715,15 @@ namespace dawn_native { namespace vulkan {
Serial fenceSerial = mFencesInFlight.front().second; Serial fenceSerial = mFencesInFlight.front().second;
ASSERT(fenceSerial > mCompletedSerial); ASSERT(fenceSerial > mCompletedSerial);
VkResult result = VK_TIMEOUT; VkResult result = VkResult::WrapUnsafe(VK_TIMEOUT);
do { 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); } while (result == VK_TIMEOUT);
// TODO: Handle errors
ASSERT(result == VK_SUCCESS);
fn.DestroyFence(mVkDevice, fence, nullptr); fn.DestroyFence(mVkDevice, fence, nullptr);
mFencesInFlight.pop(); mFencesInFlight.pop();

View File

@ -69,15 +69,12 @@ namespace dawn_native { namespace vulkan {
allocateInfo.memoryTypeIndex = mMemoryTypeIndex; allocateInfo.memoryTypeIndex = mMemoryTypeIndex;
VkDeviceMemory allocatedMemory = VK_NULL_HANDLE; 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 // First check OOM that we want to surface to the application.
// the application. DAWN_TRY(CheckVkOOMThenSuccess(
if (allocationResult == VK_ERROR_OUT_OF_DEVICE_MEMORY) { mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo, nullptr,
return DAWN_OUT_OF_MEMORY_ERROR("OOM while creating the Vkmemory"); &allocatedMemory),
} "vkAllocateMemory"));
DAWN_TRY(CheckVkSuccess(allocationResult, "vkAllocateMemory"));
ASSERT(allocatedMemory != VK_NULL_HANDLE); ASSERT(allocatedMemory != VK_NULL_HANDLE);
return {std::make_unique<ResourceHeap>(allocatedMemory, mMemoryTypeIndex)}; return {std::make_unique<ResourceHeap>(allocatedMemory, mMemoryTypeIndex)};

View File

@ -56,17 +56,32 @@ namespace dawn_native { namespace vulkan {
return "VK_ERROR_FORMAT_NOT_SUPPORTED"; return "VK_ERROR_FORMAT_NOT_SUPPORTED";
case VK_ERROR_FRAGMENTED_POOL: case VK_ERROR_FRAGMENTED_POOL:
return "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: default:
return "<Unknown VkResult>"; return "<Unknown VkResult>";
} }
} }
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)) { if (DAWN_LIKELY(result == VK_SUCCESS)) {
return {}; return {};
} }
std::string message = std::string(context) + " failed with " + VkResultAsString(result); 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); return DAWN_DEVICE_LOST_ERROR(message);
} }

View File

@ -15,19 +15,35 @@
#ifndef DAWNNATIVE_VULKAN_VULKANERROR_H_ #ifndef DAWNNATIVE_VULKAN_VULKANERROR_H_
#define DAWNNATIVE_VULKAN_VULKANERROR_H_ #define DAWNNATIVE_VULKAN_VULKANERROR_H_
#include "common/vulkan_platform.h" #include "dawn_native/ErrorInjector.h"
#include "dawn_native/Error.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<VkResult>(VK_RESULT_MAX_ENUM - 1);
namespace dawn_native { namespace vulkan { namespace dawn_native { namespace vulkan {
// Returns a string version of the result. // Returns a string version of the result.
const char* VkResultAsString(VkResult result); const char* VkResultAsString(VkResult result);
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 // Returns a success only if result if VK_SUCCESS, an error with the context and stringified
// result value instead. Can be used like this: // result value instead. Can be used like this:
// //
// DAWN_TRY(CheckVkSuccess(vkDoSomething, "doing something")); // DAWN_TRY(CheckVkSuccess(vkDoSomething, "doing something"));
MaybeError CheckVkSuccess(VkResult result, const char* context); #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 }} // namespace dawn_native::vulkan

View File

@ -266,6 +266,28 @@ namespace dawn_native { namespace vulkan {
#endif #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 }} // namespace dawn_native::vulkan
#endif // DAWNNATIVE_VULKAN_VULKANFUNCTIONS_H_ #endif // DAWNNATIVE_VULKAN_VULKANFUNCTIONS_H_

View File

@ -16,9 +16,12 @@
#include "dawn_native/vulkan/AdapterVk.h" #include "dawn_native/vulkan/AdapterVk.h"
#include "dawn_native/vulkan/BackendVk.h" #include "dawn_native/vulkan/BackendVk.h"
#include "dawn_native/vulkan/VulkanError.h"
#include <cstring> #include <cstring>
namespace dawn_native { namespace vulkan {
namespace { namespace {
bool IsLayerName(const VkLayerProperties& layer, const char* name) { bool IsLayerName(const VkLayerProperties& layer, const char* name) {
return strncmp(layer.layerName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0; return strncmp(layer.layerName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0;
@ -32,21 +35,19 @@ namespace {
const dawn_native::vulkan::VulkanFunctions& vkFunctions, const dawn_native::vulkan::VulkanFunctions& vkFunctions,
std::vector<VkExtensionProperties>* extensions) { std::vector<VkExtensionProperties>* extensions) {
uint32_t count = 0; uint32_t count = 0;
VkResult result = VkResult result = VkResult::WrapUnsafe(
vkFunctions.EnumerateInstanceExtensionProperties(layerName, &count, nullptr); vkFunctions.EnumerateInstanceExtensionProperties(layerName, &count, nullptr));
if (result != VK_SUCCESS && result != VK_INCOMPLETE) { if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
return false; return false;
} }
extensions->resize(count); extensions->resize(count);
result = result = VkResult::WrapUnsafe(vkFunctions.EnumerateInstanceExtensionProperties(
vkFunctions.EnumerateInstanceExtensionProperties(layerName, &count, extensions->data()); layerName, &count, extensions->data()));
return (result == VK_SUCCESS); return (result == VK_SUCCESS);
} }
} // namespace } // namespace
namespace dawn_native { namespace vulkan {
const char kLayerNameLunargStandardValidation[] = "VK_LAYER_LUNARG_standard_validation"; const char kLayerNameLunargStandardValidation[] = "VK_LAYER_LUNARG_standard_validation";
const char kLayerNameLunargVKTrace[] = "VK_LAYER_LUNARG_vktrace"; const char kLayerNameLunargVKTrace[] = "VK_LAYER_LUNARG_vktrace";
const char kLayerNameRenderDocCapture[] = "VK_LAYER_RENDERDOC_Capture"; const char kLayerNameRenderDocCapture[] = "VK_LAYER_RENDERDOC_Capture";
@ -85,7 +86,8 @@ namespace dawn_native { namespace vulkan {
// Gather the info about the instance layers // Gather the info about the instance layers
{ {
uint32_t count = 0; 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, // From the Vulkan spec result should be success if there are 0 layers,
// incomplete otherwise. This means that both values represent a success. // incomplete otherwise. This means that both values represent a success.
// This is the same for all Enumarte functions // This is the same for all Enumarte functions
@ -94,10 +96,9 @@ namespace dawn_native { namespace vulkan {
} }
info.layers.resize(count); info.layers.resize(count);
result = vkFunctions.EnumerateInstanceLayerProperties(&count, info.layers.data()); DAWN_TRY(CheckVkSuccess(
if (result != VK_SUCCESS) { vkFunctions.EnumerateInstanceLayerProperties(&count, info.layers.data()),
return DAWN_DEVICE_LOST_ERROR("vkEnumerateInstanceLayerProperties"); "vkEnumerateInstanceLayerProperties"));
}
for (const auto& layer : info.layers) { for (const auto& layer : info.layers) {
if (IsLayerName(layer, kLayerNameLunargStandardValidation)) { if (IsLayerName(layer, kLayerNameLunargStandardValidation)) {
@ -202,16 +203,16 @@ namespace dawn_native { namespace vulkan {
const VulkanFunctions& vkFunctions = backend.GetFunctions(); const VulkanFunctions& vkFunctions = backend.GetFunctions();
uint32_t count = 0; 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) { if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
return DAWN_DEVICE_LOST_ERROR("vkEnumeratePhysicalDevices"); return DAWN_DEVICE_LOST_ERROR("vkEnumeratePhysicalDevices");
} }
std::vector<VkPhysicalDevice> physicalDevices(count); std::vector<VkPhysicalDevice> physicalDevices(count);
result = vkFunctions.EnumeratePhysicalDevices(instance, &count, physicalDevices.data()); DAWN_TRY(CheckVkSuccess(
if (result != VK_SUCCESS) { vkFunctions.EnumeratePhysicalDevices(instance, &count, physicalDevices.data()),
return DAWN_DEVICE_LOST_ERROR("vkEnumeratePhysicalDevices"); "vkEnumeratePhysicalDevices"));
}
return physicalDevices; return physicalDevices;
} }
@ -249,35 +250,31 @@ namespace dawn_native { namespace vulkan {
// Gather the info about the device layers // Gather the info about the device layers
{ {
uint32_t count = 0; uint32_t count = 0;
VkResult result = VkResult result = VkResult::WrapUnsafe(
vkFunctions.EnumerateDeviceLayerProperties(physicalDevice, &count, nullptr); vkFunctions.EnumerateDeviceLayerProperties(physicalDevice, &count, nullptr));
if (result != VK_SUCCESS && result != VK_INCOMPLETE) { if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
return DAWN_DEVICE_LOST_ERROR("vkEnumerateDeviceLayerProperties"); return DAWN_DEVICE_LOST_ERROR("vkEnumerateDeviceLayerProperties");
} }
info.layers.resize(count); info.layers.resize(count);
result = vkFunctions.EnumerateDeviceLayerProperties(physicalDevice, &count, DAWN_TRY(CheckVkSuccess(vkFunctions.EnumerateDeviceLayerProperties(
info.layers.data()); physicalDevice, &count, info.layers.data()),
if (result != VK_SUCCESS) { "vkEnumerateDeviceLayerProperties"));
return DAWN_DEVICE_LOST_ERROR("vkEnumerateDeviceLayerProperties");
}
} }
// Gather the info about the device extensions // Gather the info about the device extensions
{ {
uint32_t count = 0; uint32_t count = 0;
VkResult result = vkFunctions.EnumerateDeviceExtensionProperties( VkResult result = VkResult::WrapUnsafe(vkFunctions.EnumerateDeviceExtensionProperties(
physicalDevice, nullptr, &count, nullptr); physicalDevice, nullptr, &count, nullptr));
if (result != VK_SUCCESS && result != VK_INCOMPLETE) { if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
return DAWN_DEVICE_LOST_ERROR("vkEnumerateDeviceExtensionProperties"); return DAWN_DEVICE_LOST_ERROR("vkEnumerateDeviceExtensionProperties");
} }
info.extensions.resize(count); info.extensions.resize(count);
result = vkFunctions.EnumerateDeviceExtensionProperties(physicalDevice, nullptr, &count, DAWN_TRY(CheckVkSuccess(vkFunctions.EnumerateDeviceExtensionProperties(
info.extensions.data()); physicalDevice, nullptr, &count, info.extensions.data()),
if (result != VK_SUCCESS) { "vkEnumerateDeviceExtensionProperties"));
return DAWN_DEVICE_LOST_ERROR("vkEnumerateDeviceExtensionProperties");
}
for (const auto& extension : info.extensions) { for (const auto& extension : info.extensions) {
if (IsExtensionName(extension, kExtensionNameExtDebugMarker)) { if (IsExtensionName(extension, kExtensionNameExtDebugMarker)) {
@ -328,13 +325,9 @@ namespace dawn_native { namespace vulkan {
const VulkanFunctions& vkFunctions = adapter.GetBackend()->GetFunctions(); const VulkanFunctions& vkFunctions = adapter.GetBackend()->GetFunctions();
// Get the surface capabilities // Get the surface capabilities
{ DAWN_TRY(CheckVkSuccess(vkFunctions.GetPhysicalDeviceSurfaceCapabilitiesKHR(
VkResult result = vkFunctions.GetPhysicalDeviceSurfaceCapabilitiesKHR( physicalDevice, surface, &info->capabilities),
physicalDevice, surface, &info->capabilities); "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
if (result != VK_SUCCESS) {
return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
}
}
// Query which queue families support presenting this surface // 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) { for (uint32_t i = 0; i < nQueueFamilies; ++i) {
VkBool32 supported = VK_FALSE; VkBool32 supported = VK_FALSE;
VkResult result = vkFunctions.GetPhysicalDeviceSurfaceSupportKHR( DAWN_TRY(CheckVkSuccess(vkFunctions.GetPhysicalDeviceSurfaceSupportKHR(
physicalDevice, i, surface, &supported); physicalDevice, i, surface, &supported),
"vkGetPhysicalDeviceSurfaceSupportKHR"));
if (result != VK_SUCCESS) {
return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfaceSupportKHR");
}
info->supportedQueueFamilies[i] = (supported == VK_TRUE); info->supportedQueueFamilies[i] = (supported == VK_TRUE);
} }
@ -357,35 +347,32 @@ namespace dawn_native { namespace vulkan {
// Gather supported formats // Gather supported formats
{ {
uint32_t count = 0; uint32_t count = 0;
VkResult result = vkFunctions.GetPhysicalDeviceSurfaceFormatsKHR( VkResult result = VkResult::WrapUnsafe(vkFunctions.GetPhysicalDeviceSurfaceFormatsKHR(
physicalDevice, surface, &count, nullptr); physicalDevice, surface, &count, nullptr));
if (result != VK_SUCCESS && result != VK_INCOMPLETE) { if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfaceFormatsKHR"); return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfaceFormatsKHR");
} }
info->formats.resize(count); info->formats.resize(count);
result = vkFunctions.GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &count, DAWN_TRY(CheckVkSuccess(vkFunctions.GetPhysicalDeviceSurfaceFormatsKHR(
info->formats.data()); physicalDevice, surface, &count, info->formats.data()),
if (result != VK_SUCCESS) { "vkGetPhysicalDeviceSurfaceFormatsKHR"));
return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfaceFormatsKHR");
}
} }
// Gather supported presents modes // Gather supported presents modes
{ {
uint32_t count = 0; uint32_t count = 0;
VkResult result = vkFunctions.GetPhysicalDeviceSurfacePresentModesKHR( VkResult result =
physicalDevice, surface, &count, nullptr); VkResult::WrapUnsafe(vkFunctions.GetPhysicalDeviceSurfacePresentModesKHR(
physicalDevice, surface, &count, nullptr));
if (result != VK_SUCCESS && result != VK_INCOMPLETE) { if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfacePresentModesKHR"); return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfacePresentModesKHR");
} }
info->presentModes.resize(count); info->presentModes.resize(count);
result = vkFunctions.GetPhysicalDeviceSurfacePresentModesKHR( DAWN_TRY(CheckVkSuccess(vkFunctions.GetPhysicalDeviceSurfacePresentModesKHR(
physicalDevice, surface, &count, info->presentModes.data()); physicalDevice, surface, &count, info->presentModes.data()),
if (result != VK_SUCCESS) { "vkGetPhysicalDeviceSurfacePresentModesKHR"));
return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfacePresentModesKHR");
}
} }
return {}; return {};

View File

@ -66,8 +66,10 @@ namespace dawn_native { namespace vulkan { namespace external_memory {
formatProperties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR; formatProperties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR;
formatProperties.pNext = &externalFormatProperties; formatProperties.pNext = &externalFormatProperties;
VkResult result = mDevice->fn.GetPhysicalDeviceImageFormatProperties2KHR( VkResult result =
ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &formatInfo, &formatProperties); VkResult::WrapUnsafe(mDevice->fn.GetPhysicalDeviceImageFormatProperties2KHR(
ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &formatInfo,
&formatProperties));
// If handle not supported, result == VK_ERROR_FORMAT_NOT_SUPPORTED // If handle not supported, result == VK_ERROR_FORMAT_NOT_SUPPORTED
if (result != VK_SUCCESS) { if (result != VK_SUCCESS) {

View File

@ -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<dawn_native::vulkan::Device*>(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);