vulkan: Support imported host-visible memory on Fuchsia.
This change adds required logic to support GOLDFISH_HOST_VISIBLE
heap and host-visible external memory allocation on Fuchsia.
It includes the following parts:
(1) BufferCollectionConstraints:
Now both DEVICE_LOCAL and HOST_VISIBLE domain can be added to
the buffer_constraints.
Currently, the domain of buffer collection constraints set in
"setBufferCollectionConstraints(collection, image_create_info)"
suppots both Heap types; other sysmem participants need to specify
buffer memory constraints (cpu_supported, ram_supported,
inaccessible_supported) or directly specify HeapType to choose
the actual Heap.
(2) Stride:
The stride of the Image will be determined by host GPU using
vkGetLinearImageLayoutGOOGLE() method; other sysmem participants
should NOT specify stride / offset requirements.
(3) Usage:
Currently the VMO and VMAR rights is completely determined by the
VkImage usage. It is writeable only when the usages in their
ImageCreateInfo contains some writeable types (e.g. TRANSFER_DST).
TEST=terminal and spinning-square-rs on FEMU (software-gpu).
Both apps uses host rendering and allocates image buffers from sysmem
host-visible Heap.
Bug: fuchsia:54153
Change-Id: I8268b5fbe6d8a547bde2b02537130530849831f7
diff --git a/system/vulkan_enc/ResourceTracker.cpp b/system/vulkan_enc/ResourceTracker.cpp
index ecf5b1f..88f9e2b 100644
--- a/system/vulkan_enc/ResourceTracker.cpp
+++ b/system/vulkan_enc/ResourceTracker.cpp
@@ -52,6 +52,7 @@
#include <lib/zx/vmo.h>
#include <zircon/errors.h>
#include <zircon/process.h>
+#include <zircon/rights.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/object.h>
@@ -1740,9 +1741,9 @@
delete sysmem_collection;
}
- inline llcpp::fuchsia::sysmem::BufferCollectionConstraints
- defaultBufferCollectionConstraints(size_t min_size_bytes,
- size_t buffer_count) {
+ inline llcpp::fuchsia::sysmem::BufferCollectionConstraints defaultBufferCollectionConstraints(
+ size_t min_size_bytes,
+ size_t buffer_count) {
llcpp::fuchsia::sysmem::BufferCollectionConstraints constraints = {};
constraints.min_buffer_count = buffer_count;
constraints.has_buffer_memory_constraints = true;
@@ -1753,12 +1754,16 @@
buffer_constraints.max_size_bytes = 0xffffffff;
buffer_constraints.physically_contiguous_required = false;
buffer_constraints.secure_required = false;
- buffer_constraints.ram_domain_supported = false;
- buffer_constraints.cpu_domain_supported = false;
+
+ // No restrictions on coherency domain or Heaps.
+ buffer_constraints.ram_domain_supported = true;
+ buffer_constraints.cpu_domain_supported = true;
buffer_constraints.inaccessible_domain_supported = true;
- buffer_constraints.heap_permitted_count = 1;
+ buffer_constraints.heap_permitted_count = 2;
buffer_constraints.heap_permitted[0] =
llcpp::fuchsia::sysmem::HeapType::GOLDFISH_DEVICE_LOCAL;
+ buffer_constraints.heap_permitted[1] =
+ llcpp::fuchsia::sysmem::HeapType::GOLDFISH_HOST_VISIBLE;
return constraints;
}
@@ -1808,6 +1813,7 @@
}
VkResult setBufferCollectionConstraints(
+ VkEncoder* enc, VkDevice device,
llcpp::fuchsia::sysmem::BufferCollection::SyncClient* collection,
const VkImageCreateInfo* pImageInfo) {
if (pImageInfo == nullptr) {
@@ -1815,13 +1821,9 @@
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
- // TODO(liyl): Currently the size only works for RGBA8 and BGRA8 images.
- // We should set the size based on its actual format.
llcpp::fuchsia::sysmem::BufferCollectionConstraints constraints =
defaultBufferCollectionConstraints(
- /* min_size_bytes */ pImageInfo->extent.width *
- pImageInfo->extent.height * 4,
- /* buffer_count */ 1u);
+ /* min_size_bytes */ 0, /* buffer_count */ 1u);
constraints.usage.vulkan =
getBufferCollectionConstraintsVulkanImageUsage(pImageInfo);
@@ -1841,6 +1843,14 @@
constraints.image_format_constraints_count = formats.size();
uint32_t format_index = 0;
for (VkFormat format : formats) {
+ // Get row alignment from host GPU.
+ VkDeviceSize offset;
+ VkDeviceSize rowPitchAlignment;
+ enc->vkGetLinearImageLayoutGOOGLE(device, format, &offset, &rowPitchAlignment);
+
+ ALOGD("vkGetLinearImageLayoutGOOGLE: format %d offset %lu rowPitchAlignment = %lu",
+ (int)format, offset, rowPitchAlignment);
+
llcpp::fuchsia::sysmem::ImageFormatConstraints&
image_constraints =
constraints.image_format_constraints[format_index++];
@@ -1873,15 +1883,16 @@
image_constraints.max_coded_width = 0xfffffff;
image_constraints.min_coded_height = pImageInfo->extent.height;
image_constraints.max_coded_height = 0xffffffff;
- image_constraints.min_bytes_per_row =
- pImageInfo->extent.width * 4;
+ // The min_bytes_per_row can be calculated by sysmem using
+ // |min_coded_width|, |bytes_per_row_divisor| and color format.
+ image_constraints.min_bytes_per_row = 0;
image_constraints.max_bytes_per_row = 0xffffffff;
image_constraints.max_coded_width_times_coded_height =
0xffffffff;
image_constraints.layers = 1;
image_constraints.coded_width_divisor = 1;
image_constraints.coded_height_divisor = 1;
- image_constraints.bytes_per_row_divisor = 1;
+ image_constraints.bytes_per_row_divisor = rowPitchAlignment;
image_constraints.start_offset_divisor = 1;
image_constraints.display_width_divisor = 1;
image_constraints.display_height_divisor = 1;
@@ -1909,8 +1920,7 @@
llcpp::fuchsia::sysmem::BufferCollectionConstraints constraints =
defaultBufferCollectionConstraints(
- /* min_size_bytes */ pBufferConstraintsInfo->pBufferCreateInfo
- ->size,
+ /* min_size_bytes */ pBufferConstraintsInfo->pBufferCreateInfo->size,
/* buffer_count */ pBufferConstraintsInfo->minCount);
constraints.usage.vulkan =
getBufferCollectionConstraintsVulkanBufferUsage(
@@ -1926,12 +1936,13 @@
}
VkResult on_vkSetBufferCollectionConstraintsFUCHSIA(
- void*, VkResult, VkDevice,
+ void* context, VkResult, VkDevice device,
VkBufferCollectionFUCHSIA collection,
const VkImageCreateInfo* pImageInfo) {
+ VkEncoder* enc = (VkEncoder*)context;
auto sysmem_collection = reinterpret_cast<
llcpp::fuchsia::sysmem::BufferCollection::SyncClient*>(collection);
- return setBufferCollectionConstraints(sysmem_collection, pImageInfo);
+ return setBufferCollectionConstraints(enc, device, sysmem_collection, pImageInfo);
}
VkResult on_vkSetBufferCollectionBufferConstraintsFUCHSIA(
@@ -1963,9 +1974,11 @@
llcpp::fuchsia::sysmem::BufferCollectionInfo_2 info =
std::move(result.Unwrap()->buffer_collection_info);
- if (!info.settings.has_image_format_constraints) {
- return VK_ERROR_INITIALIZATION_FAILED;
- }
+ bool is_host_visible = info.settings.buffer_settings.coherency_domain ==
+ llcpp::fuchsia::sysmem::CoherencyDomain::CPU;
+ bool is_device_local = info.settings.buffer_settings.coherency_domain ==
+ llcpp::fuchsia::sysmem::CoherencyDomain::INACCESSIBLE;
+
pProperties->count = info.buffer_count;
AutoLock lock(mLock);
@@ -1981,8 +1994,10 @@
// Device local memory type supported.
pProperties->memoryTypeBits = 0;
for (uint32_t i = 0; i < deviceInfo.memProps.memoryTypeCount; ++i) {
- if (deviceInfo.memProps.memoryTypes[i].propertyFlags &
- VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
+ if ((is_device_local && (deviceInfo.memProps.memoryTypes[i].propertyFlags &
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) ||
+ (is_host_visible && (deviceInfo.memProps.memoryTypes[i].propertyFlags &
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))) {
pProperties->memoryTypeBits |= 1ull << i;
}
}
@@ -2469,7 +2484,7 @@
std::move(collection_client));
if (hasDedicatedImage) {
VkResult res = setBufferCollectionConstraints(
- &collection, pImageCreateInfo);
+ enc, device, &collection, pImageCreateInfo);
if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) {
ALOGE("setBufferCollectionConstraints failed: format %u is not supported",
pImageCreateInfo->format);
@@ -2654,12 +2669,44 @@
abort();
}
+#ifdef VK_USE_PLATFORM_FUCHSIA
if (vmo_handle != ZX_HANDLE_INVALID) {
- ALOGE("%s: Host visible export/import allocation "
- "of VMO is not supported yet.",
- __func__);
- abort();
+ input_result = enc->vkAllocateMemory(device, &finalAllocInfo, pAllocator, pMemory);
+
+ // Get VMO handle rights, and only use allowed rights to map the
+ // host memory.
+ zx_info_handle_basic handle_info;
+ zx_status_t status = zx_object_get_info(vmo_handle, ZX_INFO_HANDLE_BASIC, &handle_info,
+ sizeof(handle_info), nullptr, nullptr);
+ if (status != ZX_OK) {
+ ALOGE("%s: cannot get vmo object info: vmo = %u status: %d.", __func__, vmo_handle,
+ status);
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+ }
+
+ zx_vm_option_t vm_permission = 0u;
+ vm_permission |= (handle_info.rights & ZX_RIGHT_READ) ? ZX_VM_PERM_READ : 0;
+ vm_permission |= (handle_info.rights & ZX_RIGHT_WRITE) ? ZX_VM_PERM_WRITE : 0;
+
+ zx_paddr_t addr;
+ status = zx_vmar_map(zx_vmar_root_self(), vm_permission, 0, vmo_handle, 0,
+ finalAllocInfo.allocationSize, &addr);
+ if (status != ZX_OK) {
+ ALOGE("%s: cannot map vmar: status %d.", __func__, status);
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+ }
+
+ D("host visible alloc (external): "
+ "size 0x%llx host ptr %p mapped size 0x%llx",
+ (unsigned long long)finalAllocInfo.allocationSize, mappedPtr,
+ (unsigned long long)mappedSize);
+ setDeviceMemoryInfo(device, *pMemory,
+ finalAllocInfo.allocationSize, finalAllocInfo.allocationSize,
+ reinterpret_cast<uint8_t*>(addr), finalAllocInfo.memoryTypeIndex,
+ /*ahw=*/nullptr, vmo_handle);
+ return VK_SUCCESS;
}
+#endif
// Host visible memory, non external
bool directMappingSupported = usingDirectMapping();
@@ -2755,6 +2802,17 @@
if (it == info_VkDeviceMemory.end()) return;
auto& info = it->second;
+#ifdef VK_USE_PLATFORM_FUCHSIA
+ if (info.vmoHandle && info.mappedPtr) {
+ zx_status_t status = zx_vmar_unmap(
+ zx_vmar_root_self(), reinterpret_cast<zx_paddr_t>(info.mappedPtr), info.mappedSize);
+ if (status != ZX_OK) {
+ ALOGE("%s: Cannot unmap mappedPtr: status %d", status);
+ }
+ info.mappedPtr = nullptr;
+ }
+#endif
+
if (!info.directMapped) {
lock.unlock();
VkEncoder* enc = (VkEncoder*)context;
@@ -3021,36 +3079,62 @@
}
if (vmo.is_valid()) {
- auto format = info.settings.image_format_constraints.pixel_format.type ==
- llcpp::fuchsia::sysmem::PixelFormatType::R8G8B8A8
- ? llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::RGBA
- : llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::BGRA;
+ zx::vmo vmo_dup;
+ if (zx_status_t status = vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_dup);
+ status != ZX_OK) {
+ ALOGE("%s: zx_vmo_duplicate failed: %d", __func__, status);
+ abort();
+ }
- auto createParams =
- llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Builder(
- std::make_unique<
- llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Frame>())
- .set_width(std::make_unique<uint32_t>(
- info.settings.image_format_constraints.min_coded_width))
- .set_height(std::make_unique<uint32_t>(
- info.settings.image_format_constraints.min_coded_height))
- .set_format(
- std::make_unique<
- llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType>(format))
- .set_memory_property(std::make_unique<uint32_t>(
- llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_DEVICE_LOCAL))
- .build();
+ auto buffer_handle_result = mControlDevice->GetBufferHandle(std::move(vmo_dup));
+ if (!buffer_handle_result.ok()) {
+ ALOGE("%s: GetBufferHandle FIDL error: %d", __func__,
+ buffer_handle_result.status());
+ abort();
+ }
+ if (buffer_handle_result.value().res == ZX_OK) {
+ // Buffer handle already exists.
+ // If it is a ColorBuffer, no-op; Otherwise return error.
+ if (buffer_handle_result.value().type !=
+ llcpp::fuchsia::hardware::goldfish::BufferHandleType::COLOR_BUFFER) {
+ ALOGE("%s: BufferHandle %u is not a ColorBuffer", __func__,
+ buffer_handle_result.value().id);
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+ }
+ } else if (buffer_handle_result.value().res == ZX_ERR_NOT_FOUND) {
+ // Buffer handle not found. Create ColorBuffer based on buffer settings.
+ auto format =
+ info.settings.image_format_constraints.pixel_format.type ==
+ llcpp::fuchsia::sysmem::PixelFormatType::R8G8B8A8
+ ? llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::RGBA
+ : llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::BGRA;
- auto result =
- mControlDevice->CreateColorBuffer2(std::move(vmo), std::move(createParams));
- if (!result.ok() || result.Unwrap()->res != ZX_OK) {
- if (result.ok() &&
- result.Unwrap()->res == ZX_ERR_ALREADY_EXISTS) {
- ALOGD("CreateColorBuffer: color buffer already exists");
- } else {
- ALOGE("CreateColorBuffer failed: %d:%d", result.status(),
- GET_STATUS_SAFE(result, res));
- }
+ uint32_t memory_property =
+ info.settings.buffer_settings.heap ==
+ llcpp::fuchsia::sysmem::HeapType::GOLDFISH_DEVICE_LOCAL
+ ? llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_DEVICE_LOCAL
+ : llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_HOST_VISIBLE;
+
+ auto createParams =
+ llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Builder(
+ std::make_unique<llcpp::fuchsia::hardware::goldfish::
+ CreateColorBuffer2Params::Frame>())
+ .set_width(std::make_unique<uint32_t>(
+ info.settings.image_format_constraints.min_coded_width))
+ .set_height(std::make_unique<uint32_t>(
+ info.settings.image_format_constraints.min_coded_height))
+ .set_format(std::make_unique<
+ llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType>(
+ format))
+ .set_memory_property(std::make_unique<uint32_t>(memory_property))
+ .build();
+
+ auto result =
+ mControlDevice->CreateColorBuffer2(std::move(vmo), std::move(createParams));
+ if (!result.ok() || result.Unwrap()->res != ZX_OK) {
+ ALOGE("CreateColorBuffer failed: %d:%d", result.status(),
+ GET_STATUS_SAFE(result, res));
+ }
}
}
isSysmemBackedMemory = true;