From c3ea4f300f7e103ba633e42663e7c80983848311 Mon Sep 17 00:00:00 2001 From: Antonio Caggiano Date: Thu, 12 Dec 2024 11:31:54 +0100 Subject: [PATCH] test: External memory FD Actually two apps. One app acts as exporter, exporting memory to an opaque fd containing data about its color render target, while the other app acts as importer. The exporter waits for the importer to connect via UNIX domain socket and then it sends the opaque fd. The importer connects to the exporter and retrieves the fd to use for importing memory. --- test/test_apps/CMakeLists.txt | 1 + .../external-memory-fd/CMakeLists.txt | 28 ++ .../external-memory-fd/export/CMakeLists.txt | 71 +++++ .../external-memory-fd/export/app.cpp | 274 +++++++++++++++++ .../external-memory-fd/import/CMakeLists.txt | 71 +++++ .../external-memory-fd/import/app.cpp | 283 ++++++++++++++++++ 6 files changed, 728 insertions(+) create mode 100644 test/test_apps/external-memory-fd/CMakeLists.txt create mode 100644 test/test_apps/external-memory-fd/export/CMakeLists.txt create mode 100644 test/test_apps/external-memory-fd/export/app.cpp create mode 100644 test/test_apps/external-memory-fd/import/CMakeLists.txt create mode 100644 test/test_apps/external-memory-fd/import/app.cpp diff --git a/test/test_apps/CMakeLists.txt b/test/test_apps/CMakeLists.txt index e5e44fe9a2..e8579db5fb 100644 --- a/test/test_apps/CMakeLists.txt +++ b/test/test_apps/CMakeLists.txt @@ -33,3 +33,4 @@ add_subdirectory(multisample-depth) add_subdirectory(pipeline-binaries) add_subdirectory(host-image-copy) add_subdirectory(shader-objects) +add_subdirectory(external-memory-fd) diff --git a/test/test_apps/external-memory-fd/CMakeLists.txt b/test/test_apps/external-memory-fd/CMakeLists.txt new file mode 100644 index 0000000000..50f73935b7 --- /dev/null +++ b/test/test_apps/external-memory-fd/CMakeLists.txt @@ -0,0 +1,28 @@ +############################################################################### +# Copyright (c) 2024 LunarG, Inc. +# All rights reserved +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# Author: LunarG Team +# Description: CMake script for VK_KHR_external_memory_fd tests +############################################################################### + +add_subdirectory(export) +add_subdirectory(import) diff --git a/test/test_apps/external-memory-fd/export/CMakeLists.txt b/test/test_apps/external-memory-fd/export/CMakeLists.txt new file mode 100644 index 0000000000..cf531f7f91 --- /dev/null +++ b/test/test_apps/external-memory-fd/export/CMakeLists.txt @@ -0,0 +1,71 @@ +############################################################################### +# Copyright (c) 2024 LunarG, Inc. +# All rights reserved +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# Author: LunarG Team +# Description: CMake script for VK_KHR_external_memory_fd export test app +############################################################################### + +add_executable(gfxrecon-external-memory-fd-export "") + +target_sources(gfxrecon-external-memory-fd-export + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/app.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../common/test_app_base.cpp) + +target_include_directories(gfxrecon-external-memory-fd-export PUBLIC + ${CMAKE_BINARY_DIR} + ${CMAKE_CURRENT_LIST_DIR}/../../common) + +target_link_libraries(gfxrecon-external-memory-fd-export + gfxrecon_application + gfxrecon_decode + gfxrecon_graphics + gfxrecon_format + gfxrecon_util + SDL3::SDL3 + platform_specific) + +if (MSVC) + # Force inclusion of "gfxrecon_disable_popup_result" variable in linking. + # On 32-bit windows, MSVC prefixes symbols with "_" but on 64-bit windows it doesn't. + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + target_link_options(gfxrecon-replay PUBLIC "LINKER:/Include:_gfxrecon_disable_popup_result") + else() + target_link_options(gfxrecon-replay PUBLIC "LINKER:/Include:gfxrecon_disable_popup_result") + endif() +endif() + +common_build_directives(gfxrecon-external-memory-fd-export) + +add_custom_command( + TARGET gfxrecon-external-memory-fd-export + POST_BUILD + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}) +if (WIN32) +add_custom_command(TARGET gfxrecon-external-memory-fd-export POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ + COMMAND_EXPAND_LISTS) +endif () + +install(TARGETS gfxrecon-external-memory-fd-export RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_dependencies(gfxrecon-testapps gfxrecon-external-memory-fd-export) \ No newline at end of file diff --git a/test/test_apps/external-memory-fd/export/app.cpp b/test/test_apps/external-memory-fd/export/app.cpp new file mode 100644 index 0000000000..dd3cf259a1 --- /dev/null +++ b/test/test_apps/external-memory-fd/export/app.cpp @@ -0,0 +1,274 @@ +/* +** Copyright (c) 2024 LunarG, Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and associated documentation files (the "Software"), +** to deal in the Software without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Software, and to permit persons to whom the +** Software is furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +#ifdef __linux__ +#define HAVE_MSGHDR_MSG_CONTROL +#endif + +GFXRECON_BEGIN_NAMESPACE(gfxrecon) + +GFXRECON_BEGIN_NAMESPACE(test_app) + +GFXRECON_BEGIN_NAMESPACE(external_memory_fd_export) + +class App : public gfxrecon::test::TestAppBase +{ + public: + App() = default; + virtual ~App() = default; + App(const App&) = delete; + App& operator=(const App&) = delete; + App(App&&) = delete; + App& operator=(App&&) = delete; + + private: + const uint32_t buffer_size_ = sizeof(uint32_t[42]); + VkBuffer buffer_ = VK_NULL_HANDLE; + VkDeviceMemory exportable_memory_ = VK_NULL_HANDLE; + + void configure_instance_builder(test::InstanceBuilder& instance_builder) override; + void configure_physical_device_selector(test::PhysicalDeviceSelector& phys_device_selector) override; + + uint32_t find_memory_type(uint32_t memoryTypeBits, VkMemoryPropertyFlags memory_property_flags); + void create_buffer(); + + int get_exportable_fd(); + void send_exportable_fd(int exportable_fd); + ssize_t send_int(int conn_fd, int data); + + void cleanup() override; + bool frame(const int frame_num) override; + void setup() override; +}; + +void App::configure_instance_builder(test::InstanceBuilder& instance_builder) +{ + instance_builder.enable_extension(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME); + instance_builder.set_headless(true); +} + +void App::configure_physical_device_selector(test::PhysicalDeviceSelector& phys_device_selector) +{ + phys_device_selector.add_required_extension(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME); + phys_device_selector.add_required_extension(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME); +} + +uint32_t App::find_memory_type(uint32_t memoryTypeBits, VkMemoryPropertyFlags memory_property_flags) +{ + VkPhysicalDeviceMemoryProperties memory_properties; + init.inst_disp.getPhysicalDeviceMemoryProperties(init.physical_device, &memory_properties); + + for (uint32_t i = 0; i < memory_properties.memoryTypeCount; ++i) + { + if ((memoryTypeBits & (1 << i)) && (memory_properties.memoryTypes[i].propertyFlags & memory_property_flags) > 0) + { + return i; + break; + } + } + + throw std::runtime_error("Could not find required memory type"); +} + +void App::create_buffer() +{ + VkExternalMemoryBufferCreateInfo external_mem_buf_create_info = {}; + external_mem_buf_create_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO; + external_mem_buf_create_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + + VkBufferCreateInfo buffer_create_info = {}; + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.pNext = &external_mem_buf_create_info; + buffer_create_info.flags = 0u; + buffer_create_info.size = buffer_size_; + buffer_create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + buffer_create_info.queueFamilyIndexCount = 0u; + buffer_create_info.pQueueFamilyIndices = nullptr; + VkResult result = init.disp.createBuffer(&buffer_create_info, nullptr, &buffer_); + VERIFY_VK_RESULT("Failed to create buffer", result); + + VkMemoryRequirements buf_mem_requirements; + init.disp.getBufferMemoryRequirements(buffer_, &buf_mem_requirements); + + VkExportMemoryAllocateInfo export_mem_alloc_info = {}; + export_mem_alloc_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO; + export_mem_alloc_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + + VkMemoryAllocateInfo buf_mem_allocate_info; + buf_mem_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + buf_mem_allocate_info.pNext = &export_mem_alloc_info; + buf_mem_allocate_info.allocationSize = buf_mem_requirements.size; + buf_mem_allocate_info.memoryTypeIndex = + find_memory_type(buf_mem_requirements.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + result = init.disp.allocateMemory(&buf_mem_allocate_info, nullptr, &exportable_memory_); + VERIFY_VK_RESULT("Failed to exportable memory", result); + + result = init.disp.bindBufferMemory(buffer_, exportable_memory_, 0u); + VERIFY_VK_RESULT("Failed to bind memory", result); + + uint32_t* data = nullptr; + result = init.disp.mapMemory(exportable_memory_, 0u, buffer_size_, 0u, reinterpret_cast(&data)); + VERIFY_VK_RESULT("Failed to map buffer memory", result); + for (uint32_t i = 0; i < buffer_size_ / sizeof(uint32_t); ++i) + { + data[i] = i; + } + + init.disp.unmapMemory(exportable_memory_); +} + +int App::get_exportable_fd() +{ + VkMemoryGetFdInfoKHR get_fd_info = {}; + get_fd_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; + get_fd_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + get_fd_info.memory = exportable_memory_; + + int exportable_fd = -1; + VkResult result = init.disp.getMemoryFdKHR(&get_fd_info, &exportable_fd); + VERIFY_VK_RESULT("Failed to get memory fd", result); + return exportable_fd; +} + +void App::send_exportable_fd(int exportable_fd) +{ + // Need to send the fd to the importer process + int external_socket = socket(PF_UNIX, SOCK_STREAM, 0); + GFXRECON_ASSERT(external_socket >= 0) + sockaddr_un un = {}; + un.sun_family = AF_UNIX; + + const char* socket_name = "/tmp/.external-memory"; + snprintf(un.sun_path, sizeof(un.sun_path), "%s", socket_name); + unlink(un.sun_path); + + int bind_result = bind(external_socket, reinterpret_cast(&un), sizeof(un)); + GFXRECON_ASSERT(bind_result >= 0) + + int listen_result = listen(external_socket, 1); + GFXRECON_ASSERT(listen_result >= 0) + + GFXRECON_LOG_INFO("Waiting for importer to connect"); + // Blocking + int conn_fd = accept(external_socket, nullptr, nullptr); + GFXRECON_ASSERT(conn_fd >= 0) + + // Send fd + ssize_t send_result = send_int(conn_fd, exportable_fd); + GFXRECON_ASSERT(send_result >= 0); + + close(conn_fd); + close(external_socket); +} + +ssize_t App::send_int(int conn_fd, int data) +{ + struct msghdr msg = {}; + struct iovec iov[1]; + +#ifdef HAVE_MSGHDR_MSG_CONTROL + union + { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof(int))]; + } control_un = {}; + + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof(control_un.control); + + struct cmsghdr* cmptr = CMSG_FIRSTHDR(&msg); + cmptr->cmsg_len = CMSG_LEN(sizeof(int)); + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = SCM_RIGHTS; + *reinterpret_cast(CMSG_DATA(cmptr)) = data; +#else + msg.msg_accrights = (caddr_t)&data; + msg.msg_accrightslen = sizeof(int); +#endif + + msg.msg_name = NULL; + msg.msg_namelen = 0; + + char c[] = "1"; + iov[0].iov_base = c; + iov[0].iov_len = 1; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + return sendmsg(conn_fd, &msg, 0); +} + +bool App::frame(const int frame_num) +{ + return false; +} + +void App::cleanup() +{ + init.disp.destroyBuffer(buffer_, nullptr); + init.disp.freeMemory(exportable_memory_, nullptr); +} + +void App::setup() +{ + create_buffer(); + int fd = get_exportable_fd(); + GFXRECON_LOG_INFO("Exporting fd (%d)", fd); + send_exportable_fd(fd); + GFXRECON_LOG_INFO("Bye"); +} + +GFXRECON_END_NAMESPACE(external_memory_fd_export) + +GFXRECON_END_NAMESPACE(test_app) + +GFXRECON_END_NAMESPACE(gfxrecon) + +int main(int argc, char* argv[]) +{ + try + { + gfxrecon::test_app::external_memory_fd_export::App app; + app.run("external memory fd export"); + return 0; + } + catch (const std::exception& e) + { + std::cerr << e.what() << std::endl; + return -1; + } +} diff --git a/test/test_apps/external-memory-fd/import/CMakeLists.txt b/test/test_apps/external-memory-fd/import/CMakeLists.txt new file mode 100644 index 0000000000..567aba3d55 --- /dev/null +++ b/test/test_apps/external-memory-fd/import/CMakeLists.txt @@ -0,0 +1,71 @@ +############################################################################### +# Copyright (c) 2024 LunarG, Inc. +# All rights reserved +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# Author: LunarG Team +# Description: CMake script for VK_KHR_external_memory_fd import test app +############################################################################### + +add_executable(gfxrecon-external-memory-fd-import "") + +target_sources(gfxrecon-external-memory-fd-import + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/app.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../common/test_app_base.cpp) + +target_include_directories(gfxrecon-external-memory-fd-import PUBLIC + ${CMAKE_BINARY_DIR} + ${CMAKE_CURRENT_LIST_DIR}/../../common) + +target_link_libraries(gfxrecon-external-memory-fd-import + gfxrecon_application + gfxrecon_decode + gfxrecon_graphics + gfxrecon_format + gfxrecon_util + SDL3::SDL3 + platform_specific) + +if (MSVC) + # Force inclusion of "gfxrecon_disable_popup_result" variable in linking. + # On 32-bit windows, MSVC prefixes symbols with "_" but on 64-bit windows it doesn't. + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + target_link_options(gfxrecon-replay PUBLIC "LINKER:/Include:_gfxrecon_disable_popup_result") + else() + target_link_options(gfxrecon-replay PUBLIC "LINKER:/Include:gfxrecon_disable_popup_result") + endif() +endif() + +common_build_directives(gfxrecon-external-memory-fd-import) + +add_custom_command( + TARGET gfxrecon-external-memory-fd-import + POST_BUILD + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}) +if (WIN32) +add_custom_command(TARGET gfxrecon-external-memory-fd-import POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ + COMMAND_EXPAND_LISTS) +endif () + +install(TARGETS gfxrecon-external-memory-fd-import RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_dependencies(gfxrecon-testapps gfxrecon-external-memory-fd-import) \ No newline at end of file diff --git a/test/test_apps/external-memory-fd/import/app.cpp b/test/test_apps/external-memory-fd/import/app.cpp new file mode 100644 index 0000000000..12b82c5d15 --- /dev/null +++ b/test/test_apps/external-memory-fd/import/app.cpp @@ -0,0 +1,283 @@ +/* +** Copyright (c) 2024 LunarG, Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and associated documentation files (the "Software"), +** to deal in the Software without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Software, and to permit persons to whom the +** Software is furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +#ifdef __linux__ +#define HAVE_MSGHDR_MSG_CONTROL +#endif + +GFXRECON_BEGIN_NAMESPACE(gfxrecon) + +GFXRECON_BEGIN_NAMESPACE(test_app) + +GFXRECON_BEGIN_NAMESPACE(host_image_copy) + +class App : public gfxrecon::test::TestAppBase +{ + public: + App() = default; + virtual ~App() = default; + App(const App&) = delete; + App& operator=(const App&) = delete; + App(App&&) = delete; + App& operator=(App&&) = delete; + + private: + const uint32_t buffer_size_ = sizeof(uint32_t[42]); + VkBuffer buffer_ = VK_NULL_HANDLE; + VkDeviceMemory imported_memory_ = VK_NULL_HANDLE; + + void configure_instance_builder(test::InstanceBuilder& instance_builder) override; + void configure_physical_device_selector(test::PhysicalDeviceSelector& phys_device_selector) override; + + uint32_t find_memory_type(uint32_t memoryTypeBits, VkMemoryPropertyFlags memory_property_flags); + void create_buffer_from_fd(int imported_fd); + + void cleanup() override; + bool frame(const int frame_num) override; + void setup() override; +}; + +void App::configure_instance_builder(test::InstanceBuilder& instance_builder) +{ + instance_builder.enable_extension(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME); + instance_builder.set_headless(true); +} + +void App::configure_physical_device_selector(test::PhysicalDeviceSelector& phys_device_selector) +{ + phys_device_selector.add_required_extension(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME); + phys_device_selector.add_required_extension(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME); +} + +uint32_t App::find_memory_type(uint32_t memoryTypeBits, VkMemoryPropertyFlags memory_property_flags) +{ + VkPhysicalDeviceMemoryProperties memory_properties; + init.inst_disp.getPhysicalDeviceMemoryProperties(init.physical_device, &memory_properties); + + for (uint32_t i = 0; i < memory_properties.memoryTypeCount; ++i) + { + if ((memoryTypeBits & (1 << i)) && (memory_properties.memoryTypes[i].propertyFlags & memory_property_flags) > 0) + { + return i; + break; + } + } + + throw std::runtime_error("Could not find required memory type"); +} + +int receive_int(int socket) +{ + struct msghdr msg = {}; + struct iovec iov[1]; + int newfd = -1; + int imported_fd = -1; + +#ifdef HAVE_MSGHDR_MSG_CONTROL + union + { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof(int))]; + } control_un = {}; + struct cmsghdr* cmptr = nullptr; + + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof(control_un.control); +#else + msg.msg_accrights = (caddr_t)&newfd; + msg.msg_accrightslen = sizeof(int); +#endif + + msg.msg_name = nullptr; + msg.msg_namelen = 0; + + char c = 0; + iov[0].iov_base = reinterpret_cast(&c); + iov[0].iov_len = 1; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + ssize_t n = recvmsg(socket, &msg, 0); + if (n <= 0) + { + GFXRECON_LOG_ERROR("Failed to receive message"); + GFXRECON_ASSERT(false); + } + +#ifdef HAVE_MSGHDR_MSG_CONTROL + if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) + { + if (cmptr->cmsg_level != SOL_SOCKET) + { + GFXRECON_LOG_ERROR("control level != SOL_SOCKET"); + return -1; + } + if (cmptr->cmsg_type != SCM_RIGHTS) + { + GFXRECON_LOG_ERROR("control type != SCM_RIGHTS"); + return -1; + } + imported_fd = *reinterpret_cast(CMSG_DATA(cmptr)); + } + else + { + imported_fd = -1; /* descriptor was not passed */ + } +#else + if (msg.msg_accrightslen == sizeof(int)) + { + imported_fd = newfd; + } + else + { + imported_fd = -1; /* descriptor was not passed */ + } +#endif + + return imported_fd; +} + +int receive_importable_fd() +{ + GFXRECON_LOG_INFO("Connecting to exporter"); + + int external_socket = socket(PF_UNIX, SOCK_STREAM, 0); + GFXRECON_ASSERT(external_socket >= 0); + + sockaddr_un un = {}; + un.sun_family = AF_UNIX; + const char* socket_name = "/tmp/.external-memory"; + snprintf(un.sun_path, sizeof(un.sun_path), "%s", socket_name); + + int connect_result = connect(external_socket, reinterpret_cast(&un), sizeof(un)); + GFXRECON_ASSERT(connect_result >= 0); + + int imported_fd = receive_int(external_socket); + GFXRECON_ASSERT(imported_fd >= 0); + GFXRECON_LOG_INFO("Received fd (%d)", imported_fd); + + close(external_socket); + + return imported_fd; +} + +void App::create_buffer_from_fd(int imported_fd) +{ + VkExternalMemoryBufferCreateInfo external_mem_buf_create_info = {}; + external_mem_buf_create_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO; + external_mem_buf_create_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + + VkBufferCreateInfo buffer_create_info = {}; + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.pNext = &external_mem_buf_create_info; + buffer_create_info.flags = 0u; + buffer_create_info.size = buffer_size_; + buffer_create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + buffer_create_info.queueFamilyIndexCount = 0u; + buffer_create_info.pQueueFamilyIndices = nullptr; + VkResult result = init.disp.createBuffer(&buffer_create_info, nullptr, &buffer_); + VERIFY_VK_RESULT("Failed to create buffer", result); + + VkMemoryRequirements buf_mem_requirements; + init.disp.getBufferMemoryRequirements(buffer_, &buf_mem_requirements); + + VkImportMemoryFdInfoKHR import_mem_fd_info = {}; + import_mem_fd_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR; + import_mem_fd_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + import_mem_fd_info.fd = imported_fd; + + VkMemoryAllocateInfo mem_alloc_info = {}; + mem_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mem_alloc_info.allocationSize = buffer_size_; + mem_alloc_info.memoryTypeIndex = + find_memory_type(buf_mem_requirements.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + mem_alloc_info.pNext = &import_mem_fd_info; + + result = init.disp.allocateMemory(&mem_alloc_info, nullptr, &imported_memory_); + VERIFY_VK_RESULT("Failed to import memory", result); + + result = init.disp.bindBufferMemory(buffer_, imported_memory_, 0u); + VERIFY_VK_RESULT("Failed to bind memory", result); + + uint32_t* data = nullptr; + result = init.disp.mapMemory(imported_memory_, 0u, buffer_size_, 0u, reinterpret_cast(&data)); + VERIFY_VK_RESULT("Failed to map buffer memory", result); + for (uint32_t i = 0; i < buffer_size_ / sizeof(uint32_t); ++i) + { + GFXRECON_ASSERT(data[i] == i); + } + GFXRECON_LOG_INFO("Memory imported correctly"); + + init.disp.unmapMemory(imported_memory_); +} + +bool App::frame(const int frame_num) +{ + return false; +} + +void App::cleanup() +{ + init.disp.destroyBuffer(buffer_, nullptr); + init.disp.freeMemory(imported_memory_, nullptr); +} + +void App::setup() +{ + create_buffer_from_fd(receive_importable_fd()); +} + +GFXRECON_END_NAMESPACE(host_image_copy) + +GFXRECON_END_NAMESPACE(test_app) + +GFXRECON_END_NAMESPACE(gfxrecon) + +int main(int argc, char* argv[]) +{ + try + { + gfxrecon::test_app::host_image_copy::App app{}; + app.run("external memory fd import"); + return 0; + } + catch (std::exception e) + { + std::cout << e.what() << std::endl; + return -1; + } +}