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..beeb10fbc 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", @@ -2729,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; } @@ -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..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); } @@ -229,7 +226,6 @@ class VkEmulation { uint32_t typeIndex; // Output fields - uint32_t id = 0; VkDeviceMemory memory = VK_NULL_HANDLE; // host-mapping fields @@ -251,6 +247,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 +262,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 +318,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 +328,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 +364,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..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); @@ -5871,7 +5872,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); @@ -5941,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); @@ -5962,6 +5983,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); @@ -6758,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 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", ], )