From 1349a47fcbbff109b7e360f3c96c723b7052d3ef Mon Sep 17 00:00:00 2001 From: Aaron Ruby Date: Fri, 6 Mar 2026 11:13:00 -0500 Subject: [PATCH 1/3] Emulate exportable VkDeviceMemory with QnxScreenBuffer memory with server-side allocation + import during ColorBuffer creation. ... And add platform_helper_qnx implementation; implements some helpers in the gfxstream::qnx namespace, etc.. This change allows QNX to defer the allocation to the gfxtream backend, and makes the externalMemory management more uniform across platforms, in terms of the way it is managed by the ColorBufferVk object lifetime. --- host/meson.build | 1 + host/native_sub_window_qnx.cpp | 26 +--- host/platform_helper_qnx.cpp | 110 ++++++++++++++ host/platform_helper_qnx.h | 47 ++++++ host/vulkan/vk_common_operations.cpp | 189 ++++++++++++++++++++---- host/vulkan/vk_common_operations.h | 36 +++-- host/vulkan/vk_decoder_global_state.cpp | 31 ++++ 7 files changed, 376 insertions(+), 64 deletions(-) create mode 100644 host/platform_helper_qnx.cpp create mode 100644 host/platform_helper_qnx.h diff --git a/host/meson.build b/host/meson.build index 28fa1b799..54b7c49fc 100644 --- a/host/meson.build +++ b/host/meson.build @@ -134,6 +134,7 @@ elif host_machine.system() == 'linux' and use_gles files_lib_gfxstream_backend += files('native_sub_window_x11.cpp') elif host_machine.system() == 'qnx' files_lib_gfxstream_backend += files( + 'platform_helper_qnx.cpp', 'native_sub_window_qnx.cpp', ) endif diff --git a/host/native_sub_window_qnx.cpp b/host/native_sub_window_qnx.cpp index aeddee851..cf459e0d3 100644 --- a/host/native_sub_window_qnx.cpp +++ b/host/native_sub_window_qnx.cpp @@ -14,27 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include -#include -#include "native_sub_window.h" - -namespace { - -static pthread_once_t once_control = PTHREAD_ONCE_INIT; -static screen_context_t g_screen_ctx; - -static void screen_init(void) { - /* initialize the global screen context */ - screen_create_context(&g_screen_ctx, SCREEN_APPLICATION_CONTEXT); -} - -static screen_context_t get_screen_context() { - pthread_once(&once_control, screen_init); - return g_screen_ctx; -} +#include -} // namespace +#include "native_sub_window.h" +#include "platform_helper_qnx.h" EGLNativeWindowType createSubWindow(FBNativeWindowType p_window, int x, int y, int width, int height, float dpr, @@ -44,7 +28,7 @@ EGLNativeWindowType createSubWindow(FBNativeWindowType p_window, int x, int y, i screen_window_t screen_window; int rc; - screen_ctx = get_screen_context(); + screen_ctx = gfxstream::qnx::getScreenContext(); if (screen_ctx == nullptr) { perror("No screen context"); return nullptr; @@ -125,7 +109,7 @@ int moveSubWindow(FBNativeWindowType p_parent_window, EGLNativeWindowType p_sub_ if (screen_set_window_property_iv(p_sub_window, SCREEN_PROPERTY_SIZE, size)) { return 0; } - return screen_flush_context(get_screen_context(), 0) == EOK; + return screen_flush_context(gfxstream::qnx::getScreenContext(), 0) == EOK; } void* getNativeDisplay() { diff --git a/host/platform_helper_qnx.cpp b/host/platform_helper_qnx.cpp new file mode 100644 index 000000000..c1c28513d --- /dev/null +++ b/host/platform_helper_qnx.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2026 BlackBerry Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "platform_helper_qnx.h" + +#include + +#include "gfxstream/common/logging.h" + +static pthread_once_t once_control = PTHREAD_ONCE_INIT; +static screen_context_t g_screen_ctx; + +static void screen_init(void) { + /* initialize the global screen context */ + screen_create_context(&g_screen_ctx, SCREEN_APPLICATION_CONTEXT); +} + +static inline int getScreenFormat(GfxstreamFormat format) { + switch (format) { + case GfxstreamFormat::B8G8R8A8_UNORM: + return SCREEN_FORMAT_RGBA8888; + case GfxstreamFormat::R8G8B8A8_UNORM: + return SCREEN_FORMAT_BGRA8888; + case GfxstreamFormat::R8G8B8X8_UNORM: + return SCREEN_FORMAT_BGRX8888; + case GfxstreamFormat::R8_UNORM: + return SCREEN_FORMAT_BYTE; + default: + return -1; + } +} + +namespace gfxstream { +namespace qnx { + +screen_context_t getScreenContext() { + pthread_once(&once_control, screen_init); + return g_screen_ctx; +} + +#define ASSERT_SCREEN_FUNC(func) \ + do { \ + int rc = (func); \ + if (rc != EOK) { \ + GFXSTREAM_ERROR("Failed QNX Screen API call: %s", strerror(errno)); \ + return std::nullopt; \ + } \ + } while (0) + +std::optional> createScreenStreamBuffer( + int width, int height, GfxstreamFormat format, std::string bufferName) { + screen_stream_t screen_stream = NULL; + ASSERT_SCREEN_FUNC(screen_create_stream(&screen_stream, getScreenContext())); + if (!screen_stream) { + GFXSTREAM_ERROR("Could not create screen_stream_t"); + return std::nullopt; + } + + const int screenUsage = SCREEN_USAGE_NATIVE | SCREEN_USAGE_OPENGL_ES2 | + SCREEN_USAGE_OPENGL_ES3 | SCREEN_USAGE_VULKAN; + ASSERT_SCREEN_FUNC( + screen_set_stream_property_iv(screen_stream, SCREEN_PROPERTY_USAGE, &screenUsage)); + + int screenFormat = getScreenFormat(format); + if (screenFormat <= 0) { + GFXSTREAM_ERROR("Could not create screen_stream_t"); + return std::nullopt; + } + ASSERT_SCREEN_FUNC( + screen_set_stream_property_iv(screen_stream, SCREEN_PROPERTY_FORMAT, &screenFormat)); + + int size[] = {width, height}; + ASSERT_SCREEN_FUNC( + screen_set_stream_property_iv(screen_stream, SCREEN_PROPERTY_BUFFER_SIZE, size)); + + ASSERT_SCREEN_FUNC(screen_set_stream_property_cv(screen_stream, SCREEN_PROPERTY_ID_STRING, + bufferName.length(), bufferName.c_str())); + + int rc = screen_create_stream_buffers(screen_stream, 1); + if (rc) { + GFXSTREAM_ERROR( + "Could not create buffer for screen_stream_t (usage=0x%x, id_str=%s, width=%d, " + "height=%d, format=%d)\n", + screenUsage, bufferName.c_str(), width, height, format); + return std::nullopt; + } + + screen_buffer_t stream_buffer; + ASSERT_SCREEN_FUNC(screen_get_stream_property_pv(screen_stream, SCREEN_PROPERTY_BUFFERS, + (void**)&stream_buffer)); + + return std::make_optional(std::make_pair(screen_stream, stream_buffer)); +} + +} // namespace qnx +} // namespace gfxstream diff --git a/host/platform_helper_qnx.h b/host/platform_helper_qnx.h new file mode 100644 index 000000000..670060f14 --- /dev/null +++ b/host/platform_helper_qnx.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2026 BlackBerry Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef PLATFORM_HELPER_QNX_H +#define PLATFORM_HELPER_QNX_H + +#include + +#include + +#include "gfxstream/host/external_object_manager.h" +#include "gfxstream/host/gfxstream_format.h" + +#ifdef __cplusplus +extern "C" { +#endif + +using namespace gfxstream::host; + +namespace gfxstream { +namespace qnx { + +screen_context_t getScreenContext(); +std::optional> createScreenStreamBuffer( + int width, int height, GfxstreamFormat format, std::string bufferName); + +} // namespace qnx +} // namespace gfxstream + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/host/vulkan/vk_common_operations.cpp b/host/vulkan/vk_common_operations.cpp index b9ba626ab..1b92ce586 100644 --- a/host/vulkan/vk_common_operations.cpp +++ b/host/vulkan/vk_common_operations.cpp @@ -54,6 +54,10 @@ #include // for MoltenVK portability extensions #endif +#if defined(__QNX__) +#include "platform_helper_qnx.h" +#endif + namespace gfxstream { namespace host { namespace vk { @@ -1887,7 +1891,8 @@ MTLResource_id VkEmulation::getMtlResourceFromVkDeviceMemory(VulkanDispatch* vk, bool VkEmulation::allocExternalMemory(VulkanDispatch* vk, VkEmulation::ExternalMemoryInfo* info, Optional deviceAlignment, Optional bufferForDedicatedAllocation, - Optional imageForDedicatedAllocation) { + Optional imageForDedicatedAllocation, + Optional colorBufferInfo) { VkExportMemoryAllocateInfo exportAi = { // filled, if supported .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO }; @@ -1911,6 +1916,13 @@ bool VkEmulation::allocExternalMemory(VulkanDispatch* vk, VkEmulation::ExternalM .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, .pHostPointer = nullptr, }; +#if defined(__QNX__) + VkImportScreenBufferInfoQNX importInfoQnx = { + .sType = VK_STRUCTURE_TYPE_IMPORT_SCREEN_BUFFER_INFO_QNX, + .pNext = nullptr, + .buffer = nullptr, + }; +#endif auto allocInfoChain = vk_make_chain_iterator(&allocInfo); @@ -1938,15 +1950,12 @@ bool VkEmulation::allocExternalMemory(VulkanDispatch* vk, VkEmulation::ExternalM vk_append_struct(&allocInfoChain, &dedicatedAllocInfo); } - bool memoryAllocated = false; - std::vector allocationAttempts; - constexpr size_t kMaxAllocationAttempts = 20u; - - // For host-allocation external memory mode, allocate host side memory first, then import - if (mDeviceInfo.externalMemoryMode == ExternalMemory::Mode::HostAllocation) { - // TODO(b/409769371): use PrivateMemory? - VkDeviceSize alignment = externalMemoryHostProperties().minImportedHostPointerAlignment; - VkDeviceSize alignedSize = ALIGN(allocInfo.allocationSize, alignment); + switch (getExternalMemoryMode()) { + // For host-allocation external memory mode, allocate host side memory first, then import + case ExternalMemory::Mode::HostAllocation: { + // TODO(b/409769371): use PrivateMemory? + VkDeviceSize alignment = externalMemoryHostProperties().minImportedHostPointerAlignment; + VkDeviceSize alignedSize = ALIGN(allocInfo.allocationSize, alignment); #ifdef _WIN32 void* hostAllocation = _aligned_malloc(alignedSize, alignment); #else @@ -1994,8 +2003,100 @@ bool VkEmulation::allocExternalMemory(VulkanDispatch* vk, VkEmulation::ExternalM info->hostAllocationPtr = hostAllocation; importInfoHostPtr.pHostPointer = hostAllocation; vk_append_struct(&allocInfoChain, &importInfoHostPtr); + break; + } + +#if defined(__QNX__) + case ExternalMemory::Mode::QnxScreenBuffer: { + if (colorBufferInfo) { + // Use QnxScreenBuffer external memory mode, export-from-Vulkan is not available; + // So, do server-side allocation first, then import to Vulkan. + // Note: External memory is only supported for ColorBuffers, in this case. + auto cbInfoPtr = *colorBufferInfo; + std::string bufferName = "VkColorBuffer-" + cbInfoPtr->handle; + auto screenStreamBuffer = gfxstream::qnx::createScreenStreamBuffer( + cbInfoPtr->width, cbInfoPtr->height, cbInfoPtr->format, bufferName); + if (!screenStreamBuffer) { + GFXSTREAM_ERROR( + "Could not create QNX Screen stream-buffer to emulate external memory " + "allocation (width: %d, height: %d, GfxstreamFormat: %s)", + cbInfoPtr->handle, cbInfoPtr->width, cbInfoPtr->height, + ToString(cbInfoPtr->format)); + return false; + } + + // Query Vulkan properties of the created screenBuffer + VkScreenBufferPropertiesQNX screenBufferProps = { + VK_STRUCTURE_TYPE_SCREEN_BUFFER_PROPERTIES_QNX, + 0, + }; + VkResult queryRes = mDvk->vkGetScreenBufferPropertiesQNX( + mDevice, screenStreamBuffer->second, &screenBufferProps); + if (VK_SUCCESS != queryRes) { + GFXSTREAM_ERROR("Failed to get QNX Screen Buffer properties, VK error: %s", + string_VkResult(queryRes)); + return false; + } + // Check the the allocated size is big enough to match ColorBuffer image memory + // requirements + if (screenBufferProps.allocationSize < info->size) { + GFXSTREAM_ERROR( + "QNX Screen buffer allocationSize (0x%lx) is not large enough for " + "ColorBuffer " + "image " + "size requirements (0x%lx)", + screenBufferProps.allocationSize, info->size); + return false; + } + // Update allocation size to match that of the screenBuffer + info->size = screenBufferProps.allocationSize; + allocInfo.allocationSize = info->size; + + // Check that there is a memoryType that covers both the VkImage and the + // screenBuffer memory requirements + const uint32_t combinedMemoryTypeBits = + screenBufferProps.memoryTypeBits & cbInfoPtr->imageMemReqs.memoryTypeBits; + if (!combinedMemoryTypeBits) { + GFXSTREAM_ERROR( + "There is no common memory type for both screenBuffer requirements (0x%x) " + "and VkImage requirements (0x%x) for ColorBuffer: %d", + screenBufferProps.memoryTypeBits, cbInfoPtr->imageMemReqs.memoryTypeBits, + cbInfoPtr->handle); + return false; + } + // Update the memory type: + info->typeIndex = getValidMemoryTypeIndex(screenBufferProps.memoryTypeBits, + cbInfoPtr->memoryProperty); + allocInfo.memoryTypeIndex = info->typeIndex; + + info->qnxScreenStreamHandle = screenStreamBuffer->first; + info->qnxScreenBufferHandle = screenStreamBuffer->second; + GFXSTREAM_DEBUG( + "Created screen_buffer_t for ColorBuffer: %d (width: %d, height: %d, " + "GfxstreamFormat: %s)", + cbInfoPtr->handle, cbInfoPtr->width, cbInfoPtr->height, + ToString(cbInfoPtr->format)); + + importInfoQnx.buffer = info->qnxScreenBufferHandle; + vk_append_struct(&allocInfoChain, &importInfoQnx); + + // Mark as external-compatible here; allocation will exit early as there is no + // VkMemory "get()" (export) operation available + cbInfoPtr->externalMemoryCompatible = true; + } + + break; + } +#endif + default: + // The default behavior is exporting the memory using some getMemory() function + // interface after allocation. + break; } + bool memoryAllocated = false; + std::vector allocationAttempts; + constexpr size_t kMaxAllocationAttempts = 20u; while (!memoryAllocated) { VkResult allocRes = vk->vkAllocateMemory(mDevice, &allocInfo, nullptr, &info->memory); @@ -2160,6 +2261,12 @@ bool VkEmulation::allocExternalMemory(VulkanDispatch* vk, VkEmulation::ExternalM return false; } + if (colorBufferInfo) { + // The corresponding getMemory() function succeeded; mark ColorBuffer memory as + // "external-compatible" + (*colorBufferInfo)->externalMemoryCompatible = true; + } + return true; } @@ -2209,6 +2316,14 @@ void VkEmulation::freeExternalMemoryLocked(VulkanDispatch* vk, if (info->externalMetalHandle) { CFRelease(info->externalMetalHandle); } +#endif +#if defined(__QNX__) + // Note: Destroying the screen_stream_t will also destroy the underyling buffers. + if (info->qnxScreenStreamHandle) { + screen_destroy_stream(info->qnxScreenStreamHandle); + info->qnxScreenStreamHandle = nullptr; + } + info->qnxScreenBufferHandle = nullptr; #endif if (info->hostAllocationPtr) { #ifdef _WIN32 @@ -2288,12 +2403,12 @@ bool VkEmulation::importExternalMemory(VulkanDispatch* vk, VkDevice targetDevice break; } #endif -#ifdef __QNX__ +#if defined(__QNX__) case ExternalMemory::Mode::QnxScreenBuffer: { - if (!handleInfo) { + if (!info->qnxScreenBufferHandle) { GFXSTREAM_ERROR( - "%s: external handle info is not available, cannot retrieve " - "handle with external memory mode %s.", + "%s: external qnxScreenBufferHandle is not available for import to Vulkan " + "memory; it is required for external memory mode %s.", __func__, ExternalMemory::to_string(mDeviceInfo.externalMemoryMode)); return false; } @@ -2301,7 +2416,7 @@ bool VkEmulation::importExternalMemory(VulkanDispatch* vk, VkDevice targetDevice importInfoQnx = { VK_STRUCTURE_TYPE_IMPORT_SCREEN_BUFFER_INFO_QNX, dedicatedAllocInfoPtr, - static_cast(reinterpret_cast(handleInfo->handle)), + info->qnxScreenBufferHandle, }; importInfoPtr = &importInfoQnx; break; @@ -2586,7 +2701,6 @@ VkEmulation::GetInternalFormatLocked(GfxstreamFormat format) { return format; } - // TODO(liyl): Currently we can only specify required memoryProperty // and initial layout for a color buffer. // @@ -2675,12 +2789,13 @@ bool VkEmulation::createVkColorBufferLocked(uint32_t width, uint32_t height, imageCi->pQueueFamilyIndices = nullptr; imageCi->initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - // Create the image. If external memory is supported, make it external. + // Create the image VkExternalMemoryImageCreateInfo extImageCi = { VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO }; - - if (extMemHandleInfo || mDeviceInfo.supportsExternalMemoryExport) { + if (mDeviceInfo.supportsExternalMemoryExport || mDeviceInfo.supportsExternalMemoryImport) { + // If external memory is supported (either by import or export), then append + // VkExternalMemoryImageCreateInfo unconditionally, as it may be backed by external memory. extImageCi.handleTypes = static_cast(getDefaultExternalMemoryHandleType()); @@ -2701,7 +2816,6 @@ bool VkEmulation::createVkColorBufferLocked(uint32_t width, uint32_t height, infoPtr->imageCreateInfoShallow = vk_make_orphan_copy(*imageCi); infoPtr->currentQueueFamilyIndex = mQueueFamilyIndex; - VkMemoryRequirements memReqs; if (!useDedicated && vk->vkGetImageMemoryRequirements2KHR) { VkMemoryDedicatedRequirements dedicated_reqs{ VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, nullptr}; @@ -2711,16 +2825,16 @@ bool VkEmulation::createVkColorBufferLocked(uint32_t width, uint32_t height, nullptr, infoPtr->image}; vk->vkGetImageMemoryRequirements2KHR(mDevice, &info, &reqs); useDedicated = dedicated_reqs.requiresDedicatedAllocation; - memReqs = reqs.memoryRequirements; + infoPtr->imageMemReqs = reqs.memoryRequirements; } else { - vk->vkGetImageMemoryRequirements(mDevice, infoPtr->image, &memReqs); + vk->vkGetImageMemoryRequirements(mDevice, infoPtr->image, &infoPtr->imageMemReqs); } if (extMemHandleInfo) { infoPtr->memory.handleInfo = extMemHandleInfo; infoPtr->memory.dedicatedAllocation = true; // External memory might change the memReqs for allocation - if (!updateMemReqsForExtMem(extMemHandleInfo, &memReqs)) { + if (!updateMemReqsForExtMem(extMemHandleInfo, &infoPtr->imageMemReqs)) { GFXSTREAM_ERROR( "Failed to update memReqs for ColorBuffer memory allocation with external memory: " "%d\n", @@ -2742,11 +2856,11 @@ bool VkEmulation::createVkColorBufferLocked(uint32_t width, uint32_t height, infoPtr->memoryProperty = infoPtr->memoryProperty & (VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); - infoPtr->memory.size = memReqs.size; + infoPtr->memory.size = infoPtr->imageMemReqs.size; // Determine memory type. infoPtr->memory.typeIndex = - getValidMemoryTypeIndex(memReqs.memoryTypeBits, infoPtr->memoryProperty); + getValidMemoryTypeIndex(infoPtr->imageMemReqs.memoryTypeBits, infoPtr->memoryProperty); const VkFormat imageVkFormat = infoPtr->imageCreateInfoShallow.format; GFXSTREAM_DEBUG( @@ -2758,8 +2872,9 @@ bool VkEmulation::createVkColorBufferLocked(uint32_t width, uint32_t height, infoPtr->memoryProperty); const bool isHostVisible = (infoPtr->memoryProperty & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); - Optional deviceAlignment = - (!extMemHandleInfo && isHostVisible) ? Optional(memReqs.alignment) : kNullopt; + Optional deviceAlignment = (!extMemHandleInfo && isHostVisible) + ? Optional(infoPtr->imageMemReqs.alignment) + : kNullopt; Optional dedicatedImage = useDedicated ? Optional(infoPtr->image) : kNullopt; if (extMemHandleInfo) { VkMemoryDedicatedAllocateInfo dedicatedInfo = { @@ -2782,14 +2897,12 @@ bool VkEmulation::createVkColorBufferLocked(uint32_t width, uint32_t height, infoPtr->externalMemoryCompatible = true; } else { - bool allocRes = allocExternalMemory(vk, &infoPtr->memory, - deviceAlignment, kNullopt, dedicatedImage); + bool allocRes = allocExternalMemory(vk, &infoPtr->memory, deviceAlignment, kNullopt, + dedicatedImage, infoPtr); if (!allocRes) { GFXSTREAM_ERROR("Failed to allocate ColorBuffer with Vulkan backing."); return false; } - - infoPtr->externalMemoryCompatible = mDeviceInfo.supportsExternalMemoryExport; } infoPtr->memory.pageOffset = reinterpret_cast(infoPtr->memory.mappedPtr) % kPageSize; @@ -3935,6 +4048,20 @@ MTLResource_id VkEmulation::getColorBufferMetalMemoryHandle(uint32_t colorBuffer } #endif // __APPLE__ +#if defined(__QNX__) +screen_buffer_t VkEmulation::getColorBufferScreenBufferQnxHandle(uint32_t colorBuffer) { + std::lock_guard lock(mMutex); + + auto infoPtr = gfxstream::base::find(mColorBuffers, colorBuffer); + + if (!infoPtr) { + return nullptr; + } + + return infoPtr->memory.qnxScreenBufferHandle; +} +#endif + bool VkEmulation::setColorBufferVulkanMode(uint32_t colorBuffer, uint32_t vulkanMode) { std::lock_guard lock(mMutex); diff --git a/host/vulkan/vk_common_operations.h b/host/vulkan/vk_common_operations.h index 471305f23..5e0c00e54 100644 --- a/host/vulkan/vk_common_operations.h +++ b/host/vulkan/vk_common_operations.h @@ -229,7 +229,6 @@ class VkEmulation { uint32_t typeIndex; // Output fields - uint32_t id = 0; VkDeviceMemory memory = VK_NULL_HANDLE; // host-mapping fields @@ -251,6 +250,12 @@ class VkEmulation { // This is used as an external handle with ExternalMemory::Mode::Metal MTLResource_id externalMetalHandle = nullptr; #endif +#if defined(__QNX__) + // Note: The stream handle is the parent of the buffer handle + screen_stream_t qnxScreenStreamHandle = nullptr; + screen_buffer_t qnxScreenBufferHandle = nullptr; +#endif + // Used with ExternalMemory::Mode::HostAllocation // TODO: refactor to be able to change handle type based on external memory mode // and move it into ExternalHandleInfo to support external memory exports or use @@ -260,17 +265,6 @@ class VkEmulation { bool dedicatedAllocation = false; }; - bool allocExternalMemory( - VulkanDispatch* vk, ExternalMemoryInfo* info, - gfxstream::base::Optional deviceAlignment = gfxstream::base::kNullopt, - gfxstream::base::Optional bufferForDedicatedAllocation = gfxstream::base::kNullopt, - gfxstream::base::Optional imageForDedicatedAllocation = gfxstream::base::kNullopt); - - bool importExternalMemory(VulkanDispatch* vk, VkDevice targetDevice, - const ExternalMemoryInfo* info, - VkMemoryDedicatedAllocateInfo* dedicatedAllocInfo, - VkDeviceMemory* out); - enum class VulkanMode { // Default: ColorBuffers can still be used with the existing GL-based // API. Synchronization with (if it exists) Vulkan images happens on @@ -327,6 +321,7 @@ class VkEmulation { VkImage image = VK_NULL_HANDLE; VkImageView imageView = VK_NULL_HANDLE; VkImageCreateInfo imageCreateInfoShallow = {}; + VkMemoryRequirements imageMemReqs = {}; VkImageLayout currentLayout = VK_IMAGE_LAYOUT_UNDEFINED; uint32_t currentQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL; @@ -336,6 +331,20 @@ class VkEmulation { VulkanMode vulkanMode = VulkanMode::Default; }; + + bool allocExternalMemory( + VulkanDispatch* vk, ExternalMemoryInfo* info, + gfxstream::base::Optional deviceAlignment = gfxstream::base::kNullopt, + gfxstream::base::Optional bufferForDedicatedAllocation = + gfxstream::base::kNullopt, + gfxstream::base::Optional imageForDedicatedAllocation = gfxstream::base::kNullopt, + gfxstream::base::Optional colorBufferInfo = gfxstream::base::kNullopt); + + bool importExternalMemory(VulkanDispatch* vk, VkDevice targetDevice, + const ExternalMemoryInfo* info, + VkMemoryDedicatedAllocateInfo* dedicatedAllocInfo, + VkDeviceMemory* out); + std::optional getColorBufferInfo(uint32_t colorBufferHandle); struct BufferInfo { @@ -358,6 +367,9 @@ class VkEmulation { #ifdef __APPLE__ MTLResource_id getColorBufferMetalMemoryHandle(uint32_t colorBufferHandle); #endif +#if defined(__QNX__) + screen_buffer_t getColorBufferScreenBufferQnxHandle(uint32_t colorBufferHandle); +#endif struct VkColorBufferMemoryExport { ExternalHandleInfo handleInfo; diff --git a/host/vulkan/vk_decoder_global_state.cpp b/host/vulkan/vk_decoder_global_state.cpp index 699204643..14e70c385 100644 --- a/host/vulkan/vk_decoder_global_state.cpp +++ b/host/vulkan/vk_decoder_global_state.cpp @@ -5871,7 +5871,27 @@ class VkDecoderGlobalState::Impl { opaqueFd = false; } #endif +#if defined(__QNX__) + // Use QNX Screen buffer extension on host-vulkan mode for color buffer import, + // other paths on QNX may use FD handles ... + if (m_vkEmulation->getExternalMemoryMode() == + ExternalMemory::Mode::QnxScreenBuffer) { + screen_buffer_t cbExtMemoryHandle = + m_vkEmulation->getColorBufferScreenBufferQnxHandle( + importCbInfoPtr->colorBuffer); + if (cbExtMemoryHandle == nullptr) { + GFXSTREAM_ERROR( + "%s: VK_ERROR_OUT_OF_DEVICE_MEMORY: " + "colorBuffer 0x%x does not have Vulkan external memory backing", + __func__, importCbInfoPtr->colorBuffer); + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + importScreenBufferInfo.buffer = cbExtMemoryHandle; + vk_append_struct(&structChainIter, &importScreenBufferInfo); + opaqueFd = false; + } +#endif if (m_vkEmulation->getExternalMemoryMode() == ExternalMemory::Mode::HostAllocation) { importHostInfo.pHostPointer = m_vkEmulation->getColorBufferHostPointer(importCbInfoPtr->colorBuffer); @@ -5962,6 +5982,17 @@ class VkDecoderGlobalState::Impl { opaqueFd = false; } #endif +#if defined(__QNX__) + if (m_vkEmulation->getExternalMemoryMode() == ExternalMemory::Mode::QnxScreenBuffer) { + GFXSTREAM_ERROR( + "%s: VK_ERROR_OUT_OF_DEVICE_MEMORY: " + "ExternalMemory::Mode::QnxScreenBuffer does not support memory externalization " + "for gfxstream BufferVk objects.", + __func__); + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } +#endif + if (m_vkEmulation->getExternalMemoryMode() == ExternalMemory::Mode::HostAllocation) { importHostInfo.pHostPointer = m_vkEmulation->getBufferHostPointer(importBufferInfoPtr->buffer); From 5c7e3ee6dae1790ba4a70c63dfafe463116f138d Mon Sep 17 00:00:00 2001 From: Aaron Ruby Date: Tue, 17 Mar 2026 14:34:25 -0400 Subject: [PATCH 2/3] Switch from Metal-specific helper function to just checking with the generic memoryMode interface --- host/vulkan/vk_common_operations.cpp | 2 +- host/vulkan/vk_common_operations.h | 3 --- host/vulkan/vk_decoder_global_state.cpp | 11 ++++++----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/host/vulkan/vk_common_operations.cpp b/host/vulkan/vk_common_operations.cpp index 1b92ce586..beeb10fbc 100644 --- a/host/vulkan/vk_common_operations.cpp +++ b/host/vulkan/vk_common_operations.cpp @@ -2843,7 +2843,7 @@ bool VkEmulation::createVkColorBufferLocked(uint32_t width, uint32_t height, } #ifdef __APPLE_ // importExtMemoryHandleToVkColorBuffer is not supported with external memory metal - if (supportsExternalMemoryMetal()) { + if (getExternalMemoryMode() == ExternalMemory::Mode::Metal) { GFXSTREAM_WARNING("extMemhandleInfo import in ColorBuffer creation is unexpected."); infoPtr->memory.externalMetalHandle = nullptr; } diff --git a/host/vulkan/vk_common_operations.h b/host/vulkan/vk_common_operations.h index 5e0c00e54..c62ce4449 100644 --- a/host/vulkan/vk_common_operations.h +++ b/host/vulkan/vk_common_operations.h @@ -190,9 +190,6 @@ class VkEmulation { VkExternalMemoryHandleTypeFlagBits getDefaultExternalMemoryHandleType(); void appendExternalMemoryModeDeviceExtensions(std::vector& outDeviceExtensions); ExternalMemory::Mode getExternalMemoryMode() const; - bool supportsExternalMemoryMetal() { - return (getExternalMemoryMode() == ExternalMemory::Mode::Metal); - } bool supportsExternalMemory() { return (getExternalMemoryMode() != ExternalMemory::Mode::NotSupported); } diff --git a/host/vulkan/vk_decoder_global_state.cpp b/host/vulkan/vk_decoder_global_state.cpp index 14e70c385..ae51cbb5d 100644 --- a/host/vulkan/vk_decoder_global_state.cpp +++ b/host/vulkan/vk_decoder_global_state.cpp @@ -1887,7 +1887,8 @@ class VkDecoderGlobalState::Impl { bool shouldPassthrough = !m_vkEmulation->isYcbcrEmulationEnabled(); #if defined(__APPLE__) - shouldPassthrough = shouldPassthrough && !m_vkEmulation->supportsExternalMemoryMetal(); + shouldPassthrough = shouldPassthrough && !(m_vkEmulation->getExternalMemoryMode() == + ExternalMemory::Mode::Metal); #endif if (shouldPassthrough) { return vk->vkEnumerateDeviceExtensionProperties(physicalDevice, pLayerName, @@ -1915,7 +1916,7 @@ class VkDecoderGlobalState::Impl { #if defined(__APPLE__) && defined(VK_MVK_moltenvk) // Guest will check for VK_MVK_moltenvk extension for enabling AHB support - if (m_vkEmulation->supportsExternalMemoryMetal() && + if ((m_vkEmulation->getExternalMemoryMode() == ExternalMemory::Mode::Metal) && !hasDeviceExtension(properties, VK_MVK_MOLTENVK_EXTENSION_NAME)) { // TODO(b/433496880): make sure any relevant guest image will check external memory // metal instead @@ -5852,7 +5853,7 @@ class VkDecoderGlobalState::Impl { #if defined(__APPLE__) // Use metal object extension on host-vulkan mode for color buffer import, // other paths on MacOS will use FD handles - if (m_vkEmulation->supportsExternalMemoryMetal()) { + if (m_vkEmulation->getExternalMemoryMode() == ExternalMemory::Mode::Metal) { MTLResource_id cbExtMemoryHandle = m_vkEmulation->getColorBufferMetalMemoryHandle( importCbInfoPtr->colorBuffer); @@ -5961,7 +5962,7 @@ class VkDecoderGlobalState::Impl { bool opaqueFd = true; #ifdef __APPLE__ - if (m_vkEmulation->supportsExternalMemoryMetal()) { + if (m_vkEmulation->getExternalMemoryMode() == ExternalMemory::Mode::Metal) { MTLResource_id bufferMetalMemoryHandle = m_vkEmulation->getBufferMetalMemoryHandle(importBufferInfoPtr->buffer); @@ -6789,7 +6790,7 @@ class VkDecoderGlobalState::Impl { STREAM_HANDLE_TYPE_MEM_SHM, info->caching, std::nullopt); } else if (m_vkEmulation->getFeatures().ExternalBlob.enabled) { #ifdef __APPLE__ - if (m_vkEmulation->supportsExternalMemoryMetal()) { + if (m_vkEmulation->getExternalMemoryMode() == ExternalMemory::Mode::Metal) { GFXSTREAM_FATAL("ExternalBlob feature is not supported with external memory metal"); } #endif From 033e1f54a1ea06d2f56b89dd263c80f3ec90705d Mon Sep 17 00:00:00 2001 From: Aaron Ruby Date: Tue, 17 Mar 2026 16:53:51 -0400 Subject: [PATCH 3/3] Add missing libdrm dependency for Bazel end2end tests --- tests/end2end/BUILD.bazel | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/end2end/BUILD.bazel b/tests/end2end/BUILD.bazel index 2450f83d8..c5e292169 100644 --- a/tests/end2end/BUILD.bazel +++ b/tests/end2end/BUILD.bazel @@ -93,5 +93,6 @@ cc_test( "@mesa//:mesa_gfxstream_guest_platform", "@rules_cc//cc/runfiles", "@rutabaga//:rutabaga_gfx_kumquat_client_ffi_headers", + "@drm//:libdrm", ], )