gfxstream: answer a deferred AHB image layout from the AHB itself - #176
gfxstream: answer a deferred AHB image layout from the AHB itself#176jvle wants to merge 4 commits into
Conversation
gurchetansingh
left a comment
There was a problem hiding this comment.
Can you separate the codegen changes to commit (1) [with a no-op impl] and the actual implemntation to commit (2)? Thanks!
| // AHB is imported at the dedicated allocation so the driver resolves the layout on bind. | ||
| // shared_ptr with a releasing deleter so every mImageInfo teardown path frees it, including | ||
| // clearLocked()'s bulk clear() which runs neither destroy helper. | ||
| std::shared_ptr<AHardwareBuffer> deferredLayoutAhb; |
There was a problem hiding this comment.
Group these into:
struct DeferredLayoutInfo {
std::shared_ptr<AHardwareBuffer> ahb;
VkDeviceSize size = 0;
VkDeviceSize alignment = 0;
uint32_t memoryTypeBits = 0;
VkDeviceSize rowPitch = 0;
};
| // TODO: might need to use an array of layouts to represent each sub resource | ||
| VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; | ||
| VkDeviceMemory memory = VK_NULL_HANDLE; | ||
| // External memory handle types the image was created with, taken from the |
There was a problem hiding this comment.
Umm, looks like LLM-speak :-), I suggest rewording this or just deleting this.
| return; | ||
| } | ||
|
|
||
| // Deferred image layout (see ImageInfo::deferredLayoutAhb). Placed AFTER |
There was a problem hiding this comment.
This can be consolidated into a helper function:
void updateImageMemoryRequirementsLocked(VkDevice device, VkImage image,
VkMemoryRequirements* pMemoryRequirements) REQUIRES(mMutex) {
auto* imageInfo = gfxstream::base::find(mImageInfo, image);
if (!imageInfo) return;
if (imageInfo->compressInfo) {
*pMemoryRequirements = imageInfo->compressInfo->getMemoryRequirements();
return;
}
#ifdef __ANDROID__
if (pMemoryRequirements && pMemoryRequirements->size == 0 && imageInfo->deferredLayoutSize > 0) {
pMemoryRequirements->size = imageInfo->deferredLayoutSize;
pMemoryRequirements->alignment = imageInfo->deferredLayoutAlignment;
pMemoryRequirements->memoryTypeBits = imageInfo->deferredLayoutMemoryTypeBits;
}
#endif
}
| .pNext = nullptr, | ||
| .buffer = nullptr, | ||
| }; | ||
| if (dedicatedAllocInfoPtr && dedicatedAllocInfoPtr->image != VK_NULL_HANDLE) { |
There was a problem hiding this comment.
Hold a shared_ptr<> since you drop the mutex:
std::shared_ptr<AHardwareBuffer> deferredAhbHold;
if (dedicatedAllocInfoPtr && dedicatedAllocInfoPtr->image != VK_NULL_HANDLE) {
std::lock_guard<std::mutex> dlLock(mMutex);
auto* dlInfo = gfxstream::base::find(mImageInfo, dedicatedAllocInfoPtr->image);
if (dlInfo && dlInfo->deferredLayoutAhb) {
deferredAhbHold = dlInfo->deferredLayoutAhb;
importDeferredLayoutAhb.buffer = deferredAhbHold.get();
Decode it with emit_global_state_wrapped_decoding, as the other image
entry points already are, so the host can answer it. The implementation
added here just calls through to the driver, so behaviour is unchanged.
The generator side is a separate change in Mesa:
gitlab.freedesktop.org/mesa/mesa/-/merge_requests/43969
vk_decoder.cpp carries only the delta that change produces. A plain
regeneration also rewrites ~87 unrelated lines across three generated
files, because the checked-in output has drifted from the generator;
that is left alone here.
Test: regenerated with and without MR 43969 -- the only difference is
the dispatch below, and the other generated files are identical
Intel ANV leaves an AHB-external image's layout unresolved until
vkBindImageMemory and reports size=0 until then. That is spec-valid, but
guests take it literally: Mesa zink asks for a zero-sized allocation,
the host rejects it with VK_ERROR_OUT_OF_HOST_MEMORY, and the compositor
dies on the NULL.
The image is AHB-external because gfxstream makes it so -- a Linux guest
asks for DMA_BUF and transformImpl_VkImageCreateInfo_tohost ORs in the
host default, which is what lets the image alias an AHB-backed
ColorBuffer. So the requirements have to be answered, not avoided.
Allocate the AHB up front for an image the driver refuses to describe,
take the size from vkGetAndroidHardwareBufferPropertiesANDROID, and
import the same AHB on the guest's dedicated allocation so the layout
resolves on bind.
The substitution runs in updateImageMemoryRequirementsLocked, so it
happens before transformToGuestMemoryRequirements -- necessary because
ahbProps.memoryTypeBits is in host indices and that transform maps them
to guest indices.
Bug: 545345381
Test: Intel PTL host -- Weston reaches its GL renderer and the host
substitutes size=3768320 for the 1280x720 scanout image
Test: glmark2-wayland runs accelerated on zink/Intel PTL
c2c228e to
e181255
Compare
|
Thanks — all addressed, now two commits.
Retested on Intel PTL: Weston on its GL renderer, substitution and stride both applied, no One note on (1): |
gurchetansingh
left a comment
There was a problem hiding this comment.
Looks better, few questions though
| } | ||
| } | ||
|
|
||
| // An AHB-backed image does not need to be CPU-mappable, and it |
There was a problem hiding this comment.
AFAICT, memoryTypeBits is assigned directly from ahbProps.memoryTypeBits without stripping HOST_VISIBLE bits?
On Intel ANV (which uses unified memory where types often report HOST_VISIBLE), the guest may
pick a host-visible memory type, causing crosvm resource_map_blob() to fail and mmap64 to return EINVAL—the exact failure mode described in the comment.
Or am I missing something?
| // The driver only resolves the layout if the bound memory carries an AHB, so import the | ||
| // image's AHB on its dedicated allocation. Function scope: vk_append_struct() only stores | ||
| // a pointer and the chain is consumed at vkAllocateMemory below. | ||
| VkImportAndroidHardwareBufferInfoANDROID importDeferredLayoutAhb = { |
There was a problem hiding this comment.
can you add logic to make sure the importDeferredLayoutAhb never conflicts from the import add by importCbInfoPtr: I.e, we don't have duplication AHB extension struts ever.
Two gaps @gurchetansingh caught in the AHB-layout deferral: the substituted memoryTypeBits copied ahbProps.memoryTypeBits verbatim, so on a host where the AHB reports HOST_VISIBLE types (Intel ANV's unified memory typically does), the guest could pick one -- the exact case the comment above on_vkGetImageMemoryRequirements says is disallowed, but nothing enforced it. Separately, vkAllocateMemory could append two VkImportAndroidHardwareBufferInfoANDROID structs to the same pNext chain: one from the deferred-layout import, one from an existing VkImportColorBufferGOOGLE import, whenever a dedicated allocation carried both. Mask deferredLayout.memoryTypeBits down to the host's non-host-visible memory types before handing it back, falling back to the full mask only if the AHB has no such type. Skip the deferred-layout import when a ColorBuffer import is already present on the same allocation -- that path supplies its own. Bug: 545345381 Test: fatcat -- Weston GL renderer, desktop and app windows composite correctly, no GFXSTREAM errors
|
Both real, thanks for catching these — pushed as a third commit. memoryTypeBits: confirmed — Duplicate AHB import: also confirmed reachable — Retested on Intel PTL/fatcat: Weston GL renderer, desktop and app windows composite correctly, no GFXSTREAM errors. |
| // below, by which point the image may have been destroyed. | ||
| std::shared_ptr<AHardwareBuffer> deferredAhbHold; | ||
| if (dedicatedAllocInfoPtr && dedicatedAllocInfoPtr->image != VK_NULL_HANDLE) { | ||
| std::lock_guard<std::mutex> dlLock(mMutex); |
There was a problem hiding this comment.
Thinking about this more, I think we can possibly eliminate this entire block on on_VkAllocateMemory all-together.
The reason is because the guest has:
bufferBlob = instance->createBlob(createBlob);
if (!bufferBlob) return VK_ERROR_OUT_OF_DEVICE_MEMORY;
and
if (bufferBlob) {
if (hasDedicatedBuffer) {
importBufferInfo.buffer = bufferBlob->getResourceHandle();
vk_append_struct(&structChainIter, &importBufferInfo);
} else {
importCbInfo.colorBuffer = bufferBlob->getResourceHandle();
vk_append_struct(&structChainIter, &importCbInfo);
}
}
so importCbInfo is always appended when using an AHB. Therefore, I think we should move ownership of the AHB. So rather than AHardwareBuffer* allocAhb(const VkImageCreateInfo* imageCreateInfo) exposed to the vkDecobder, it should be std::optional<AhbInfo> VkEmulation::allocate_ahb(const VkImageCreateInfo):
struct AhbInfo {
VkDeviceSize size = 0;
VkDeviceSize alignment = 0;
uint32_t memoryTypeBits = 0;
VkDeviceSize rowPitch = 0;
};
You would save the Ahb itself inside the VkEmulation, and then move when the createBlob requests. Essentially, you would have a table of pending Ahb, and if their properties match the create blob request the move occurs.
Right now, I think we might be double allocating? The deferred layout AHB and the createBlob AHB.
There was a problem hiding this comment.
You were right double allocating happened — I don't think the literal "match a pending AHB at createBlob time" version works, for a specific reason:
The issue: VkImportColorBufferGOOGLE only ever appears in VkMemoryAllocateInfo (at vkAllocateMemory) — it's never present in VkImageCreateInfo (at vkCreateImage), so there's no signal at image-creation time that a ColorBuffer import is coming. And on the other side, on_vkAllocateMemory's ColorBuffer handling only ever looks up an existing ColorBuffer by handle (getColorBufferAllocationInfo) — it never creates one. VkEmulation::createVkColorBufferLocked creates the ColorBuffer's own image + AHB independently, and normally before the guest ever calls vkCreateImage for the image that will later import it (confirmed this is consistent with the existing AddPendingBlob/TakePendingBlob mechanism in virtio_gpu_context.cpp, which correlates CREATE_3D metadata with a later CREATE_BLOB call — a similar but distinct pending mechanism, at the virtio-gpu resource level rather than the Vulkan level).
So by the time the deferred-layout probe fires in on_vkCreateImage, a ColorBuffer that will later be imported already exists with its own AHB — there's no "pending" probe AHB from an earlier point in time that a later ColorBuffer creation could adopt. Matching them at createBlob time as described would mean handing the same physical AHB to two unrelated images, which risks aliasing their memory.
What I would do instead: moved the probe into VkEmulation::getDeferredLayoutProbe(), memoized by AHB shape (width/height/format/usage) for the life of VkEmulation. It never keeps the AHB around — queries vkGetAndroidHardwareBufferPropertiesANDROID once per distinct shape and releases it immediately, caching only the 4 scalars (size/alignment/memoryTypeBits/rowPitch). on_vkAllocateMemory now allocates a real, non-shared AHB lazily, only when actually needed as backing memory (no ColorBuffer import present) — same cost as before for that case, but after the first image of a given shape, every other image sharing that shape costs zero extra AHardwareBuffer_allocate() calls for its probe. That should cover the common case you flagged (e.g. every same-sized window surface) without the aliasing risk.
Please let me know if that address your concern so I can send another commit for review
There was a problem hiding this comment.
So by the time the deferred-layout probe fires in on_vkCreateImage, a ColorBuffer that will later be imported already exists with its own AHB — there's no "pending" probe AHB from an earlier point in time that a later ColorBuffer creation could adopt.
That does not match my mental model of how it's supposed to work. The flow I have in mind is:
- Guest requests
vkCreateImagewithVkExternalMemoryImageCreateInfoand the dmabuf handle type. That is translatedvkCreateImageto External Memory + AHB on the host. This is where we want to put the probe. - After getting the image size, the guest calls CreateBlob with the following path:
if (hasDedicatedImage) {
VkImageCreateInfo imageCreateInfo;
{
std::lock_guard<std::recursive_mutex> lock(mLock);
auto it = info_VkImage.find(dedicatedAllocInfoPtr->image);
if (it == info_VkImage.end()) return VK_ERROR_INITIALIZATION_FAILED;
const auto& imageInfo = it->second;
imageCreateInfo = imageInfo.createInfo;
}
// Need to query the stride of the underyling image resource
// (VkSubresourceLayout::rowPitch) In most cases, the application will have created the
// VkImage w/ VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, in which case the aspectMask to
// query is the PLANE_0_BIT resource. Otherwise, query the more generic COLOR_BIT.
// Note: For VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, the image may actually be emulated
// with VK_IMAGE_TILING_LINEAR.
const VkImageSubresource imageSubresource = {
.aspectMask = (imageCreateInfo.tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT)
? VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT
: VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.arrayLayer = 0,
};
VkSubresourceLayout subResourceLayout;
enc->vkGetImageSubresourceLayout(device, dedicatedAllocInfoPtr->image,
&imageSubresource, &subResourceLayout,
true /* do lock */);
But if you don't see DedicatedImageCreateInfo, I suppose we can miss that path entirely? Let's try to confirm the path taken before deciding on what to do.
| pMemoryRequirements->alignment = imageInfo->deferredLayout.alignment; | ||
| pMemoryRequirements->memoryTypeBits = imageInfo->deferredLayout.memoryTypeBits; | ||
|
|
||
| uint32_t typeBits = imageInfo->deferredLayout.memoryTypeBits; |
There was a problem hiding this comment.
Looking further, I was thinking we don't need to worry about updating the memory types anymore. The reason is when creating color buffers, kBlobFlagMappable is not set, but kBlobFlagShareable | kBlobFlagCrossDevice is.
Since your prior commit did not mutate the memory types and it worked, there a good chance we get lucky again and can avoid the complexity for now (we'll probably aim for a more robust solution long-term, but that might require some new Android graphics APIs).
Second of the pieces @gurchetansingh asked for on the ARSP review — the hand-coded
half. The regenerated
vk_decoder.cppand the mappable-only-blob change follow asseparate PRs.
What goes wrong
Intel ANV leaves an AHB-external image's layout unresolved until
vkBindImageMemoryand reports
size=0, alignment=0until then. That is spec-valid, but guests take itliterally:
The guest allocates and maps host-visible memory in one step inside
vkAllocateMemory(ResourceTracker::getCoherentMemory), so a zero-sized requestfails at the host allocation with
VK_ERROR_OUT_OF_HOST_MEMORY— the-1above —before anything is mapped.
Why the image is AHB-external at all
Not the guest's doing. A Linux guest asks for
DMA_BUF_BIT_EXT, andtransformImpl_VkImageCreateInfo_tohostORs in the host's default handle type, whichis AHB on an Android host. That is deliberate — it is what lets the guest's
window-system image alias an AHB-backed ColorBuffer — so the requirements have to be
answered rather than the handle type avoided.
I did try the other direction (dmabuf end to end via
VulkanExternalMemoryMode:OpaqueFd)and it does remove this failure, but scanout then breaks: the Android display path needs
AHB for zero-copy, and the composited frame never reaches the surface. So this seemed
like the right place to fix it.
Approach
Allocate the AHB up front for an image the driver refuses to describe, take the real
size from
vkGetAndroidHardwareBufferPropertiesANDROID, substitute it, and import thesame AHB on the guest's dedicated allocation so the driver resolves the layout on bind.
Placement is load-bearing: after
updateImageMemorySizeLocked(which rewrites thestruct for compressed images) and before
transformToGuestMemoryRequirements, becauseahbProps.memoryTypeBitsis in host indices and that transform is what maps them toguest indices.
Note on scope
on_vkGetImageSubresourceLayoutis added here but is not reached until the decoderroutes that call through
VkDecoderGlobalState— that isMesa MR 43969 plus the
regenerated
vk_decoder.cpp, which I will send once the Mesa side lands.Testing
Intel PTL Android host: Weston reaches
GL renderer: zink Vulkan 1.4 (Intel(R) Graphics (PTL)), the host substitutessize=3768320for the 1280x720 scanout image, andglmark2-waylandruns accelerated on zink/Intel PTL.