mirror of
https://github.com/openharmony/third_party_vulkan-loader.git
synced 2026-07-19 17:13:36 -04:00
test: Add revamped allocation tests
Creates a memory tracker class and rewrites the existing allocation tests. Change-Id: I09ca2dc5b4d83f775ae0e3fd362a3fa4f7255d27
This commit is contained in:
committed by
Charles Giessen
parent
266e0783a7
commit
2e9d8dc1a8
@@ -117,7 +117,8 @@ option(TEST_USE_ADDRESS_SANITIZER "Linux only: Advanced memory checking" OFF)
|
||||
include(GoogleTest)
|
||||
add_subdirectory(framework)
|
||||
|
||||
add_executable(test_regression loader_testing_main.cpp loader_regression_tests.cpp loader_version_tests.cpp)
|
||||
add_executable(test_regression loader_testing_main.cpp loader_regression_tests.cpp
|
||||
loader_version_tests.cpp loader_alloc_callback_tests.cpp)
|
||||
target_link_libraries(test_regression PRIVATE testing_dependencies)
|
||||
|
||||
add_executable(test_wsi loader_testing_main.cpp loader_wsi_tests.cpp)
|
||||
|
||||
@@ -29,10 +29,9 @@
|
||||
* The test_environment is what combines the icd, layer, and shim library into a single object that
|
||||
* test fixtures can create and use. Responsible for loading the libraries and establishing the
|
||||
* channels for tests to talk with the icd's and layers.
|
||||
*/
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
// Must include gtest first to guard against Xlib colliding due to redefinitions of "None" and "Bool"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@@ -55,6 +54,12 @@
|
||||
#undef Bool
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Use the NDK's header on Android
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
@@ -124,7 +129,6 @@ struct EnvVarICDOverrideShim : FrameworkEnvironment {
|
||||
GetNewTestICDFunc get_new_test_icd;
|
||||
};
|
||||
|
||||
|
||||
struct SingleICDShim : FrameworkEnvironment {
|
||||
SingleICDShim(TestICDDetails icd_details, DebugMode debug_mode = DebugMode::none);
|
||||
|
||||
|
||||
@@ -622,7 +622,7 @@ DeviceCreateInfo& DeviceCreateInfo::add_device_queue(DeviceQueueCreateInfo queue
|
||||
}
|
||||
|
||||
VkResult CreateInst(InstWrapper& inst, InstanceCreateInfo& inst_info) {
|
||||
return inst.functions->fp_vkCreateInstance(inst_info.get(), nullptr, &inst.inst);
|
||||
return inst.functions->fp_vkCreateInstance(inst_info.get(), inst.callbacks, &inst.inst);
|
||||
}
|
||||
|
||||
VkResult CreatePhysDevs(InstWrapper& inst, uint32_t phys_dev_count, std::vector<VkPhysicalDevice>& physical_devices) {
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
* Instance & Device create info helpers
|
||||
* InstWrapper & DeviceWrapper - for easier test writing
|
||||
* operator == overloads for many vulkan structs - more concise tests
|
||||
*/
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// Following items are needed for C++ to work with PRIxLEAST64
|
||||
@@ -105,7 +105,7 @@ bool remove_env_var(std::string const& name);
|
||||
std::string get_env_var(std::string const& name);
|
||||
#endif
|
||||
|
||||
//Windows specific error handling logic
|
||||
// Windows specific error handling logic
|
||||
#if defined(WIN32)
|
||||
const long ERROR_SETENV_FAILED = 10543; // chosen at random, attempts to not conflict
|
||||
const long ERROR_REMOVEDIRECTORY_FAILED = 10544; // chosen at random, attempts to not conflict
|
||||
@@ -253,7 +253,7 @@ struct path {
|
||||
// get C++ style string
|
||||
std::string const& str() const { return contents; }
|
||||
std::string& str() { return contents; }
|
||||
size_t size() const { return contents.size();};
|
||||
size_t size() const { return contents.size(); };
|
||||
|
||||
private:
|
||||
std::string contents;
|
||||
@@ -347,7 +347,9 @@ struct LibraryWrapper {
|
||||
}
|
||||
LibraryWrapper(LibraryWrapper const& wrapper) = delete;
|
||||
LibraryWrapper& operator=(LibraryWrapper const& wrapper) = delete;
|
||||
LibraryWrapper(LibraryWrapper&& wrapper) noexcept : lib_handle(wrapper.lib_handle), lib_path(wrapper.lib_path) { wrapper.lib_handle = nullptr; }
|
||||
LibraryWrapper(LibraryWrapper&& wrapper) noexcept : lib_handle(wrapper.lib_handle), lib_path(wrapper.lib_path) {
|
||||
wrapper.lib_handle = nullptr;
|
||||
}
|
||||
LibraryWrapper& operator=(LibraryWrapper&& wrapper) noexcept {
|
||||
if (this != &wrapper) {
|
||||
if (lib_handle != nullptr) {
|
||||
@@ -596,10 +598,11 @@ struct DeviceCreateInfo {
|
||||
};
|
||||
|
||||
struct InstWrapper {
|
||||
InstWrapper(VulkanFunctions& functions) noexcept : functions(&functions) {}
|
||||
InstWrapper(VulkanFunctions& functions, VkInstance inst) noexcept : functions(&functions), inst(inst) {}
|
||||
InstWrapper(VulkanFunctions& functions, VkAllocationCallbacks* callbacks = nullptr) noexcept : functions(&functions), callbacks(callbacks) {}
|
||||
InstWrapper(VulkanFunctions& functions, VkInstance inst, VkAllocationCallbacks* callbacks = nullptr) noexcept
|
||||
: functions(&functions), inst(inst), callbacks(callbacks) {}
|
||||
~InstWrapper() {
|
||||
if (inst != VK_NULL_HANDLE) functions->fp_vkDestroyInstance(inst, nullptr);
|
||||
if (inst != VK_NULL_HANDLE) functions->fp_vkDestroyInstance(inst, callbacks);
|
||||
}
|
||||
|
||||
// Immoveable object
|
||||
@@ -614,6 +617,7 @@ struct InstWrapper {
|
||||
|
||||
VulkanFunctions* functions = nullptr;
|
||||
VkInstance inst = VK_NULL_HANDLE;
|
||||
VkAllocationCallbacks* callbacks = nullptr;
|
||||
};
|
||||
|
||||
VkResult CreateInst(InstWrapper& inst, InstanceCreateInfo& inst_info);
|
||||
|
||||
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
* Copyright (c) 2021 The Khronos Group Inc.
|
||||
* Copyright (c) 2021 Valve Corporation
|
||||
* Copyright (c) 2021 LunarG, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and/or associated documentation files (the "Materials"), to
|
||||
* deal in the Materials without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Materials, and to permit persons to whom the Materials are
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice(s) and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Materials.
|
||||
*
|
||||
* THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
*
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
|
||||
* USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*
|
||||
* Author: Charles Giessen <charles@lunarg.com>
|
||||
*/
|
||||
|
||||
#include "test_environment.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
struct MemoryTrackerSettings {
|
||||
MemoryTrackerSettings(bool should_fail_on_allocation, size_t fail_after_allocations)
|
||||
: should_fail_on_allocation(should_fail_on_allocation), fail_after_allocations(fail_after_allocations) {}
|
||||
bool should_fail_on_allocation = false;
|
||||
size_t fail_after_allocations = 0;
|
||||
};
|
||||
|
||||
class MemoryTracker {
|
||||
std::mutex main_mutex;
|
||||
MemoryTrackerSettings settings;
|
||||
VkAllocationCallbacks callbacks{};
|
||||
// Implementation internals
|
||||
struct AllocationDetails {
|
||||
size_t requested_size_bytes;
|
||||
size_t actual_size_bytes;
|
||||
VkSystemAllocationScope alloc_scope;
|
||||
};
|
||||
const static size_t UNKNOWN_ALLOCATION = std::numeric_limits<size_t>::max();
|
||||
size_t allocation_count = 0;
|
||||
std::vector<std::unique_ptr<char[]>> allocations;
|
||||
std::vector<void*> allocations_aligned;
|
||||
std::vector<AllocationDetails> allocation_details;
|
||||
void add_element(std::unique_ptr<char[]>&& alloc, void* aligned_alloc, AllocationDetails detail) {
|
||||
allocations.push_back(std::move(alloc));
|
||||
allocations_aligned.push_back(aligned_alloc);
|
||||
allocation_details.push_back(detail);
|
||||
}
|
||||
void erase_index(size_t index) {
|
||||
allocations.erase(std::next(allocations.begin(), index));
|
||||
allocations_aligned.erase(std::next(allocations_aligned.begin(), index));
|
||||
allocation_details.erase(std::next(allocation_details.begin(), index));
|
||||
}
|
||||
size_t find_element(void* ptr) {
|
||||
auto it = std::find(allocations_aligned.begin(), allocations_aligned.end(), ptr);
|
||||
if (it == allocations_aligned.end()) return UNKNOWN_ALLOCATION;
|
||||
return it - allocations_aligned.begin();
|
||||
}
|
||||
|
||||
void* allocate(size_t size, size_t alignment, VkSystemAllocationScope alloc_scope) {
|
||||
AllocationDetails detail{size, size + (alignment - 1), alloc_scope};
|
||||
auto alloc = std::unique_ptr<char[]>(new char[detail.actual_size_bytes]);
|
||||
if (!alloc) return nullptr;
|
||||
uint64_t addr = (uint64_t)alloc.get();
|
||||
addr += (alignment - 1);
|
||||
addr &= ~(alignment - 1);
|
||||
void* aligned_alloc = (void*)addr;
|
||||
add_element(std::move(alloc), aligned_alloc, detail);
|
||||
allocation_count++;
|
||||
return allocations_aligned.back();
|
||||
}
|
||||
void* reallocate(void* pOriginal, size_t size, size_t alignment, VkSystemAllocationScope alloc_scope) {
|
||||
if (pOriginal == nullptr) {
|
||||
return allocate(size, alignment, alloc_scope);
|
||||
}
|
||||
size_t index = find_element(pOriginal);
|
||||
if (index == UNKNOWN_ALLOCATION) return nullptr;
|
||||
size_t original_size = allocation_details[index].requested_size_bytes;
|
||||
if (size == 0) {
|
||||
erase_index(index);
|
||||
allocation_count--;
|
||||
return nullptr;
|
||||
} else if (size < original_size) {
|
||||
return pOriginal;
|
||||
} else {
|
||||
void* new_alloc = allocate(size, alignment, alloc_scope);
|
||||
if (new_alloc == nullptr) return nullptr;
|
||||
memcpy(new_alloc, pOriginal, original_size);
|
||||
erase_index(index);
|
||||
return new_alloc;
|
||||
}
|
||||
}
|
||||
void free(void* pMemory) {
|
||||
if (pMemory == nullptr) return;
|
||||
size_t index = find_element(pMemory);
|
||||
if (index == UNKNOWN_ALLOCATION) return;
|
||||
erase_index(index);
|
||||
assert(allocation_count != 0 && "Cant free when there are no valid allocations");
|
||||
allocation_count--;
|
||||
}
|
||||
|
||||
// Implementation of public functions
|
||||
void* impl_allocation(size_t size, size_t alignment, VkSystemAllocationScope allocationScope) noexcept {
|
||||
std::lock_guard<std::mutex> lg(main_mutex);
|
||||
if (settings.should_fail_on_allocation && allocation_count == settings.fail_after_allocations) return nullptr;
|
||||
void* addr = allocate(size, alignment, allocationScope);
|
||||
return addr;
|
||||
}
|
||||
void* impl_reallocation(void* pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope) noexcept {
|
||||
std::lock_guard<std::mutex> lg(main_mutex);
|
||||
void* addr = reallocate(pOriginal, size, alignment, allocationScope);
|
||||
return addr;
|
||||
}
|
||||
void impl_free(void* pMemory) noexcept {
|
||||
std::lock_guard<std::mutex> lg(main_mutex);
|
||||
free(pMemory);
|
||||
}
|
||||
void impl_internal_allocation_notification(size_t size, VkInternalAllocationType allocationType,
|
||||
VkSystemAllocationScope allocationScope) noexcept {
|
||||
std::lock_guard<std::mutex> lg(main_mutex);
|
||||
// TODO?
|
||||
}
|
||||
void impl_internal_free(size_t size, VkInternalAllocationType allocationType,
|
||||
VkSystemAllocationScope allocationScope) noexcept {
|
||||
std::lock_guard<std::mutex> lg(main_mutex);
|
||||
// TODO?
|
||||
}
|
||||
|
||||
public:
|
||||
MemoryTracker(MemoryTrackerSettings settings) noexcept : settings(settings) {
|
||||
allocations.reserve(512);
|
||||
allocations_aligned.reserve(512);
|
||||
allocation_details.reserve(512);
|
||||
|
||||
callbacks.pUserData = this;
|
||||
callbacks.pfnAllocation = public_allocation;
|
||||
callbacks.pfnReallocation = public_reallocation;
|
||||
callbacks.pfnFree = public_free;
|
||||
callbacks.pfnInternalAllocation = public_internal_allocation_notification;
|
||||
callbacks.pfnInternalFree = public_internal_free;
|
||||
}
|
||||
MemoryTracker() noexcept : MemoryTracker(MemoryTrackerSettings{false, 0}) {}
|
||||
|
||||
VkAllocationCallbacks* get() noexcept { return &callbacks; }
|
||||
|
||||
bool empty() noexcept { return allocation_count == 0; }
|
||||
|
||||
// Static callbacks
|
||||
static VKAPI_ATTR void* VKAPI_CALL public_allocation(void* pUserData, size_t size, size_t alignment,
|
||||
VkSystemAllocationScope allocationScope) noexcept {
|
||||
return reinterpret_cast<MemoryTracker*>(pUserData)->impl_allocation(size, alignment, allocationScope);
|
||||
}
|
||||
static VKAPI_ATTR void* VKAPI_CALL public_reallocation(void* pUserData, void* pOriginal, size_t size, size_t alignment,
|
||||
VkSystemAllocationScope allocationScope) noexcept {
|
||||
return reinterpret_cast<MemoryTracker*>(pUserData)->impl_reallocation(pOriginal, size, alignment, allocationScope);
|
||||
}
|
||||
static VKAPI_ATTR void VKAPI_CALL public_free(void* pUserData, void* pMemory) noexcept {
|
||||
reinterpret_cast<MemoryTracker*>(pUserData)->impl_free(pMemory);
|
||||
}
|
||||
static VKAPI_ATTR void VKAPI_CALL public_internal_allocation_notification(void* pUserData, size_t size,
|
||||
VkInternalAllocationType allocationType,
|
||||
VkSystemAllocationScope allocationScope) noexcept {
|
||||
reinterpret_cast<MemoryTracker*>(pUserData)->impl_internal_allocation_notification(size, allocationType, allocationScope);
|
||||
}
|
||||
static VKAPI_ATTR void VKAPI_CALL public_internal_free(void* pUserData, size_t size, VkInternalAllocationType allocationType,
|
||||
VkSystemAllocationScope allocationScope) noexcept {
|
||||
reinterpret_cast<MemoryTracker*>(pUserData)->impl_internal_free(size, allocationType, allocationScope);
|
||||
}
|
||||
};
|
||||
|
||||
class Allocation : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
env = std::unique_ptr<SingleICDShim>(new SingleICDShim(TestICDDetails(TEST_ICD_PATH_VERSION_2, VK_MAKE_VERSION(1, 0, 0))));
|
||||
}
|
||||
|
||||
virtual void TearDown() { env.reset(); }
|
||||
std::unique_ptr<SingleICDShim> env;
|
||||
};
|
||||
|
||||
// Test making sure the allocation functions are called to allocate and cleanup everything during
|
||||
// a CreateInstance/DestroyInstance call pair.
|
||||
TEST_F(Allocation, Instance) {
|
||||
MemoryTracker tracker;
|
||||
{
|
||||
InstWrapper inst{env->vulkan_functions, tracker.get()};
|
||||
InstanceCreateInfo inst_create_info;
|
||||
ASSERT_EQ(CreateInst(inst, inst_create_info), VK_SUCCESS);
|
||||
}
|
||||
ASSERT_TRUE(tracker.empty());
|
||||
}
|
||||
|
||||
// Test making sure the allocation functions are called to allocate and cleanup everything during
|
||||
// a CreateInstance/DestroyInstance call pair with a call to GetInstanceProcAddr.
|
||||
TEST_F(Allocation, GetInstanceProcAddr) {
|
||||
MemoryTracker tracker;
|
||||
{
|
||||
InstWrapper inst{env->vulkan_functions, tracker.get()};
|
||||
InstanceCreateInfo inst_create_info;
|
||||
ASSERT_EQ(CreateInst(inst, inst_create_info), VK_SUCCESS);
|
||||
|
||||
auto* pfnCreateDevice = inst->fp_vkGetInstanceProcAddr(inst, "vkCreateDevice");
|
||||
auto* pfnDestroyDevice = inst->fp_vkGetInstanceProcAddr(inst, "vkDestroyDevice");
|
||||
ASSERT_TRUE(pfnCreateDevice != nullptr && pfnDestroyDevice != nullptr);
|
||||
}
|
||||
ASSERT_TRUE(tracker.empty());
|
||||
}
|
||||
|
||||
// Test making sure the allocation functions are called to allocate and cleanup everything during
|
||||
// a vkEnumeratePhysicalDevices call pair.
|
||||
TEST_F(Allocation, EnumeratePhysicalDevices) {
|
||||
MemoryTracker tracker;
|
||||
auto& driver = env->get_test_icd();
|
||||
driver.physical_devices.emplace_back("physical_device_0");
|
||||
{
|
||||
InstWrapper inst{env->vulkan_functions, tracker.get()};
|
||||
InstanceCreateInfo inst_create_info;
|
||||
ASSERT_EQ(CreateInst(inst, inst_create_info), VK_SUCCESS);
|
||||
|
||||
uint32_t physical_count = 1;
|
||||
uint32_t returned_physical_count = 0;
|
||||
ASSERT_EQ(VK_SUCCESS, inst->fp_vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr));
|
||||
ASSERT_EQ(physical_count, returned_physical_count);
|
||||
|
||||
VkPhysicalDevice physical_device;
|
||||
ASSERT_EQ(VK_SUCCESS, inst->fp_vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, &physical_device));
|
||||
ASSERT_EQ(physical_count, returned_physical_count);
|
||||
}
|
||||
ASSERT_TRUE(tracker.empty());
|
||||
}
|
||||
|
||||
// Test making sure the allocation functions are called to allocate and cleanup everything from
|
||||
// vkCreateInstance, to vkCreateDevicce, and then through their destructors. With special
|
||||
// allocators used on both the instance and device.
|
||||
TEST_F(Allocation, InstanceAndDevice) {
|
||||
MemoryTracker tracker;
|
||||
auto& driver = env->get_test_icd();
|
||||
driver.physical_devices.emplace_back("physical_device_0");
|
||||
driver.physical_devices[0].add_queue_family_properties({VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}});
|
||||
{
|
||||
InstWrapper inst{env->vulkan_functions, tracker.get()};
|
||||
InstanceCreateInfo inst_create_info;
|
||||
ASSERT_EQ(CreateInst(inst, inst_create_info), VK_SUCCESS);
|
||||
|
||||
uint32_t physical_count = 1;
|
||||
uint32_t returned_physical_count = 0;
|
||||
ASSERT_EQ(VK_SUCCESS, inst->fp_vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr));
|
||||
ASSERT_EQ(physical_count, returned_physical_count);
|
||||
|
||||
VkPhysicalDevice physical_device;
|
||||
ASSERT_EQ(VK_SUCCESS, inst->fp_vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, &physical_device));
|
||||
ASSERT_EQ(physical_count, returned_physical_count);
|
||||
|
||||
uint32_t family_count = 1;
|
||||
uint32_t returned_family_count = 0;
|
||||
env->vulkan_functions.fp_vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, nullptr);
|
||||
ASSERT_EQ(returned_family_count, family_count);
|
||||
|
||||
VkQueueFamilyProperties family;
|
||||
env->vulkan_functions.fp_vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, &family);
|
||||
ASSERT_EQ(returned_family_count, family_count);
|
||||
ASSERT_EQ(family.queueFlags, VK_QUEUE_GRAPHICS_BIT);
|
||||
ASSERT_EQ(family.queueCount, family_count);
|
||||
ASSERT_EQ(family.timestampValidBits, 0);
|
||||
|
||||
DeviceCreateInfo dev_create_info;
|
||||
DeviceQueueCreateInfo queue_info;
|
||||
queue_info.add_priority(0.0f);
|
||||
dev_create_info.add_device_queue(queue_info);
|
||||
|
||||
VkDevice device;
|
||||
ASSERT_EQ(inst->fp_vkCreateDevice(physical_device, dev_create_info.get(), tracker.get(), &device), VK_SUCCESS);
|
||||
inst->fp_vkDestroyDevice(device, tracker.get());
|
||||
}
|
||||
ASSERT_TRUE(tracker.empty());
|
||||
}
|
||||
// Test making sure the allocation functions are called to allocate and cleanup everything from
|
||||
// vkCreateInstance, to vkCreateDevicce, and then through their destructors. With special
|
||||
// allocators used on only the instance and not the device.
|
||||
TEST_F(Allocation, InstanceButNotDevice) {
|
||||
MemoryTracker tracker;
|
||||
{
|
||||
auto& driver = env->get_test_icd();
|
||||
driver.physical_devices.emplace_back("physical_device_0");
|
||||
driver.physical_devices[0].add_queue_family_properties({VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}});
|
||||
|
||||
InstWrapper inst{env->vulkan_functions, tracker.get()};
|
||||
InstanceCreateInfo inst_create_info;
|
||||
ASSERT_EQ(CreateInst(inst, inst_create_info), VK_SUCCESS);
|
||||
|
||||
uint32_t physical_count = 1;
|
||||
uint32_t returned_physical_count = 0;
|
||||
ASSERT_EQ(VK_SUCCESS, inst->fp_vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr));
|
||||
ASSERT_EQ(physical_count, returned_physical_count);
|
||||
|
||||
VkPhysicalDevice physical_device;
|
||||
ASSERT_EQ(VK_SUCCESS, inst->fp_vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, &physical_device));
|
||||
ASSERT_EQ(physical_count, returned_physical_count);
|
||||
|
||||
uint32_t family_count = 1;
|
||||
uint32_t returned_family_count = 0;
|
||||
env->vulkan_functions.fp_vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, nullptr);
|
||||
ASSERT_EQ(returned_family_count, family_count);
|
||||
|
||||
VkQueueFamilyProperties family;
|
||||
env->vulkan_functions.fp_vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, &family);
|
||||
ASSERT_EQ(returned_family_count, family_count);
|
||||
ASSERT_EQ(family.queueFlags, VK_QUEUE_GRAPHICS_BIT);
|
||||
ASSERT_EQ(family.queueCount, family_count);
|
||||
ASSERT_EQ(family.timestampValidBits, 0);
|
||||
|
||||
DeviceCreateInfo dev_create_info;
|
||||
DeviceQueueCreateInfo queue_info;
|
||||
queue_info.add_priority(0.0f);
|
||||
dev_create_info.add_device_queue(queue_info);
|
||||
|
||||
VkDevice device;
|
||||
ASSERT_EQ(inst->fp_vkCreateDevice(physical_device, dev_create_info.get(), nullptr, &device), VK_SUCCESS);
|
||||
inst->fp_vkDestroyDevice(device, nullptr);
|
||||
}
|
||||
ASSERT_TRUE(tracker.empty());
|
||||
}
|
||||
|
||||
// Test making sure the allocation functions are called to allocate and cleanup everything from
|
||||
// vkCreateInstance, to vkCreateDevicce, and then through their destructors. With special
|
||||
// allocators used on only the device and not the instance.
|
||||
TEST_F(Allocation, DeviceButNotInstance) {
|
||||
MemoryTracker tracker;
|
||||
{
|
||||
auto& driver = env->get_test_icd();
|
||||
driver.physical_devices.emplace_back("physical_device_0");
|
||||
driver.physical_devices[0].add_queue_family_properties({VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}});
|
||||
|
||||
InstWrapper inst{env->vulkan_functions};
|
||||
InstanceCreateInfo inst_create_info;
|
||||
ASSERT_EQ(CreateInst(inst, inst_create_info), VK_SUCCESS);
|
||||
|
||||
uint32_t physical_count = 1;
|
||||
uint32_t returned_physical_count = 0;
|
||||
ASSERT_EQ(VK_SUCCESS, inst->fp_vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr));
|
||||
ASSERT_EQ(physical_count, returned_physical_count);
|
||||
|
||||
VkPhysicalDevice physical_device;
|
||||
ASSERT_EQ(VK_SUCCESS, inst->fp_vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, &physical_device));
|
||||
ASSERT_EQ(physical_count, returned_physical_count);
|
||||
|
||||
uint32_t family_count = 1;
|
||||
uint32_t returned_family_count = 0;
|
||||
env->vulkan_functions.fp_vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, nullptr);
|
||||
ASSERT_EQ(returned_family_count, family_count);
|
||||
|
||||
VkQueueFamilyProperties family;
|
||||
env->vulkan_functions.fp_vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, &family);
|
||||
ASSERT_EQ(returned_family_count, family_count);
|
||||
ASSERT_EQ(family.queueFlags, VK_QUEUE_GRAPHICS_BIT);
|
||||
ASSERT_EQ(family.queueCount, family_count);
|
||||
ASSERT_EQ(family.timestampValidBits, 0);
|
||||
|
||||
DeviceCreateInfo dev_create_info;
|
||||
DeviceQueueCreateInfo queue_info;
|
||||
queue_info.add_priority(0.0f);
|
||||
dev_create_info.add_device_queue(queue_info);
|
||||
|
||||
VkDevice device;
|
||||
ASSERT_EQ(inst->fp_vkCreateDevice(physical_device, dev_create_info.get(), tracker.get(), &device), VK_SUCCESS);
|
||||
inst->fp_vkDestroyDevice(device, tracker.get());
|
||||
}
|
||||
ASSERT_TRUE(tracker.empty());
|
||||
}
|
||||
|
||||
// Test failure during vkCreateInstance to make sure we don't leak memory if
|
||||
// one of the out-of-memory conditions trigger.
|
||||
TEST_F(Allocation, CreateInstanceIntentionalAllocFail) {
|
||||
size_t fail_index = 0;
|
||||
VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY;
|
||||
while (result == VK_ERROR_OUT_OF_HOST_MEMORY && fail_index <= 10000) {
|
||||
MemoryTracker tracker(MemoryTrackerSettings{true, fail_index});
|
||||
|
||||
InstanceCreateInfo inst_create_info;
|
||||
VkInstance instance;
|
||||
result = env->vulkan_functions.fp_vkCreateInstance(inst_create_info.get(), tracker.get(), &instance);
|
||||
if (result == VK_SUCCESS) {
|
||||
env->vulkan_functions.fp_vkDestroyInstance(instance, tracker.get());
|
||||
}
|
||||
ASSERT_TRUE(tracker.empty());
|
||||
fail_index++;
|
||||
}
|
||||
}
|
||||
|
||||
// Test failure during vkCreateDevice to make sure we don't leak memory if
|
||||
// one of the out-of-memory conditions trigger.
|
||||
TEST_F(Allocation, CreateDeviceIntentionalAllocFail) {
|
||||
auto& driver = env->get_test_icd();
|
||||
driver.physical_devices.emplace_back("physical_device_0");
|
||||
driver.physical_devices[0].add_queue_family_properties({VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}});
|
||||
|
||||
InstWrapper inst{env->vulkan_functions};
|
||||
InstanceCreateInfo inst_create_info;
|
||||
ASSERT_EQ(CreateInst(inst, inst_create_info), VK_SUCCESS);
|
||||
|
||||
uint32_t physical_count = 1;
|
||||
uint32_t returned_physical_count = 0;
|
||||
ASSERT_EQ(VK_SUCCESS, inst->fp_vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr));
|
||||
ASSERT_EQ(physical_count, returned_physical_count);
|
||||
|
||||
VkPhysicalDevice physical_device;
|
||||
ASSERT_EQ(VK_SUCCESS, inst->fp_vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, &physical_device));
|
||||
ASSERT_EQ(physical_count, returned_physical_count);
|
||||
|
||||
uint32_t family_count = 1;
|
||||
uint32_t returned_family_count = 0;
|
||||
env->vulkan_functions.fp_vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, nullptr);
|
||||
ASSERT_EQ(returned_family_count, family_count);
|
||||
|
||||
VkQueueFamilyProperties family;
|
||||
env->vulkan_functions.fp_vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, &family);
|
||||
ASSERT_EQ(returned_family_count, family_count);
|
||||
ASSERT_EQ(family.queueFlags, VK_QUEUE_GRAPHICS_BIT);
|
||||
ASSERT_EQ(family.queueCount, family_count);
|
||||
ASSERT_EQ(family.timestampValidBits, 0);
|
||||
|
||||
size_t fail_index = 0;
|
||||
VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY;
|
||||
while (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
|
||||
MemoryTracker tracker(MemoryTrackerSettings{true, fail_index});
|
||||
|
||||
DeviceCreateInfo dev_create_info;
|
||||
DeviceQueueCreateInfo queue_info;
|
||||
queue_info.add_priority(0.0f);
|
||||
dev_create_info.add_device_queue(queue_info);
|
||||
|
||||
VkDevice device;
|
||||
result = inst->fp_vkCreateDevice(physical_device, dev_create_info.get(), tracker.get(), &device);
|
||||
if (result == VK_SUCCESS || fail_index > 10000) {
|
||||
inst->fp_vkDestroyDevice(device, tracker.get());
|
||||
break;
|
||||
}
|
||||
ASSERT_TRUE(tracker.empty());
|
||||
fail_index++;
|
||||
}
|
||||
}
|
||||
|
||||
// Test failure during vkCreateInstance and vkCreateDevice to make sure we don't
|
||||
// leak memory if one of the out-of-memory conditions trigger.
|
||||
TEST_F(Allocation, CreateInstanceDeviceIntentionalAllocFail) {
|
||||
auto& driver = env->get_test_icd();
|
||||
driver.physical_devices.emplace_back("physical_device_0");
|
||||
driver.physical_devices[0].add_queue_family_properties({VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}});
|
||||
|
||||
size_t fail_index = 0;
|
||||
VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY;
|
||||
while (result == VK_ERROR_OUT_OF_HOST_MEMORY && fail_index <= 10000) {
|
||||
MemoryTracker tracker(MemoryTrackerSettings{true, fail_index});
|
||||
fail_index++; // applies to the next loop
|
||||
|
||||
InstanceCreateInfo inst_create_info;
|
||||
VkInstance instance;
|
||||
result = env->vulkan_functions.fp_vkCreateInstance(inst_create_info.get(), tracker.get(), &instance);
|
||||
if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
|
||||
ASSERT_TRUE(tracker.empty());
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t physical_count = 1;
|
||||
uint32_t returned_physical_count = 0;
|
||||
result = env->vulkan_functions.fp_vkEnumeratePhysicalDevices(instance, &returned_physical_count, nullptr);
|
||||
if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
|
||||
env->vulkan_functions.fp_vkDestroyInstance(instance, tracker.get());
|
||||
ASSERT_TRUE(tracker.empty());
|
||||
continue;
|
||||
}
|
||||
ASSERT_EQ(physical_count, returned_physical_count);
|
||||
|
||||
VkPhysicalDevice physical_device;
|
||||
result = env->vulkan_functions.fp_vkEnumeratePhysicalDevices(instance, &returned_physical_count, &physical_device);
|
||||
if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
|
||||
env->vulkan_functions.fp_vkDestroyInstance(instance, tracker.get());
|
||||
ASSERT_TRUE(tracker.empty());
|
||||
continue;
|
||||
}
|
||||
ASSERT_EQ(physical_count, returned_physical_count);
|
||||
|
||||
uint32_t family_count = 1;
|
||||
uint32_t returned_family_count = 0;
|
||||
env->vulkan_functions.fp_vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, nullptr);
|
||||
ASSERT_EQ(returned_family_count, family_count);
|
||||
|
||||
VkQueueFamilyProperties family;
|
||||
env->vulkan_functions.fp_vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, &family);
|
||||
ASSERT_EQ(returned_family_count, family_count);
|
||||
ASSERT_EQ(family.queueFlags, VK_QUEUE_GRAPHICS_BIT);
|
||||
ASSERT_EQ(family.queueCount, family_count);
|
||||
ASSERT_EQ(family.timestampValidBits, 0);
|
||||
|
||||
DeviceCreateInfo dev_create_info;
|
||||
DeviceQueueCreateInfo queue_info;
|
||||
queue_info.add_priority(0.0f);
|
||||
dev_create_info.add_device_queue(queue_info);
|
||||
|
||||
VkDevice device;
|
||||
result = env->vulkan_functions.fp_vkCreateDevice(physical_device, dev_create_info.get(), tracker.get(), &device);
|
||||
if (result == VK_SUCCESS) {
|
||||
env->vulkan_functions.fp_vkDestroyDevice(device, tracker.get());
|
||||
}
|
||||
env->vulkan_functions.fp_vkDestroyInstance(instance, tracker.get());
|
||||
|
||||
ASSERT_TRUE(tracker.empty());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user