Repository navigation
Vulkan Interop
隔离 Maven consumer 中的 PublishedVulkanInteropConsumer
是随发布门禁编译的完整控制流样例:它演示 extension discovery、有界等待、非空结果、lease 的
ACTIVE -> RELEASED -> CLOSED 顺序,以及只按 RendererDeviceException.RecoveryAction 决策的恢复路径。应用只需在
NativeFrameConsumer 内实现自身 Vulkan device/queue 的 import、ownership transfer 与 submission;不得把这些 native
职责隐藏回普通 RayTracingRenderer 路径。
VulkanFrameInterop 是显式 opt-in 的零拷贝外部图像接口。它只适合已经能正确实现 Vulkan external memory、external
semaphore、queue-family ownership transfer 和 Win32 handle 所有权的调用方。普通应用应使用
RayTracingRenderer.pollLatestCpuFrame()/有界等待,或使用官方 VulkanFramePresenter 完成 GPU 窗口显示。
两条 GPU 路径使用同一 lease 契约,但所有权层级不同:
| 路径 | 调用方负责 | API/provider 负责 |
|---|---|---|
VulkanFramePresenter |
创建配置、pump event、提交帧、调用 presentLatestFrame
|
同设备 managed timeline fast path、swapchain copy/present、HUD、completion、lease close;必要时 external fallback |
VulkanFrameInterop |
全部 consumer Vulkan import、queue ownership、同步、submission、completion 与 lease close | producer image/handle export 与契约验证 |
官方 presenter 通过 VulkanFramePresenter.open(renderer, config) 打开。其全部方法绑定创建线程;同一 renderer 同时只允许一个官方 presenter。简单模式调用 presentLatestFrame(),由 provider 获取可用 internal managed lease;该路径复用 renderer 的 VkInstance、VkDevice 与受控 queue,并用 GPU timeline 表达 producer-ready 依赖,不导出 Win32 memory handle。VulkanFrameInterop.pollLatestFrame() 是独立专家入口,继续只返回 CPU 已观察完成的 external lease。两种消费模式在同一 renderer session 内互斥:存在 expert lease 时不能打开 presenter,presenter 打开期间也不能 poll expert lease。maximumFramesQueuedAhead 限制 producer lead,但不按时间锁 FPS。
PresentMode.UNCAPPED 依次偏好 immediate、mailbox、FIFO;LOW_LATENCY 偏好 mailbox;VSYNC 使用 FIFO。平台可以 fallback,唯一权威结果是 activePresentMode()。PresentationResult 的 PRESENTED 才能计入实际 present FPS;minimized 和 swapchain recreate 结果会安全退休 lease,但不是可见帧。同一 VkSwapchainKHR 的 acquire/present host access 必须串行;不得用跨线程竞争伪造吞吐。performanceSnapshot() 用于区分 acquire、GPU copy、queue lock 与 native present;setOverlayText(...) 可把诊断值直接画入 swapchain。
FG/MFG 启用时,官方 presenter 通过 Streamline 官方 Vulkan WSI proxy 函数创建、枚举、acquire、present 和销毁 swapchain;
没有第二套可由应用直接调用的 swapchain owner。proxy 调用仍受 Vulkan host synchronization 约束,全部 acquire/present
操作保持在 presenter owner 线程串行执行。VK_SUBOPTIMAL_KHR 和 VK_ERROR_OUT_OF_DATE_KHR 进入有界的
retire/recreate 路径,不把正常 WSI 重建永久解释为功能熔断。
每个 native frame 使用同一个严格递增 sequence 获取 Streamline frame token。HUD-less color、depth 和 motion vectors 以
VK_IMAGE_LAYOUT_GENERAL 及真实 extent/format/usage 提交 eValidUntilPresent tag;只有 exact token、backbuffer extent 和 tagged
资源合同全部匹配时才请求生成,否则该帧确定性回退为 native present 并增加 request-miss 诊断。presenter 的 copy 目标在
调用 native 或 proxy vkQueuePresentKHR 前必须从 TRANSFER_DST_OPTIMAL 回到 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR。
同步和资源回收遵循以下边界:
- presenter 等待 swapchain acquire binary semaphore 和 producer-ready timeline,再提交 copy,并 signal present semaphore。
- proxy present 消费该 present semaphore;同一 frame context 的 retirement fence 证明 presenter/Streamline 对 tagged 输入的访问已经完成。
- fence 完成后,以原 frame token 为 HUD-less color、depth 和 motion vectors 提交 null tag;null tag 成功前不得释放或复用其 image/view。
- tag retirement、managed lease release/close 和 host backlog 通知是一个可重试事务。任一步失败都保留 pending lease,不能提前把 slot 归还 producer。
- minimized、skipped、fallback、swapchain recreate 和 presenter close 使用同一 retirement 事务;post-present diagnostics 失败不能跳过清理。
swapchain recreate/close 先把 DLSS-G 配置为 off,再释放全部剩余 tag。配置 off 失败时保留原 proxy swapchain 供重试;tag 释放
失败时也禁止销毁仍被 Streamline 引用的图像。完成这些资源引用义务后,关闭路径等待 device idle 覆盖 proxy 的异步工作,随后
销毁 proxy swapchain,最后才销毁 frame-context fence/semaphore、feature session 和 Vulkan device。该顺序是 provider 内部合同,
应用不得通过直接销毁 window、swapchain 或 renderer 绕过 VulkanFramePresenter.close()。
VulkanFrameInterop interop = renderer.extension(VulkanFrameInterop.class)
.orElseThrow(() -> new IllegalStateException("Vulkan interop unavailable"));扩展缺失表示当前 provider 不支持该契约。调用方不得通过强制类型转换、反射或假定 backend 实现来绕过能力发现。
| 方法 | 返回 | 说明 |
|---|---|---|
pollLatestFrame() |
FrameAvailable 或 FrameNotReady.INSTANCE
|
非阻塞、非 null、穷尽结果 |
awaitLatestFrame(Duration) |
同上 | 有界等待,超时返回 FrameNotReady.INSTANCE
|
awaitLatestFrameAsync(Duration, Executor) |
CompletableFuture<FramePollResult> |
使用调用方拥有的 executor |
FrameAvailable.lease() 转移一个新 lease 的独占 consumer 所有权。每个成功获取的 lease 都必须完成必要同步、发布
completion,并关闭。
GpuFrameLease 提供:
-
descriptor():帧 sequence、scene revision、extent、Vulkan format/type/tiling/usage/create flags/layout、mip/layer/sample、sharing mode、producer queue family、memoryTypeIndex、allocation size/offset 和 dedicated allocation;resourceId在同一底层 image 生命周期内稳定。 -
memoryHandle():导出内存 Win32 handle 的有状态 owner,包含强类型 memory handle type、import disposition 和 handle state。 -
acquireSignal():可选 producer completion semaphore;为空表示 producer completion 已由 CPU 观察。 -
consumerCompletionCapabilities():该 concrete lease 实际接受的 completion 机制。 -
state():权威ACTIVE、RELEASED、CLOSED状态。
所有 Vulkan 数值都使用不同的强类型包装。不要把 memory handle type、semaphore handle type、format、layout、queue family 或普通
int 相互替代。
resourceId 不是 frame sequence。consumer 可以用它缓存同一个 external image 的 import,但每次 lease 仍拥有独立的 acquire/completion 生命周期。同一 resourceId 若对应不同的 extent、format、tiling、usage、layout、memory type 或 allocation metadata,必须拒绝为协议错误。导入前还必须验证 image memory requirements 的 memoryTypeBits 包含 descriptor 的 memoryTypeIndex。
一个完整 lease 必须按以下顺序处理:
- 读取
FrameDescriptor,创建与 descriptor 完全一致的 consumer-side Vulkan image/import 配置。 - 导入
memoryHandle().value()。只有 Vulkan import 成功后才能调用memoryHandle().markImported()。 - 如果有
acquireSignal(),导入其 semaphore handle;只有 import 成功后才能调用该 handle owner 的markImported()。 - 提交 consumer GPU 工作:等待 acquire signal,并在需要时从
VK_QUEUE_FAMILY_EXTERNAL获取 image ownership。 - 等待或生成该 lease 明确支持的 consumer completion;若转移过 queue-family ownership,在 completion 前释放回
VK_QUEUE_FAMILY_EXTERNAL。 - 调用
lease.release(completion)。成功后状态从ACTIVE变为RELEASED。 - 调用
lease.close()关闭 consumer-side lease ownership 和 exporter 仍拥有的 handle。成功后状态为CLOSED。
这些 mutation 必须由调用方串行:handle import 必须先于 release(...)/close() 完成,release(...) 与 close() 也不能并发。
ExportedNativeHandle 状态是:
EXPORTED -> IMPORTED
EXPORTED -> CLOSED
IMPORTED -> CLOSED
规则:
- import 失败:不要调用
markImported();关闭 handle owner,让 exporter 关闭仍归它所有的 Win32 handle。 - import 成功:立即调用一次
markImported();它根据ImportDisposition应用“import 消耗 handle”或“caller 继续拥有 handle”的规则。 -
markImported()只有第一次成功转换返回true;不要用重复调用掩盖控制流错误。 - 不要直接关闭
value()表示的 OS handle 后再让 owner 关闭;所有权必须始终由 stateful owner 记录。
acquireSignal() 返回 Optional<AcquireSignal>:
| 字段 | 语义 |
|---|---|
handle() |
导出 semaphore handle owner |
kind() |
BINARY 或 TIMELINE
|
timelineValue() |
binary 必须为 0;timeline 必须为正值 |
消费者必须在访问共享 image 前满足该 signal。Optional.empty() 只表示 producer completion 已在 CPU 侧观察,不取消 image
layout、memory visibility 或 queue-family 规则。
先读取 consumerCompletionCapabilities(),再选择 completion:
| completion | 使用条件 | 调用方保证 |
|---|---|---|
new GpuFrameLease.CpuCompleted() |
cpuCompleted() == true |
CPU 已确认 consumer GPU 使用完成,且必要的 external queue release 已完成 |
ExternalSemaphoreSignal |
supports(kind) == true |
提供已由 consumer signal 的可导入 external semaphore,字段和所有权规则完整匹配 |
当前 Windows Vulkan backend 会逐 lease 协商能力;调用方不能因为公共 API 定义了某种 completion 就假定 backend 接受它。不支持的
completion 会抛 UnsupportedOperationException,不会静默等待或退化。
ExternalSemaphoreSignal 的 handle 仍由调用方按 importDisposition 收尾:release(...) 失败时调用方继续拥有
handle;成功且为 CALLER_RETAINS_HANDLE 时调用方必须关闭 handle;成功且为 IMPORT_CONSUMES_HANDLE 时不得再次关闭。当前
Windows backend 只在 capability 协商通过后接受 binary OPAQUE_WIN32 completion,并要求 CALLER_RETAINS_HANDLE。
lease 状态机是:
ACTIVE -> RELEASED -> CLOSED
-
release(...)失败时保持ACTIVE,调用方仍拥有 completion/handle,可在修复瞬时错误后重试原操作。 - native handle cleanup 失败时保持
RELEASED,调用方可重试close()。 - imported 且仍为
ACTIVE的 lease 不能直接关闭;renderer 不会猜测 consumer GPU 已完成。 - 关闭 renderer 前必须归还全部 lease。
RendererHealth.obligations().outstandingGpuFrameLeases()可观测未归还数量。
renderer 创建时选择 FrameOutputFormat:
| 配置 | Descriptor format 语义 | Resolve 语义 |
|---|---|---|
SDR_RGBA8 |
display-ready RGBA8 | 每样本 tone-map/linear-to-sRGB 后 resolve |
LINEAR_HDR_RGBA16F |
linear scene-referred RGBA16F | 线性 radiance resolve,不 tone-map |
后端在 probe/open 阶段验证对应 storage image 与 Win32 external-memory export 能力;不支持时拒绝配置,不会改成另一种格式。不要根据设备名称推断
capability,使用 RayTracingGpuDevice.capabilities() 和实际 open 结果。
- 所有成功 import 都已
markImported()。 - 所有失败 import 的 exporter-owned handle 都已关闭。
- consumer GPU 工作已等待 acquire signal。
- 必要的 queue-family ownership 已释放回 external。
- completion 类型已通过当前 lease capability 协商。
- lease 已成功从
ACTIVE到RELEASED再到CLOSED。 - renderer 关闭前
outstandingGpuFrameLeases == 0。
普通 uncapped 提交循环应使用 renderer.trySubmit(...)。FrameSubmissionDeferred 保证该次没有推进 frame sequence 或 native submission;调用方保留同一 request 稍后重试,并让出 CPU。官方 presenter 的默认 producer lead 是 2,允许 trace/present 重叠但不允许生产端无限堆积不可见工作。