Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix support for --headless. #1090

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,12 @@ vulkan_samples sample swapchain_images
# Run AFBC sample in benchmark mode for 5000 frames
vulkan_samples sample afbc --benchmark --stop-after-frame 5000

# Run bonza test offscreen
vulkan_samples test bonza --headless
# Run compute nbody using headless_surface and take a screenshot of frame 5
# Note: headless_surface uses VK_EXT_headless_surface.
# This will create a surface and a Swapchain, but present will be a no op.
# The extension is supported by Swiftshader(https://github.com/google/swiftshader).
# It allows to quickly test content in environments without a GPU.
vulkan_samples sample compute_nbody --headless_surface -screenshot 5

# Run all the performance samples for 10 seconds in each configuration
vulkan_samples batch --category performance --duration 10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ public void setArguments(String... args) {
arguments.add("--benchmark");
}
if (isHeadless) {
arguments.add("--headless");
arguments.add("--headless_surface");
}
String[] argArray = new String[arguments.size()];
sendArgumentsToPlatform(arguments.toArray(argArray));
Expand Down
4 changes: 2 additions & 2 deletions app/plugins/window_options/window_options.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (c) 2020-2022, Arm Limited and Contributors
/* Copyright (c) 2020-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
Expand Down Expand Up @@ -47,7 +47,7 @@ class WindowOptions : public WindowOptionsTags
vkb::FlagCommand width_flag = {vkb::FlagType::OneValue, "width", "", "Initial window width"};
vkb::FlagCommand height_flag = {vkb::FlagType::OneValue, "height", "", "Initial window height"};
vkb::FlagCommand fullscreen_flag = {vkb::FlagType::FlagOnly, "fullscreen", "", "Run in fullscreen mode"};
vkb::FlagCommand headless_flag = {vkb::FlagType::FlagOnly, "headless", "", "Run in headless mode"};
vkb::FlagCommand headless_flag = {vkb::FlagType::FlagOnly, "headless_surface", "", "Run in headless surface mode. A Surface and swap-chain is still created using VK_EXT_headless_surface."};
vkb::FlagCommand borderless_flag = {vkb::FlagType::FlagOnly, "borderless", "", "Run in borderless mode"};
vkb::FlagCommand stretch_flag = {vkb::FlagType::FlagOnly, "stretch", "", "Stretch window to fullscreen (direct-to-display only)"};
vkb::FlagCommand vsync_flag = {vkb::FlagType::OneValue, "vsync", "", "Force vsync {ON | OFF}. If not set samples decide how vsync is set"};
Expand Down
12 changes: 5 additions & 7 deletions framework/api_vulkan_sample.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* Copyright (c) 2019-2024, Sascha Willems
* Copyright (c) 2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
Expand Down Expand Up @@ -49,13 +50,10 @@ bool ApiVulkanSample::prepare(const vkb::ApplicationOptions &options)
submit_info = vkb::initializers::submit_info();
submit_info.pWaitDstStageMask = &submit_pipeline_stages;

if (window->get_window_mode() != vkb::Window::Mode::Headless)
{
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &semaphores.acquired_image_ready;
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &semaphores.render_complete;
}
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &semaphores.acquired_image_ready;
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &semaphores.render_complete;

queue = get_device().get_suitable_graphics_queue().get_handle();

Expand Down
27 changes: 11 additions & 16 deletions framework/core/hpp_instance.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
Expand Down Expand Up @@ -187,7 +188,6 @@ HPPInstance::HPPInstance(const std::string &applicati
const std::unordered_map<const char *, bool> &required_extensions,
const std::vector<const char *> &required_validation_layers,
const std::vector<vk::LayerSettingEXT> &required_layer_settings,
bool headless,
uint32_t api_version)
{
std::vector<vk::ExtensionProperties> available_instance_extensions = vk::enumerateInstanceExtensionProperties();
Expand Down Expand Up @@ -232,20 +232,11 @@ HPPInstance::HPPInstance(const std::string &applicati
}
#endif

// Try to enable headless surface extension if it exists
if (headless)
{
const bool has_headless_surface = enable_extension(VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME,
available_instance_extensions, enabled_extensions);
if (!has_headless_surface)
{
LOGW("{} is not available, disabling swapchain creation", VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME);
}
}
else
{
enabled_extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
}
// Specific surface extensions are obtained from Window::get_required_surface_extensions
// They are already added to required_extensions by VulkanSample::prepare

// If using VK_EXT_headless_surface, we still create and use a surface
enabled_extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);

// VK_KHR_get_physical_device_properties2 is a prerequisite of VK_KHR_performance_query
// which will be used for stats gathering where available.
Expand Down Expand Up @@ -445,7 +436,7 @@ vk::Instance HPPInstance::get_handle() const
return handle;
}

vkb::core::HPPPhysicalDevice &HPPInstance::get_suitable_gpu(vk::SurfaceKHR surface)
vkb::core::HPPPhysicalDevice &HPPInstance::get_suitable_gpu(vk::SurfaceKHR surface, bool headless_surface)
{
assert(!gpus.empty() && "No physical devices were found on the system.");

Expand All @@ -459,6 +450,10 @@ vkb::core::HPPPhysicalDevice &HPPInstance::get_suitable_gpu(vk::SurfaceKHR surfa
}
return *gpus[selected_gpu_index.value()];
}
if ( headless_surface )
{
LOGW("Using headless surface with multiple GPUs. Considered explicitly selecting the target GPU.")
}

// Find a discrete GPU
for (auto &gpu : gpus)
Expand Down
7 changes: 4 additions & 3 deletions framework/core/hpp_instance.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
Expand Down Expand Up @@ -54,15 +55,13 @@ class HPPInstance
* @param required_extensions The extensions requested to be enabled
* @param required_validation_layers The validation layers to be enabled
* @param required_layer_settings The layer settings to be enabled
* @param headless Whether the application is requesting a headless setup or not
* @param api_version The Vulkan API version that the instance will be using
* @throws runtime_error if the required extensions and validation layers are not found
*/
HPPInstance(const std::string &application_name,
const std::unordered_map<const char *, bool> &required_extensions = {},
const std::vector<const char *> &required_validation_layers = {},
const std::vector<vk::LayerSettingEXT> &required_layer_settings = {},
bool headless = false,
uint32_t api_version = VK_API_VERSION_1_0);

/**
Expand Down Expand Up @@ -94,9 +93,11 @@ class HPPInstance
/**
* @brief Tries to find the first available discrete GPU that can render to the given surface
* @param surface to test against
* @param headless_surface Is surface created with VK_EXT_headless_surface
* @returns A valid physical device

*/
HPPPhysicalDevice &get_suitable_gpu(vk::SurfaceKHR);
HPPPhysicalDevice &get_suitable_gpu(vk::SurfaceKHR surface, bool headless_surface);

/**
* @brief Checks if the given extension is enabled in the vk::Instance
Expand Down
26 changes: 10 additions & 16 deletions framework/core/instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,6 @@ Instance::Instance(const std::string &application_nam
const std::unordered_map<const char *, bool> &required_extensions,
const std::vector<const char *> &required_validation_layers,
const std::vector<VkLayerSettingEXT> &required_layer_settings,
bool headless,
uint32_t api_version)
{
uint32_t instance_extension_count;
Expand Down Expand Up @@ -240,20 +239,11 @@ Instance::Instance(const std::string &application_nam
}
#endif

// Try to enable headless surface extension if it exists
if (headless)
{
const bool has_headless_surface = enable_extension(VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME,
available_instance_extensions, enabled_extensions);
if (!has_headless_surface)
{
LOGW("{} is not available, disabling swapchain creation", VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME);
}
}
else
{
enabled_extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
}
// Specific surface extensions are obtained from Window::get_required_surface_extensions
// They are already added to required_extensions by VulkanSample::prepare

// Even for a headless surface a swapchain is still required
enabled_extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);

// VK_KHR_get_physical_device_properties2 is a prerequisite of VK_KHR_performance_query
// which will be used for stats gathering where available.
Expand Down Expand Up @@ -503,7 +493,7 @@ PhysicalDevice &Instance::get_first_gpu()
return *gpus[0];
}

PhysicalDevice &Instance::get_suitable_gpu(VkSurfaceKHR surface)
PhysicalDevice &Instance::get_suitable_gpu(VkSurfaceKHR surface, bool headless_surface)
{
assert(!gpus.empty() && "No physical devices were found on the system.");

Expand All @@ -517,6 +507,10 @@ PhysicalDevice &Instance::get_suitable_gpu(VkSurfaceKHR surface)
}
return *gpus[selected_gpu_index.value()];
}
if (headless_surface)
{
LOGW("Using headless surface with multiple GPUs. Considered explicitly selecting the target GPU.")
}

// Find a discrete GPU
for (auto &gpu : gpus)
Expand Down
5 changes: 2 additions & 3 deletions framework/core/instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,13 @@ class Instance
* @param required_extensions The extensions requested to be enabled
* @param required_validation_layers The validation layers to be enabled
* @param required_layer_settings The layer settings to be enabled
* @param headless Whether the application is requesting a headless setup or not
* @param api_version The Vulkan API version that the instance will be using
* @throws runtime_error if the required extensions and validation layers are not found
*/
Instance(const std::string &application_name,
const std::unordered_map<const char *, bool> &required_extensions = {},
const std::vector<const char *> &required_validation_layers = {},
const std::vector<VkLayerSettingEXT> &required_layer_settings = {},
bool headless = false,
uint32_t api_version = VK_API_VERSION_1_0);

/**
Expand Down Expand Up @@ -88,9 +86,10 @@ class Instance
/**
* @brief Tries to find the first available discrete GPU that can render to the given surface
* @param surface to test against
* @param headless_surface Is surface created with VK_EXT_headless_surface
* @returns A valid physical device
*/
PhysicalDevice &get_suitable_gpu(VkSurfaceKHR);
PhysicalDevice &get_suitable_gpu(VkSurfaceKHR surface, bool headless_surface);

/**
* @brief Tries to find the first available discrete GPU
Expand Down
8 changes: 3 additions & 5 deletions framework/hpp_api_vulkan_sample.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
Expand Down Expand Up @@ -46,11 +47,8 @@ bool HPPApiVulkanSample::prepare(const vkb::ApplicationOptions &options)
submit_info = vk::SubmitInfo();
submit_info.pWaitDstStageMask = &submit_pipeline_stages;

if (window->get_window_mode() != vkb::Window::Mode::Headless)
{
submit_info.setWaitSemaphores(semaphores.acquired_image_ready);
submit_info.setSignalSemaphores(semaphores.render_complete);
}
submit_info.setWaitSemaphores(semaphores.acquired_image_ready);
submit_info.setSignalSemaphores(semaphores.render_complete);

queue = get_device().get_suitable_graphics_queue().get_handle();

Expand Down
20 changes: 15 additions & 5 deletions framework/platform/headless_window.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (c) 2018-2023, Arm Limited and Contributors
/* Copyright (c) 2018-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
Expand Down Expand Up @@ -26,12 +26,22 @@ HeadlessWindow::HeadlessWindow(const Window::Properties &properties) :

VkSurfaceKHR HeadlessWindow::create_surface(Instance &instance)
{
return VK_NULL_HANDLE;
return create_surface(instance.get_handle(), VK_NULL_HANDLE);
}

VkSurfaceKHR HeadlessWindow::create_surface(VkInstance, VkPhysicalDevice)
VkSurfaceKHR HeadlessWindow::create_surface(VkInstance instance, VkPhysicalDevice)
{
return VK_NULL_HANDLE;
VkSurfaceKHR surface = VK_NULL_HANDLE;

if (instance)
{
VkHeadlessSurfaceCreateInfoEXT info{};
info.sType = VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT;

VK_CHECK(vkCreateHeadlessSurfaceEXT(instance, &info, VK_NULL_HANDLE, &surface));
}

return surface;
}

bool HeadlessWindow::should_close()
Expand All @@ -52,6 +62,6 @@ float HeadlessWindow::get_dpi_factor() const

std::vector<const char *> HeadlessWindow::get_required_surface_extensions() const
{
return {};
return {VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME};
}
} // namespace vkb
6 changes: 4 additions & 2 deletions framework/platform/headless_window.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (c) 2018-2023, Arm Limited and Contributors
/* Copyright (c) 2018-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
Expand All @@ -22,7 +22,9 @@
namespace vkb
{
/**
* @brief Surface-less implementation of a Window, for use in headless rendering
* @brief Surface-less implementation of a Window using VK_EXT_headless_surface.
* A surface and swapchain are still created but the the present operation resolves to a no op.
* Useful for testing and benchmarking in CI environments.
*/
class HeadlessWindow : public Window
{
Expand Down
11 changes: 6 additions & 5 deletions framework/rendering/hpp_render_context.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
Expand Down Expand Up @@ -95,7 +96,7 @@ void HPPRenderContext::update_swapchain(const vk::Extent2D &extent)
{
if (!swapchain)
{
LOGW("Can't update the swapchains extent in headless mode, skipping.");
LOGW("Can't update the swapchains extent. No swapchain, offscreen rendering detected, skipping.");
return;
}

Expand All @@ -110,7 +111,7 @@ void HPPRenderContext::update_swapchain(const uint32_t image_count)
{
if (!swapchain)
{
LOGW("Can't update the swapchains image count in headless mode, skipping.");
LOGW("Can't update the swapchains image count. No swapchain, offscreen rendering detected, skipping.");
return;
}

Expand All @@ -127,7 +128,7 @@ void HPPRenderContext::update_swapchain(const std::set<vk::ImageUsageFlagBits> &
{
if (!swapchain)
{
LOGW("Can't update the swapchains image usage in headless mode, skipping.");
LOGW("Can't update the swapchains image usage. No swapchain, offscreen rendering detected, skipping.");
return;
}

Expand All @@ -142,7 +143,7 @@ void HPPRenderContext::update_swapchain(const vk::Extent2D &extent, const vk::Su
{
if (!swapchain)
{
LOGW("Can't update the swapchains extent and surface transform in headless mode, skipping.");
LOGW("Can't update the swapchains extent and surface transform. No swapchain, offscreen rendering detected, skipping.");
return;
}

Expand Down Expand Up @@ -199,7 +200,7 @@ bool HPPRenderContext::handle_surface_changes(bool force_update)
{
if (!swapchain)
{
LOGW("Can't handle surface changes in headless mode, skipping.");
LOGW("Can't handle surface changes. No swapchain, offscreen rendering detected, skipping.");
return false;
}

Expand Down
5 changes: 3 additions & 2 deletions framework/rendering/hpp_render_context.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved.
/* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
Expand Down Expand Up @@ -40,7 +41,7 @@ class HPPRenderContext
/**
* @brief Constructor
* @param device A valid device
* @param surface A surface, nullptr if in headless mode
* @param surface A surface, nullptr if in offscreen mode
* @param window The window where the surface was created
* @param present_mode Requests to set the present mode of the swapchain
* @param present_mode_priority_list The order in which the swapchain prioritizes selecting its present mode
Expand Down
Loading
Loading