mirror of
https://github.com/InstanceBrick/nightcap.git
synced 2026-10-23 11:01:04 +02:00
180 lines
6.9 KiB
C++
180 lines
6.9 KiB
C++
#include "mocktail/graphics/vulkan_text_overlay_compositor.h"
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <libplacebo/vulkan.h>
|
|
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
|
|
namespace mocktail {
|
|
namespace graphics {
|
|
namespace {
|
|
|
|
std::atomic<int> g_submit_calls{0};
|
|
std::atomic<int> g_submit2_calls{0};
|
|
std::atomic<int> g_bind_sparse_calls{0};
|
|
std::atomic<int> g_wait_idle_calls{0};
|
|
std::atomic<VkQueue> g_last_queue{VK_NULL_HANDLE};
|
|
|
|
VkQueue FakeQueue() {
|
|
return reinterpret_cast<VkQueue>(static_cast<uintptr_t>(0x51b));
|
|
}
|
|
|
|
VkResult RecordingQueueSubmit(VkQueue queue, uint32_t, const VkSubmitInfo*,
|
|
VkFence) {
|
|
g_submit_calls.fetch_add(1, std::memory_order_relaxed);
|
|
g_last_queue.store(queue, std::memory_order_relaxed);
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult RecordingQueueSubmit2(VkQueue queue, uint32_t, const VkSubmitInfo2*,
|
|
VkFence) {
|
|
g_submit2_calls.fetch_add(1, std::memory_order_relaxed);
|
|
g_last_queue.store(queue, std::memory_order_relaxed);
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult RecordingQueueBindSparse(VkQueue queue, uint32_t,
|
|
const VkBindSparseInfo*, VkFence) {
|
|
g_bind_sparse_calls.fetch_add(1, std::memory_order_relaxed);
|
|
g_last_queue.store(queue, std::memory_order_relaxed);
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult RecordingQueueWaitIdle(VkQueue queue) {
|
|
g_wait_idle_calls.fetch_add(1, std::memory_order_relaxed);
|
|
g_last_queue.store(queue, std::memory_order_relaxed);
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
class VulkanTextOverlayCompositorTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
g_submit_calls.store(0, std::memory_order_relaxed);
|
|
g_submit2_calls.store(0, std::memory_order_relaxed);
|
|
g_bind_sparse_calls.store(0, std::memory_order_relaxed);
|
|
g_wait_idle_calls.store(0, std::memory_order_relaxed);
|
|
g_last_queue.store(VK_NULL_HANDLE, std::memory_order_relaxed);
|
|
}
|
|
};
|
|
|
|
class VulkanTextOverlayCompositorDeviceTest
|
|
: public VulkanTextOverlayCompositorTest {
|
|
protected:
|
|
void SetUp() override {
|
|
VulkanTextOverlayCompositorTest::SetUp();
|
|
pl_vulkan_params params = pl_vulkan_default_params;
|
|
params.allow_software = true;
|
|
params.async_compute = false;
|
|
params.async_transfer = false;
|
|
host_ = pl_vulkan_create(nullptr, ¶ms);
|
|
if (host_ == nullptr) {
|
|
GTEST_SKIP() << "A compatible Vulkan device is unavailable";
|
|
}
|
|
get_device_proc_addr_ = reinterpret_cast<PFN_vkGetDeviceProcAddr>(
|
|
host_->get_proc_addr(host_->instance, "vkGetDeviceProcAddr"));
|
|
ASSERT_NE(get_device_proc_addr_, nullptr);
|
|
}
|
|
|
|
void TearDown() override { pl_vulkan_destroy(&host_); }
|
|
|
|
bool RegisterHostDevice(VulkanTextOverlayCompositor* compositor) {
|
|
return compositor->RegisterDevice(
|
|
host_->instance, host_->phys_device, host_->device, host_->api_version,
|
|
host_->extensions, host_->num_extensions, host_->queue_graphics.index,
|
|
host_->queue_graphics.count, host_->features, host_->get_proc_addr,
|
|
get_device_proc_addr_);
|
|
}
|
|
|
|
pl_vulkan host_ = nullptr;
|
|
PFN_vkGetDeviceProcAddr get_device_proc_addr_ = nullptr;
|
|
};
|
|
|
|
TEST_F(VulkanTextOverlayCompositorDeviceTest,
|
|
RegisteredDeviceTeardownRemovesStateAndPreservesHost) {
|
|
VulkanTextOverlayCompositor compositor;
|
|
ASSERT_TRUE(RegisterHostDevice(&compositor));
|
|
const auto get_device_queue = reinterpret_cast<PFN_vkGetDeviceQueue>(
|
|
get_device_proc_addr_(host_->device, "vkGetDeviceQueue"));
|
|
ASSERT_NE(get_device_queue, nullptr);
|
|
VkQueue queue = VK_NULL_HANDLE;
|
|
get_device_queue(host_->device, host_->queue_graphics.index, 0, &queue);
|
|
ASSERT_NE(queue, VK_NULL_HANDLE);
|
|
ASSERT_TRUE(compositor.RegisterQueue(host_->device, queue,
|
|
host_->queue_graphics.index, 0));
|
|
|
|
compositor.DestroyDevice(VK_NULL_HANDLE);
|
|
compositor.DestroyDevice(
|
|
reinterpret_cast<VkDevice>(static_cast<uintptr_t>(0x51d)));
|
|
ASSERT_TRUE(compositor.RegisterQueue(host_->device, queue,
|
|
host_->queue_graphics.index, 0));
|
|
|
|
// Run with ThreadSanitizer to detect an unlock after DeviceState is freed.
|
|
compositor.DestroyDevice(host_->device);
|
|
EXPECT_FALSE(compositor.RegisterQueue(host_->device, queue,
|
|
host_->queue_graphics.index, 0));
|
|
compositor.DestroyDevice(host_->device);
|
|
EXPECT_EQ(compositor.QueueSubmit(queue, 0, nullptr, VK_NULL_HANDLE,
|
|
RecordingQueueSubmit),
|
|
VK_SUCCESS);
|
|
EXPECT_EQ(g_submit_calls.load(std::memory_order_relaxed), 1);
|
|
EXPECT_EQ(g_last_queue.load(std::memory_order_relaxed), queue);
|
|
|
|
const auto device_wait_idle = reinterpret_cast<PFN_vkDeviceWaitIdle>(
|
|
get_device_proc_addr_(host_->device, "vkDeviceWaitIdle"));
|
|
ASSERT_NE(device_wait_idle, nullptr);
|
|
EXPECT_EQ(device_wait_idle(host_->device), VK_SUCCESS);
|
|
ASSERT_TRUE(RegisterHostDevice(&compositor));
|
|
compositor.DestroyDevice(host_->device);
|
|
}
|
|
|
|
TEST_F(VulkanTextOverlayCompositorTest,
|
|
InactiveOverlayQueueSubmitInvokesFallback) {
|
|
VulkanTextOverlayCompositor compositor;
|
|
const VkQueue queue = FakeQueue();
|
|
EXPECT_EQ(compositor.QueueSubmit(queue, 0, nullptr, VK_NULL_HANDLE,
|
|
RecordingQueueSubmit),
|
|
VK_SUCCESS);
|
|
EXPECT_EQ(g_submit_calls.load(std::memory_order_relaxed), 1);
|
|
EXPECT_EQ(g_last_queue.load(std::memory_order_relaxed), queue);
|
|
}
|
|
|
|
TEST_F(VulkanTextOverlayCompositorTest,
|
|
InactiveOverlayQueueSubmit2WaitIdleAndBindSparseInvokeFallback) {
|
|
VulkanTextOverlayCompositor compositor;
|
|
const VkQueue queue = FakeQueue();
|
|
EXPECT_EQ(compositor.QueueSubmit2(queue, 0, nullptr, VK_NULL_HANDLE,
|
|
RecordingQueueSubmit2),
|
|
VK_SUCCESS);
|
|
EXPECT_EQ(compositor.QueueBindSparse(queue, 0, nullptr, VK_NULL_HANDLE,
|
|
RecordingQueueBindSparse),
|
|
VK_SUCCESS);
|
|
EXPECT_EQ(compositor.QueueWaitIdle(queue, RecordingQueueWaitIdle),
|
|
VK_SUCCESS);
|
|
EXPECT_EQ(g_submit2_calls.load(std::memory_order_relaxed), 1);
|
|
EXPECT_EQ(g_bind_sparse_calls.load(std::memory_order_relaxed), 1);
|
|
EXPECT_EQ(g_wait_idle_calls.load(std::memory_order_relaxed), 1);
|
|
EXPECT_EQ(g_last_queue.load(std::memory_order_relaxed), queue);
|
|
}
|
|
|
|
TEST_F(VulkanTextOverlayCompositorTest, NullFallbackFailsClosed) {
|
|
VulkanTextOverlayCompositor compositor;
|
|
const VkQueue queue = FakeQueue();
|
|
EXPECT_EQ(
|
|
compositor.QueueSubmit(queue, 0, nullptr, VK_NULL_HANDLE, nullptr),
|
|
VK_ERROR_INITIALIZATION_FAILED);
|
|
EXPECT_EQ(
|
|
compositor.QueueSubmit2(queue, 0, nullptr, VK_NULL_HANDLE, nullptr),
|
|
VK_ERROR_INITIALIZATION_FAILED);
|
|
EXPECT_EQ(
|
|
compositor.QueueBindSparse(queue, 0, nullptr, VK_NULL_HANDLE, nullptr),
|
|
VK_ERROR_INITIALIZATION_FAILED);
|
|
EXPECT_EQ(compositor.QueueWaitIdle(queue, nullptr),
|
|
VK_ERROR_INITIALIZATION_FAILED);
|
|
EXPECT_EQ(g_submit_calls.load(std::memory_order_relaxed), 0);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace graphics
|
|
} // namespace mocktail
|