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:
parent
2c8a17ecc7
commit
6ea362cae0
6
BUILD.gn
6
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) {
|
||||
|
|
|
@ -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().
|
||||
|
|
|
@ -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 = [ ":*" ]
|
||||
|
|
|
@ -78,18 +78,18 @@ class alignas(kNativeVkHandleAlignment) VkNonDispatchableHandle {
|
|||
VkNonDispatchableHandle& operator=(const VkNonDispatchableHandle<Tag, HandleType>&) = default;
|
||||
|
||||
// Comparisons between handles
|
||||
bool operator==(VkNonDispatchableHandle<Tag, HandleType> other) {
|
||||
bool operator==(VkNonDispatchableHandle<Tag, HandleType> other) const {
|
||||
return mHandle == other.mHandle;
|
||||
}
|
||||
bool operator!=(VkNonDispatchableHandle<Tag, HandleType> other) {
|
||||
bool operator!=(VkNonDispatchableHandle<Tag, HandleType> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)}; \
|
||||
} \
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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();
|
||||
|
|
|
@ -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<ResourceHeap>(allocatedMemory, mMemoryTypeIndex)};
|
||||
|
|
|
@ -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 "<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)) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<VkResult>(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
|
||||
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -16,37 +16,38 @@
|
|||
|
||||
#include "dawn_native/vulkan/AdapterVk.h"
|
||||
#include "dawn_native/vulkan/BackendVk.h"
|
||||
#include "dawn_native/vulkan/VulkanError.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
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<VkExtensionProperties>* 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<VkExtensionProperties>* 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<VkPhysicalDevice> 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 {};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
Loading…
Reference in New Issue