WindowsExternalBrowserSurface.kt
src/jvmMain/kotlin/afield/media/browser/WindowsExternalBrowserSurface.kt · 85 lines
Windows import path for Electron browser frames: wraps a D3D11 shared texture, passed as an NT handle, in a Vulkan image that Field can copy from.
WindowsExternalBrowserSurface imports each frame that carries a handle through
VK_KHR_external_memory_win32, closes the NT handle after the import, replaces that slot's earlier
image, and retires every slot when the stream id changes. It requires Windows and a device that supports that extension. It has
been compiled but not yet run on Windows.
package afield.media.browser
import afield.graphics.vulkan.Renderer
import afield.graphics.vulkan.util.OS
import afield.graphics.vulkan.util.OSType
import afield.app.diagnostics.logging.lprintln
import org.lwjgl.vulkan.VK10.VK_IMAGE_LAYOUT_GENERAL
import org.lwjgl.vulkan.VK11.VK_QUEUE_FAMILY_EXTERNAL
class WindowsExternalBrowserSurface
Windows receiver for Electron D3D11 shared textures. The producer must duplicate Electron's process-local NT handle into Field before constructing ExternalBrowserImageHandle.WindowsD3D11NtHandle.
This implementation is compile-reviewed from macOS and keeps the shape of the src/personal
prototype. It still needs a Windows Vulkan validation/readback smoke before it should be called
runtime-proven.
class WindowsExternalBrowserSurface(
renderer: Renderer,
spec: ExternalBrowserSurfaceSpec,
releaseListener: BrowserFrameReleaseListener = BrowserFrameReleaseListener { }
) : CopiedExternalBrowserSurface(renderer, spec, releaseListener) {
private val importedSlots = mutableMapOf<Int, ImportedImage>()
private var importedStreamId: Long? = null
init
init {
require(OS == OSType.WINDOWS) { "WindowsExternalBrowserSurface requires Windows" }
require(renderer.device.supportsExternalMemoryWin32) {
"WindowsExternalBrowserSurface requires VK_KHR_external_memory_win32"
}
}
override fun importFrame(frame: ExternalBrowserFrame): ImportedImage?
override fun importFrame(frame: ExternalBrowserFrame): ImportedImage? {
if (frame.streamId != importedStreamId) {
importedSlots.values.forEach(::retire)
importedSlots.clear()
importedStreamId = frame.streamId
}
if (frame.handle == null) return importedSlots[frame.slot]
val handle = frame.handle as? ExternalBrowserImageHandle.WindowsD3D11NtHandle
?: return null
val native = try {
ExternalBrowserVulkanImports.d3d11Texture(
renderer.device,
handle.value,
spec.width,
spec.height,
importVkFormat
)
} finally {
// Win32 imports retain the underlying allocation but do not consume the HANDLE.
runCatching { handle.close() }
.onFailure {
lprintln("warning,graphics", "could not close imported browser NT handle: ${it.message}")
}
}
val imported = ImportedImage(
frame.frameId,
native.image,
native::cleanup,
layout = VK_IMAGE_LAYOUT_GENERAL,
externalQueueFamily = VK_QUEUE_FAMILY_EXTERNAL
)
importedSlots.put(frame.slot, imported)?.let(::retire)
return imported
}
override fun cleanupImports()
override fun cleanupImports() {
importedSlots.values.forEach { it.cleanup() }
importedSlots.clear()
}
}