blob: 55e88efc965c7f3053534cd7718b44a239dae409 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/graphics/tests/vkreadback/vkreadback.h"
#include <condition_variable>
#include <cstdint>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>
#include <gtest/gtest.h>
#include "src/graphics/tests/common/utils.h"
#include <vulkan/vulkan.hpp>
namespace {
constexpr int64_t ms_to_ns(int64_t ms) { return ms * 1000000ll; }
} // namespace
TEST(Vulkan, Readback) {
VkReadbackTest test;
ASSERT_TRUE(test.Initialize(VK_API_VERSION_1_1));
ASSERT_TRUE(test.Exec());
ASSERT_TRUE(test.Readback());
}
TEST(Vulkan, ReadbackMultiple) {
std::vector<std::unique_ptr<VkReadbackTest>> readback_tests;
constexpr int kReadbackCount = 75;
for (int i = 0; i < kReadbackCount; i++) {
auto readback_test = std::make_unique<VkReadbackTest>();
ASSERT_TRUE(readback_test->Initialize(VK_API_VERSION_1_1));
ASSERT_TRUE(readback_test->Exec());
readback_tests.push_back(std::move(readback_test));
}
for (auto& readback_test : readback_tests) {
ASSERT_TRUE(readback_test->Readback());
}
}
TEST(Vulkan, ReadbackLoopWithFenceWaitOnSeparateThread) {
VkReadbackTest readback_test;
ASSERT_TRUE(readback_test.Initialize(VK_API_VERSION_1_1));
std::mutex mutex;
std::condition_variable cond_var;
std::queue<vk::UniqueFence> fences;
constexpr int kFenceCount = 500;
const vk::Device device = readback_test.vulkan_device();
std::thread fence_waiting_thread([&mutex, &cond_var, &fences, &device] {
for (int i = 0; i < kFenceCount; i++) {
vk::UniqueFence fence;
while (!fence) {
std::unique_lock<std::mutex> lock(mutex);
if (fences.empty()) {
cond_var.wait(lock);
continue;
}
fence = std::move(fences.front());
fences.pop();
}
EXPECT_EQ(vk::Result::eSuccess,
device.waitForFences(*fence, /* waitAll= */ true, ms_to_ns(1000)));
}
});
const vk::FenceCreateInfo fence_info{};
for (int i = 0; i < kFenceCount; i++) {
auto [fence_result, fence] = device.createFenceUnique(fence_info);
ASSERT_EQ(fence_result, vk::Result::eSuccess);
EXPECT_TRUE(readback_test.Submit(
{.include_start_transition = (i == 0), .include_end_barrier = (i == kFenceCount - 1)},
fence.get()));
{
std::unique_lock<std::mutex> lock(mutex);
fences.push(std::move(fence));
}
cond_var.notify_one();
EXPECT_TRUE(readback_test.Wait());
}
fence_waiting_thread.join();
EXPECT_TRUE(readback_test.Readback());
}
TEST(Vulkan, ReadbackLoopWithFenceWait) {
VkReadbackTest test;
ASSERT_TRUE(test.Initialize(VK_API_VERSION_1_1));
const vk::Device device = test.vulkan_device();
auto [fence_result, fence] = device.createFenceUnique(vk::FenceCreateInfo{});
ASSERT_EQ(fence_result, vk::Result::eSuccess);
constexpr int kIterationCount = 500;
for (int i = 0; i < kIterationCount; i++) {
EXPECT_TRUE(
test.Submit({.include_start_transition = (i == 0), .include_end_barrier = true}, *fence));
EXPECT_EQ(vk::Result::eSuccess,
device.waitForFences(*fence, /* waitAll= */ true, ms_to_ns(1000)));
device.resetFences(*fence);
EXPECT_TRUE(test.Readback());
}
}
TEST(Vulkan, ReadbackLoopWithTimelineWait) {
VkReadbackTest readback_test;
ASSERT_TRUE(readback_test.Initialize(VK_API_VERSION_1_2));
auto timeline_semaphore_support = readback_test.timeline_semaphore_support();
if (timeline_semaphore_support == VulkanExtensionSupportState::kNotSupported) {
fprintf(stderr, "Timeline semaphore feature not supported. Test skipped.\n");
GTEST_SKIP();
}
const vk::Device device = readback_test.vulkan_device();
vk::UniqueSemaphore semaphore{};
{
// Initialize a timeline semaphore with initial value of 0.
vk::StructureChain<vk::SemaphoreCreateInfo, vk::SemaphoreTypeCreateInfo> create_info;
create_info.get<vk::SemaphoreTypeCreateInfo>().semaphoreType = vk::SemaphoreType::eTimeline;
auto create_semaphore_result = device.createSemaphoreUnique(create_info.get());
ASSERT_EQ(vk::Result::eSuccess, create_semaphore_result.result);
semaphore = std::move(create_semaphore_result.value);
}
constexpr int kSemaphoreUpdateCount = 500;
for (int i = 0; i < kSemaphoreUpdateCount; i++) {
const uint64_t timeline_value = 1 + i;
{
// Every time we submit commands to VkQueue, the value of the timeline
// semaphore will increment by 1.
EXPECT_TRUE(
readback_test.Submit({.include_start_transition = (i == 0), .include_end_barrier = true},
*semaphore, timeline_value));
}
{
// Wait until the timeline semaphore value is updated.
vk::SemaphoreWaitInfo wait_info(vk::SemaphoreWaitFlags{}, *semaphore, timeline_value);
// We'll use Vulkan 1.2 core API only if it is supported; otherwise we
// use Vulkan 1.1 with extension instead. Ditto for below.
vk::Result wait_result = vk::Result::eErrorInitializationFailed;
switch (timeline_semaphore_support) {
case VulkanExtensionSupportState::kSupportedInCore:
wait_result = device.waitSemaphores(wait_info, ms_to_ns(1000));
break;
case VulkanExtensionSupportState::kSupportedAsExtensionOnly:
wait_result =
device.waitSemaphoresKHR(wait_info, ms_to_ns(1000), readback_test.vulkan_loader());
break;
case VulkanExtensionSupportState::kNotSupported:
__builtin_unreachable();
break;
}
EXPECT_EQ(vk::Result::eSuccess, wait_result);
}
{
// Verify that the timeline semaphore counter has been updated.
vk::ResultValue<uint64_t> counter_result(vk::Result::eErrorInitializationFailed, 0u);
switch (timeline_semaphore_support) {
case VulkanExtensionSupportState::kSupportedInCore:
counter_result = device.getSemaphoreCounterValue(*semaphore);
break;
case VulkanExtensionSupportState::kSupportedAsExtensionOnly:
counter_result =
device.getSemaphoreCounterValueKHR(*semaphore, readback_test.vulkan_loader());
break;
case VulkanExtensionSupportState::kNotSupported:
__builtin_unreachable();
break;
}
EXPECT_EQ(counter_result.result, vk::Result::eSuccess);
EXPECT_EQ(counter_result.value, timeline_value);
}
EXPECT_TRUE(readback_test.Readback());
}
}