Skip to content

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()
    }
}