mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
5f8e8db1b96d1663bfe67155efb5a0c0c370b6dc
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7ef7c7e543 |
[Fix] (Spikes): answer the tier question for DirectGLES too, make an OK mean bytes round-tripped through a real GPU access, and exercise T3 in the direction that makes it a tier
- plan-B §8.3 asks which tier `AcquirePersistentMap` lands in, but the probe only
asked Vulkan. DirectGLES ("Espryt") reaches a persistent map through
`glBufferStorageEXT` + `glMapBufferRange(PERSISTENT|COHERENT)`, not a
`VkDeviceMemory` map, so a Vulkan-only answer decides nothing for that backend.
Add a GLES leg to T1: the exported fd imported with `glCreateMemoryObjectsEXT`
+ `glImportMemoryFdEXT` + `glBufferStorageMemEXT`, then mapped
PERSISTENT|COHERENT -- in-process first (isolates "GL can import this fd" from
"the fd survives a process boundary"), then cross-process (new `t1gl` child).
Drivers disagree about how the import must be phrased, so each attempt walks a
ladder over {dedicated flag} x {import size = memory requirement or the fd's
own size} x {buffer size} and reports the rung the driver accepted plus every
rejected rung with its GL error -- a driver *preference* must never be reported
as a missing capability. A driver that backs the storage but refuses
PERSISTENT|COHERENT is reported separately from one that refuses the storage:
that distinction is exactly T1 vs T2 for DirectGLES. The T0 GLES leg
(`EGL_ANDROID_get_native_client_buffer` + `glBufferStorageExternalEXT` +
persistent map, verified by `AHardwareBuffer_lock` on the client side) now
reports every step's GL enum and requires the persistent flags for OK.
- the verdict was unfalsifiable: T1 reported PARTIAL when neither leg had moved a
byte. Replace it with an explicit decisive-leg model -- OK only when every
decisive leg round-tripped in both directions, PARTIAL when at least one did,
FAIL otherwise with the failing step and its driver error named in `why:`.
Every row now opens with a per-leg trace (`vkimport[D]=rt gpu[D]=rt`). The raw
`mmap` leg is informational for opaque-fd (Vulkan forbids interpreting that
payload outside the driver, so a refusal is conformant) and decisive for
dma-buf, where a CPU mapping is the point of the handle type.
- T3 never ran the direction that would make it a tier: both ends were the
importing process. Add `T3-client-memfd-server-import` (new `t3c` child) -- the
client creates and writes the memfd, the server mmaps the received fd, imports
the client's host pointer into a `VkDeviceMemory`, reads what the client wrote,
writes back, and takes a GPU access on the client's memory, which the client
then verifies through its own mapping.
- no route touched the GPU, so an OK proved only that a map call returned a
pointer. Every tier row now takes a real GPU access before it can be OK:
`vkCmdCopyBuffer` out of the shared allocation into private staging (mismatch =
the GPU could not read what the peer wrote) plus `vkCmdFillBuffer` into it,
queue-idle and an explicit host-read barrier, with the peer checking the filled
region through its own mapping. VkCtx grows a queue and command pool for it.
- the device run executes in the `shell` SELinux domain, not the `untrusted_app`
domain MobileGL runs in, and the two do not share dmabuf/gralloc rules. Print
uid/pid/`/proc/self/attr/current` in a run-context header, repeat the caveat in
the summary, and document in README.md how to answer it for the real domain
later (exec the same binary from the trace app's spike hook, spike-A package)
without implementing that here.
- `vkStr()` returned a pointer into one static buffer while several results
routinely appear in one format call, so all of them showed the last one; it
returns std::string now, `memFlagStr` likewise, and `fmt`/`pr` carry
`format(printf)` so a missed `.c_str()` is a compile error rather than UB.
- `advertisedExportable` decided the status at the allocate site but not at the
`vkGetMemoryFdKHR` site. One rule at every export failure now
(`exportFailStatus`): advertised EXPORTABLE and then declining is FAIL, never
advertised is UNSUPPORTED. Export + map + fd is factored into `exportHostVisible`.
- `T0-ahb-blob-transfer` was recorded OK on the socket handoff alone. The handoff
keeps its own informational row; the tier row is now composed at the end from
the full import+map+compare+writeback chain over the Vulkan, GL and GPU legs.
- `mmapErrno` kept the first attempt's errno after the second-chance mmap
succeeded, so a working mapping carried a failure code; it is cleared on
success and the first errno moves into the note.
- a failed `glImportMemoryFdEXT` no longer closes the fd: EXT_memory_object_fd
does not say whether ownership still transfers on failure and Mesa closes it
either way, so closing risks a double close landing on the socket. Leaking a
handful of dups in a short-lived probe is the safe side of that trade.
- validated end to end on the host harness (lavapipe + llvmpipe,
`VK_DRIVER_FILES=lvp_icd.json EGL_PLATFORM=surfaceless`): T1-opaque-fd OK,
T3-external-memory-host OK, T3-memfd-cross-process OK,
T3-client-memfd-server-import OK, T1-dma-buf UNSUPPORTED (not advertised
exportable). The two T1-gles rows FAIL there with GL_OUT_OF_MEMORY on every
ladder rung although GL_DEVICE_UUID_EXT matches the Vulkan deviceUUID --
llvmpipe's GL does not implement importing a lavapipe opaque-fd allocation,
a Mesa interop gap recorded in README.md so a device FAIL stays attributable.
Rebuilt for arm64-v8a with NDK r27d (PIE, android-30); the device run is
pending, both device locks are held by another campaign.
|
||
|
|
6c7ad0a1bf |
[Feat] (Spikes): add the standalone external-memory probe that decides the persistent-map tier
- Plan B §11 P0 requires spike B ("external memory 导出,两台设备") to run before the
persistent-map decision of §8.3 can be taken: T0 (server imports a client
allocation), T1 (server exports its own HOST_VISIBLE|HOST_COHERENT allocation)
or T2 (AcquirePersistentMap returns nullptr, making the §5.10 client-side block
push mandatory). §8.3 says the answer must be measured on the two campaign
devices, and that a platform unknown must not block the interface work.
- tools/spikes/extmem_probe/ is a self-contained NDK command-line program: it
links vulkan/EGL/GLESv3/android/log and nothing from MobileGL, is configured by
its own CMakeLists with the android toolchain file, and is deliberately absent
from the project's build graph (the root CMakeLists only pulls in
tools/trace_replay), so the default ALL target is untouched.
- Phase A enumerates VK_KHR_external_memory_fd, VK_EXT_external_memory_dma_buf,
VK_EXT_external_memory_host, VK_ANDROID_external_memory_android_hardware_buffer
and, through a headless EGL pbuffer context, GL_EXT_memory_object{,_fd},
GL_EXT_external_buffer, GL_EXT_buffer_storage, GL_OES_EGL_image_external{,_essl3}
and EGL_ANDROID_get_native_client_buffer, plus the memory-type table and the
vkGetPhysicalDeviceExternalBufferProperties verdict per handle type for the exact
buffer usage set MobileGL needs.
- Route T1 allocates a HOST_VISIBLE|HOST_COHERENT buffer memory with
VkExportMemoryAllocateInfo, writes a pattern through vkMapMemory, exports an fd
with vkGetMemoryFdKHR (opaque-fd and, where advertised, dma-buf), hands it to a
second process over SCM_RIGHTS, and has that process both mmap() the fd and
import it into its own VkDeviceMemory + vkMapMemory. Both sides write and both
sides compare, so a copy-on-import or one-directional mapping is reported as
PARTIAL rather than as success.
- Route T0 has the second process allocate an AHardwareBuffer BLOB
(CPU_READ_OFTEN|CPU_WRITE_OFTEN|GPU_DATA_BUFFER), send it with
AHardwareBuffer_sendHandleToUnixSocket, and the first process import it three
ways -- AHardwareBuffer_lock, VkDeviceMemory via
VK_ANDROID_external_memory_android_hardware_buffer, and a GL buffer via
eglGetNativeClientBufferANDROID + glBufferStorageExternalEXT mapped
persistent/coherent -- with a write-back leg the allocating process verifies.
- Route T3 covers VK_EXT_external_memory_host: a memfd-backed mmap region aligned
to minImportedHostPointerAlignment, imported through
VkImportMemoryHostPointerInfoEXT, plus the same memfd handed to another process.
- The second process is /proc/self/exe re-exec'd with --child=<route> and one end
of a socketpair on fd 3. A bare fork() is not usable: neither side's Vulkan
driver survives fork, and both sides need live Vulkan. It is also the topology
the transport actually has (§8.1, inheriting PLAN.md §11.1-§11.6: the client
spawns the server), so the probe measures the arrangement that would ship.
- The probe also builds for the host with T0 compiled out. That is not scope
creep: a negative device result is only worth something if the harness is known
to report a working route as working. Running it on lavapipe did that, and paid
for itself immediately by exposing two harness bugs that would have produced
false negatives on the devices -- (a) the child wrote through its plain mmap
before reading through the Vulkan import, so on a driver whose exported fd maps
at an offset the probe overwrote the very payload the second read compares
(lavapipe reports payloadAt=4096); reads through both mappings now precede
writes through either, and the offset is searched for and reported; (b) an
export failure on a handle type the driver never advertised as EXPORTABLE was
classified FAIL instead of UNSUPPORTED (lavapipe's dma-buf answer).
- Output is a RESULT/summary table carrying the raw driver verdicts (VkResult
names, errno, GL enums) because those codes -- not a pass/fail bit -- are what
§8.3 needs in order to pick the tier.
- Built with NDK 27.3.13750724 for arm64-v8a / android-30, RelWithDebInfo, PIE,
warning-clean; host build clang/RelWithDebInfo, warning-clean.
|