LinuxExternalBrowserSurface.kt
src/jvmMain/kotlin/afield/media/browser/LinuxExternalBrowserSurface.kt · 75 lines
package afield.media.browser
import afield.graphics.vulkan.Renderer
import afield.graphics.vulkan.util.OS
import afield.graphics.vulkan.util.OSType
import org.lwjgl.vulkan.EXTQueueFamilyForeign.VK_QUEUE_FAMILY_FOREIGN_EXT
import org.lwjgl.vulkan.VK10.VK_IMAGE_LAYOUT_GENERAL
The Linux browser-frame receiver, which imports Electron's dma-buf frames into Vulkan.
class LinuxExternalBrowserSurface
Linux receiver for Electron nativePixmap dma-bufs. The producer must transfer every fd with
SCM_RIGHTS; copying the integer values from Electron's process is not a transfer.
The first implementation supports one dma-buf allocation with one or more DRM layout planes, which covers the accelerated RGB/BGRA browser surfaces this API negotiates. Distinct fds per plane fail explicitly until disjoint multi-allocation binding is implemented and tested on Linux.
class LinuxExternalBrowserSurface(
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.LINUX) { "LinuxExternalBrowserSurface requires Linux" }
require(renderer.device.supportsDmaBufImport) {
"LinuxExternalBrowserSurface requires VK_KHR_external_memory_fd, VK_EXT_external_memory_dma_buf, VK_EXT_image_drm_format_modifier, and VK_EXT_queue_family_foreign"
}
}
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.LinuxDmaBuf
?: return null
val native = try {
ExternalBrowserVulkanImports.dmaBuf(
renderer.device,
handle,
spec.width,
spec.height,
importVkFormat
)
} catch (t: Throwable) {
// Vulkan consumes the fd only when allocation succeeds. Before that, it remains ours.
runCatching { handle.close() }
throw t
}
val imported = ImportedImage(
frame.frameId,
native.image,
native::cleanup,
layout = VK_IMAGE_LAYOUT_GENERAL,
externalQueueFamily = VK_QUEUE_FAMILY_FOREIGN_EXT
)
importedSlots.put(frame.slot, imported)?.let(::retire)
return imported
}
override fun cleanupImports()
override fun cleanupImports() {
importedSlots.values.forEach { it.cleanup() }
importedSlots.clear()
}
}