diff options
Diffstat (limited to 'cube/cube.cpp')
| -rw-r--r-- | cube/cube.cpp | 248 |
1 files changed, 245 insertions, 3 deletions
diff --git a/cube/cube.cpp b/cube/cube.cpp index 45c3afd0..12cc2dfb 100644 --- a/cube/cube.cpp +++ b/cube/cube.cpp @@ -2,6 +2,7 @@ * Copyright (c) 2015-2019 The Khronos Group Inc. * Copyright (c) 2015-2019 Valve Corporation * Copyright (c) 2015-2019 LunarG, Inc. + * Copyright (c) 2025 The Fuchsia Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +20,7 @@ * Author: Charles Giessen <charles@lunarg.com> */ +#include <algorithm> #include <cassert> #include <cinttypes> #include <cstdio> @@ -41,6 +43,15 @@ #include <linux/input.h> #include "wayland_loader.h" #endif +#if defined(VK_USE_PLATFORM_FUCHSIA) +#include <fidl/fuchsia.ui.app/cpp/fidl.h> +#include <lib/async-loop/cpp/loop.h> +#include <lib/component/incoming/cpp/protocol.h> +#include <lib/component/outgoing/cpp/outgoing_directory.h> +#include <lib/zx/result.h> + +#include "fuchsia/flatland_view.h" +#endif #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 #define VULKAN_HPP_NO_EXCEPTIONS @@ -212,6 +223,8 @@ enum class WsiPlatform { wayland, directfb, display, + fuchsia_display, + fuchsia_scenic, invalid, // Sentinel just to indicate invalid user input }; @@ -244,6 +257,10 @@ WsiPlatform wsi_from_string(std::string const &str) { #if defined(VK_USE_PLATFORM_DISPLAY_KHR) if (str == "display") return WsiPlatform::display; #endif +#if defined(VK_USE_PLATFORM_FUCHSIA) + if (str == "fuchsia_display") return WsiPlatform::fuchsia_display; + if (str == "fuchsia_scenic") return WsiPlatform::fuchsia_scenic; +#endif return WsiPlatform::invalid; }; @@ -287,6 +304,12 @@ const char *wsi_to_string(WsiPlatform wsi_platform) { case (WsiPlatform::display): return "display"; #endif +#if defined(VK_USE_PLATFORM_FUCHSIA) + case (WsiPlatform::fuchsia_display): + return "fuchsia_display"; + case (WsiPlatform::fuchsia_scenic): + return "fuchsia_scenic"; +#endif default: return "unknown"; } @@ -389,6 +412,12 @@ struct Demo { void run(); void create_window(); #endif +#if defined(VK_USE_PLATFORM_FUCHSIA) + // Returns a layer name of static storage duration. + const char *get_fuchsia_image_pipe_layer() const; + void create_flatland_view(); + void run(); +#endif std::string name = "vkcubepp"; // Name to put on the window/icon #if defined(VK_USE_PLATFORM_WIN32_KHR) @@ -438,6 +467,14 @@ struct Demo { screen_window_t screen_window = nullptr; screen_event_t screen_event = nullptr; #endif +#if defined(VK_USE_PLATFORM_FUCHSIA) + fuchsia_ui_views::ViewCreationToken view_creation_token; + async::Loop loop{&kAsyncLoopConfigNeverAttachToThread}; + fidl::ClientEnd<fuchsia_io::Directory> incoming; + std::unique_ptr<component::OutgoingDirectory> outgoing; + std::unique_ptr<FlatlandViewProviderService> view_provider_service; + std::unique_ptr<FlatlandView> flatland_view; +#endif WsiPlatform wsi_platform = WsiPlatform::auto_; vk::SurfaceKHR surface; bool prepared = false; @@ -1116,6 +1153,12 @@ void Demo::init(int argc, char **argv) { if (!wsi_platforms.empty()) wsi_platforms.append("|"); wsi_platforms.append("qnx"); #endif +#if defined(VK_USE_PLATFORM_FUCHSIA) + if (!wsi_platforms.empty()) wsi_platforms.append("|"); + wsi_platforms.append("fuchsia_display"); + if (!wsi_platforms.empty()) wsi_platforms.append("|"); + wsi_platforms.append("fuchsia_scenic"); +#endif std::stringstream usage; usage << "Usage:\n " << APP_SHORT_NAME << "\t[--use_staging] [--validate]\n" << "\t[--break] [--c <framecount>] [--suppress_popups]\n" @@ -1327,6 +1370,11 @@ void Demo::check_and_set_wsi_platform() { } } #endif +#if defined(VK_USE_PLATFORM_FUCHSIA) + if (wsi_platform == WsiPlatform::auto_) { + ERR_EXIT("auto WSI platform is not supported on Fuchsia", "check_and_set_wsi_platform error"); + } +#endif } #if defined(VK_USE_PLATFORM_DISPLAY_KHR) int find_display_gpu(int gpu_number, const std::vector<vk::PhysicalDevice> &physical_devices) { @@ -1479,7 +1527,19 @@ void Demo::init_vk() { vk::Bool32 platformSurfaceExtFound = VK_FALSE; bool portabilityEnumerationActive = false; - auto instance_extensions_return = vk::enumerateInstanceExtensionProperties(); + // Some platforms (for example, Fuchsia) may have their WSI instance + // extension in layers and require the client to manually specify the + // instance extension layer. + vk::Optional<const std::string> instance_extension_layer = nullptr; +#if VK_USE_PLATFORM_FUCHSIA + const char *image_pipe_layer_name = get_fuchsia_image_pipe_layer(); + enabled_layers.push_back(image_pipe_layer_name); + + const std::string image_pipe_layer_name_str = image_pipe_layer_name; + instance_extension_layer = image_pipe_layer_name_str; +#endif // VK_USE_PLATFORM_FUCHSIA + + auto instance_extensions_return = vk::enumerateInstanceExtensionProperties(instance_extension_layer); VERIFY(instance_extensions_return.result == vk::Result::eSuccess); for (const auto &extension : instance_extensions_return.value) { @@ -1553,6 +1613,12 @@ void Demo::init_vk() { enabled_instance_extensions.push_back(VK_QNX_SCREEN_SURFACE_EXTENSION_NAME); } #endif +#if defined(VK_USE_PLATFORM_FUCHSIA) + else if (!strcmp(VK_FUCHSIA_IMAGEPIPE_SURFACE_EXTENSION_NAME, extension.extensionName)) { + platformSurfaceExtFound = 1; + enabled_instance_extensions.push_back(VK_FUCHSIA_IMAGEPIPE_SURFACE_EXTENSION_NAME); + } +#endif } if (!surfaceExtFound) { @@ -1638,6 +1704,16 @@ void Demo::init_vk() { "vkCreateInstance Failure"); } #endif +#if defined(VK_USE_PLATFORM_FUCHSIA) + if (wsi_platform == WsiPlatform::fuchsia_display || wsi_platform == WsiPlatform::fuchsia_scenic) { + ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find the " VK_FUCHSIA_IMAGEPIPE_SURFACE_EXTENSION_NAME + " extension.\n\nDo you have a compatible " + "Vulkan installable client driver (ICD) installed?\nPlease " + "look at the Getting Started guide for additional " + "information.\n", + "vkCreateInstance Failure"); + } +#endif ERR_EXIT( "vkEnumerateInstanceExtensionProperties failed to find any supported WSI surface extension.\n\n" "Do you have a compatible Vulkan installable client driver (ICD) installed?\n" @@ -1780,7 +1856,22 @@ void Demo::select_physical_device() { /* Look for device extensions */ vk::Bool32 swapchainExtFound = VK_FALSE; - auto device_extension_return = gpu.enumerateDeviceExtensionProperties(); + // Some platforms (for example, Fuchsia) may have their device swapchain + // extension in layers and require the client to manually specify the + // device swapchain extension layer. + vk::Optional<const std::string> device_extension_layer = nullptr; +#if VK_USE_PLATFORM_FUCHSIA + const std::string image_pipe_layer_name = get_fuchsia_image_pipe_layer(); + device_extension_layer = image_pipe_layer_name; +#endif // VK_USE_PLATFORM_FUCHSIA + + // Currently we assume that the device swapchain extension layer is always + // enabled when the instance is initialized. + assert((!device_extension_layer) || (std::any_of(enabled_layers.begin(), enabled_layers.end(), [&](const char *layer_name) { + return *device_extension_layer == layer_name; + }))); + + auto device_extension_return = gpu.enumerateDeviceExtensionProperties(device_extension_layer); VERIFY(device_extension_return.result == vk::Result::eSuccess); for (const auto &extension : device_extension_return.value) { @@ -1879,6 +1970,21 @@ void Demo::create_surface() { VERIFY(result == vk::Result::eSuccess); } #endif +#if defined(VK_USE_PLATFORM_FUCHSIA) + if (wsi_platform == WsiPlatform::fuchsia_display) { + auto createInfo = vk::ImagePipeSurfaceCreateInfoFUCHSIA(); + auto result = inst.createImagePipeSurfaceFUCHSIA(&createInfo, nullptr, &surface); + VERIFY(result == vk::Result::eSuccess); + } + if (wsi_platform == WsiPlatform::fuchsia_scenic) { + auto createInfo = vk::ImagePipeSurfaceCreateInfoFUCHSIA(); + // We are using ImagePipeSurface here, but it is being migrated to Flatland and the handle parameter is Flatland's + // ViewCreationToken during this process. + createInfo.setImagePipeHandle(std::move(view_creation_token).value().release()); + auto result = inst.createImagePipeSurfaceFUCHSIA(&createInfo, nullptr, &surface); + VERIFY(result == vk::Result::eSuccess); + } +#endif } void Demo::init_vk_swapchain() { @@ -2394,7 +2500,11 @@ void Demo::prepare_framebuffers() { vk::ShaderModule Demo::prepare_fs() { const uint32_t fragShaderCode[] = { +#ifdef CUBE_FRAG_INC +#include CUBE_FRAG_INC +#else #include "cube.frag.inc" +#endif }; frag_shader_module = prepare_shader_module(fragShaderCode, sizeof(fragShaderCode)); @@ -2725,7 +2835,11 @@ void Demo::prepare_textures() { vk::ShaderModule Demo::prepare_vs() { const uint32_t vertShaderCode[] = { +#ifdef CUBE_VERT_INC +#include CUBE_VERT_INC +#else #include "cube.vert.inc" +#endif }; vert_shader_module = prepare_shader_module(vertShaderCode, sizeof(vertShaderCode)); @@ -2857,7 +2971,11 @@ void Demo::update_data_buffer() { } /* Convert ppm image data from header file into RGBA texture image */ +#ifdef TEXTURE_PPM_H +#include TEXTURE_PPM_H +#else #include "lunarg.ppm.h" +#endif bool Demo::loadTexture(const char *filename, uint8_t *rgba_data, vk::SubresourceLayout &layout, uint32_t &width, uint32_t &height) { (void)filename; char *cPtr; @@ -3637,6 +3755,115 @@ void Demo::create_window() { } } #endif +#if defined(VK_USE_PLATFORM_FUCHSIA) + +const char *Demo::get_fuchsia_image_pipe_layer() const { + switch (wsi_platform) { + case (WsiPlatform::fuchsia_display): + return "VK_LAYER_FUCHSIA_imagepipe_swapchain_fb"; + case (WsiPlatform::fuchsia_scenic): + return "VK_LAYER_FUCHSIA_imagepipe_swapchain"; + case (WsiPlatform::auto_): + ERR_EXIT("auto WSI platform is not supported on Fuchsia", "get_fuchsia_image_pipe_layer failure"); + default: + ERR_EXIT("Invalid WSI platform", "get_fuchsia_image_pipe_layer failure"); + } +} + +void Demo::create_flatland_view() { + if (flatland_view) return; + + zx::result<fidl::ClientEnd<fuchsia_io::Directory> > incoming_result = component::OpenServiceRoot(); + if (incoming_result.is_error()) { + printf("Failed to open incoming directory: %s\n", incoming_result.status_string()); + ERR_EXIT("Failed to open incoming directory", "create_flatland_view failure"); + } + incoming = std::move(incoming_result).value(); + + outgoing = std::make_unique<component::OutgoingDirectory>(loop.dispatcher()); + + FlatlandViewProviderService::CreateView2Callback create_view_callback = [this](fuchsia_ui_app::CreateView2Args args) { + auto resize_callback = [this](uint32_t width, uint32_t height) { + this->width = width; + this->height = height; + if (prepared) { + resize(); + } + }; + + flatland_view = + FlatlandView::Create(incoming.borrow(), std::move(*args.view_creation_token()), resize_callback, loop.dispatcher()); + if (!flatland_view) ERR_EXIT("Failed to created FlatlandView", "create_flatland_view failure"); + + view_creation_token = flatland_view->TakeChildViewCreationToken(); + }; + + view_provider_service = std::make_unique<FlatlandViewProviderService>(std::move(create_view_callback), loop.dispatcher()); + + zx::result<> add_protocol_result = outgoing->AddUnmanagedProtocol<fuchsia_ui_app::ViewProvider>( + [view_provider_service = view_provider_service.get()](fidl::ServerEnd<fuchsia_ui_app::ViewProvider> server_end) { + view_provider_service->HandleViewProviderRequest(std::move(server_end)); + }); + if (add_protocol_result.is_error()) { + printf("Failed to add protocol to outgoing directory: %s\n", add_protocol_result.status_string()); + ERR_EXIT("Failed to add protocol to outgoing directory", "create_flatland_view failure"); + } + + zx::result<> serve_result = outgoing->ServeFromStartupInfo(); + if (serve_result.is_error()) { + printf("Failed to serve outgoing directory: %s\n", serve_result.status_string()); + ERR_EXIT("Failed to serve outgoing directory", "create_flatland_view failure"); + } + + zx_status_t loop_status = ZX_OK; + + // Run message loop until view has been created. + while (!quit && !flatland_view && loop_status == ZX_OK) { + loop_status = loop.RunUntilIdle(); + } +} + +void Demo::run() { + uint32_t num_frames = 60; + uint32_t elapsed_frames = 0; + static const float kMsPerSec = 1000; + + double total_ms = 0; + auto t0 = std::chrono::high_resolution_clock::now(); + + while (!quit) { + if (wsi_platform == WsiPlatform::fuchsia_scenic) { + if (loop.RunUntilIdle() != ZX_OK) { + break; + } + } + auto t1 = std::chrono::high_resolution_clock::now(); + std::chrono::duration<double, std::milli> elapsed = t1 - t0; + total_ms += elapsed.count(); + t0 = t1; + + if (elapsed_frames && (elapsed_frames % num_frames) == 0) { + float fps = static_cast<float>(num_frames / (total_ms / kMsPerSec)); + printf("Framerate average for last %u frames: %f frames per second\n", num_frames, fps); + fflush(stdout); + total_ms = 0; + // attempt to log once per second + num_frames = static_cast<uint32_t>(fps); + elapsed_frames = 0; + } + + draw(); + + curFrame++; + elapsed_frames++; + + if (frameCount != UINT32_MAX && curFrame == frameCount) { + quit = true; + } + } +} + +#endif #if _WIN32 // Include header required for parsing the command line options. @@ -3784,7 +4011,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, return static_cast<int>(msg.wParam); } -#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__QNX__) || defined(__GNU__) +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__QNX__) || defined(__GNU__) || \ + defined(__Fuchsia__) int main(int argc, char **argv) { Demo demo; @@ -3829,6 +4057,14 @@ int main(int argc, char **argv) { // nothing to do here break; #endif +#if defined(VK_USE_PLATFORM_FUCHSIA) + case (WsiPlatform::fuchsia_display): + // nothing to do here + break; + case (WsiPlatform::fuchsia_scenic): + demo.create_flatland_view(); + break; +#endif } demo.create_surface(); @@ -3877,6 +4113,12 @@ int main(int argc, char **argv) { demo.run(); break; #endif +#if defined(VK_USE_PLATFORM_FUCHSIA) + case (WsiPlatform::fuchsia_display): + case (WsiPlatform::fuchsia_scenic): + demo.run(); + break; +#endif } demo.cleanup(); |
