diff --git a/README.adoc b/README.adoc index 05e8cc7f0..fb7aedc53 100644 --- a/README.adoc +++ b/README.adoc @@ -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 diff --git a/app/android/java/com/khronos/vulkan_samples/SampleLauncherActivity.java b/app/android/java/com/khronos/vulkan_samples/SampleLauncherActivity.java index 499a0dff4..5c0ea52b0 100644 --- a/app/android/java/com/khronos/vulkan_samples/SampleLauncherActivity.java +++ b/app/android/java/com/khronos/vulkan_samples/SampleLauncherActivity.java @@ -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)); diff --git a/app/plugins/window_options/window_options.h b/app/plugins/window_options/window_options.h index 72d3a16eb..574f26e54 100644 --- a/app/plugins/window_options/window_options.h +++ b/app/plugins/window_options/window_options.h @@ -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 * @@ -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"}; diff --git a/framework/core/hpp_instance.cpp b/framework/core/hpp_instance.cpp index d03b27bb8..669de5948 100644 --- a/framework/core/hpp_instance.cpp +++ b/framework/core/hpp_instance.cpp @@ -235,7 +235,7 @@ HPPInstance::HPPInstance(const std::string &applicati // 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 + // 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 @@ -436,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."); @@ -450,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) diff --git a/framework/core/hpp_instance.h b/framework/core/hpp_instance.h index 362885b2b..c220d87f0 100644 --- a/framework/core/hpp_instance.h +++ b/framework/core/hpp_instance.h @@ -93,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 diff --git a/framework/core/instance.cpp b/framework/core/instance.cpp index b5b523f51..42e0886d0 100644 --- a/framework/core/instance.cpp +++ b/framework/core/instance.cpp @@ -493,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."); @@ -507,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) diff --git a/framework/core/instance.h b/framework/core/instance.h index ae45e6fb6..d4a1c9f47 100644 --- a/framework/core/instance.h +++ b/framework/core/instance.h @@ -86,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 diff --git a/framework/platform/headless_window.h b/framework/platform/headless_window.h index 8f9175a9b..e8225218b 100644 --- a/framework/platform/headless_window.h +++ b/framework/platform/headless_window.h @@ -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 * @@ -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 { diff --git a/framework/rendering/hpp_render_context.cpp b/framework/rendering/hpp_render_context.cpp index b4d982528..3ffb307de 100644 --- a/framework/rendering/hpp_render_context.cpp +++ b/framework/rendering/hpp_render_context.cpp @@ -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 * @@ -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; } @@ -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; } @@ -127,7 +128,7 @@ void HPPRenderContext::update_swapchain(const std::set & { 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; } @@ -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; } @@ -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; } diff --git a/framework/rendering/hpp_render_context.h b/framework/rendering/hpp_render_context.h index 8fb312c24..6c83bf045 100644 --- a/framework/rendering/hpp_render_context.h +++ b/framework/rendering/hpp_render_context.h @@ -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 * @@ -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 diff --git a/framework/rendering/render_context.cpp b/framework/rendering/render_context.cpp index 4ce45fc1c..09ff67b69 100644 --- a/framework/rendering/render_context.cpp +++ b/framework/rendering/render_context.cpp @@ -106,7 +106,7 @@ void RenderContext::update_swapchain(const VkExtent2D &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; } @@ -121,7 +121,7 @@ void RenderContext::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; } @@ -138,7 +138,7 @@ void RenderContext::update_swapchain(const std::set &image { 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; } @@ -153,7 +153,7 @@ void RenderContext::update_swapchain(const VkExtent2D &extent, const VkSurfaceTr { 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; } @@ -179,7 +179,7 @@ void RenderContext::update_swapchain(const VkImageCompressionFlagsEXT compressio { if (!swapchain) { - LOGW("Can't update the swapchains compression in headless mode, skipping."); + LOGW("Can't update the swapchains compression. No swapchain, offscreen rendering detected, skipping."); return; } @@ -228,7 +228,7 @@ bool RenderContext::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; } diff --git a/framework/rendering/render_context.h b/framework/rendering/render_context.h index dfe917ce6..69b754858 100644 --- a/framework/rendering/render_context.h +++ b/framework/rendering/render_context.h @@ -51,7 +51,7 @@ class Window; * For normal rendering (using a swapchain), the RenderContext can be created by passing in a * swapchain. A RenderFrame will then be created for each Swapchain image. * - * For headless rendering (no swapchain), the RenderContext can be given a valid Device, and + * For offscreen rendering (no swapchain), the RenderContext can be given a valid Device, and * a width and height. A single RenderFrame will then be created. */ class RenderContext @@ -63,7 +63,7 @@ class RenderContext /** * @brief Constructor * @param device A valid device - * @param surface A surface, VK_NULL_HANDLE if in headless mode + * @param surface A surface, VK_NULL_HANDLE 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 diff --git a/framework/vulkan_sample.h b/framework/vulkan_sample.h index c4e65f6c5..921eaf02c 100644 --- a/framework/vulkan_sample.h +++ b/framework/vulkan_sample.h @@ -1066,7 +1066,7 @@ inline bool VulkanSample::prepare(const ApplicationOptions &options throw std::runtime_error("Failed to create window surface."); } - auto &gpu = instance->get_suitable_gpu(surface); + auto &gpu = instance->get_suitable_gpu(surface, headless); gpu.set_high_priority_graphics_queue_enable(high_priority_graphics_queue); // Request to enable ASTC @@ -1086,7 +1086,7 @@ inline bool VulkanSample::prepare(const ApplicationOptions &options } // Creating vulkan device, specifying the swapchain extension always - if (!headless || get_instance().is_enabled(VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME)) + // If using VK_EXT_headless_surface, we still create and use a swap-chain { add_device_extension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);