1//go:build linux
2
3package vkdevice
4
5import (
6 "fmt"
7 "unsafe"
8
9 "github.com/bnema/purego-vulkan/vulkan"
10)
11
12// BinaryFence exports a SYNC_FD after its queue submit, never an OPAQUE_FD.
13// Vulkan consumes a successfully imported SYNC_FD; a failed import leaves it
14// with the caller. Exported SYNC_FDs belong to the caller until DRM accepts.
15type BinaryFence struct {
16 Device *Device
17 Semaphore vulkan.Semaphore
18 // Reused call arguments: the bindings heap-allocate pointer arguments, and
19 // a fence has a single owner that calls these methods one at a time.
20 getInfo vulkan.SemaphoreGetFdInfoKHR
21 importInfo vulkan.ImportSemaphoreFdInfoKHR
22 fd int32
23}
24
25func (d *Device) NewBinaryFence(export bool) (*BinaryFence, error) {
26 ci := vulkan.SemaphoreCreateInfo{SType: vulkan.StructureTypeSemaphoreCreateInfo}
27 if export {
28 ex := vulkan.ExportSemaphoreCreateInfo{SType: vulkan.StructureTypeExportSemaphoreCreateInfo, HandleTypes: vulkan.ExternalSemaphoreHandleTypeSyncFDBit}
29 ci.Next = unsafe.Pointer(&ex)
30 var sem vulkan.Semaphore
31 if err := vulkan.Check(d.Dispatch.CreateSemaphore(d.Logical, &ci, nil, &sem)); err != nil {
32 return nil, fmt.Errorf("create SYNC_FD semaphore: %w", err)
33 }
34 return &BinaryFence{Device: d, Semaphore: sem}, nil
35 }
36 var sem vulkan.Semaphore
37 if err := vulkan.Check(d.Dispatch.CreateSemaphore(d.Logical, &ci, nil, &sem)); err != nil {
38 return nil, err
39 }
40 return &BinaryFence{Device: d, Semaphore: sem}, nil
41}
42
43func (s *BinaryFence) Export() (int, error) {
44 s.getInfo = vulkan.SemaphoreGetFdInfoKHR{SType: vulkan.StructureTypeSemaphoreGetFDInfoKHR, Semaphore: s.Semaphore, HandleType: vulkan.ExternalSemaphoreHandleTypeSyncFDBit}
45 s.fd = -1
46 if err := vulkan.Check(s.Device.Dispatch.GetSemaphoreFdKHR(s.Device.Logical, &s.getInfo, &s.fd)); err != nil {
47 return -1, fmt.Errorf("export Vulkan SYNC_FD: %w", err)
48 }
49 return int(s.fd), nil
50}
51
52func (s *BinaryFence) ImportTemporary(fd int) error {
53 s.importInfo = vulkan.ImportSemaphoreFdInfoKHR{SType: vulkan.StructureTypeImportSemaphoreFDInfoKHR, Semaphore: s.Semaphore, Flags: vulkan.SemaphoreImportTemporaryBit, HandleType: vulkan.ExternalSemaphoreHandleTypeSyncFDBit, Fd: int32(fd)}
54 return vulkan.Check(s.Device.Dispatch.ImportSemaphoreFdKHR(s.Device.Logical, &s.importInfo))
55}
56
57func (s *BinaryFence) Close() {
58 if s == nil || s.Semaphore == 0 {
59 return
60 }
61 s.Device.Dispatch.DestroySemaphore(s.Device.Logical, s.Semaphore, nil)
62 s.Semaphore = 0
63}
64
65// SubmitEmpty is a hardware probe for the fence bridge. The production
66// renderer must submit its draw command buffer instead of an empty submit.
67// The queue is idle before exporting so this test isolates FD interop from
68// in-flight rendering; it does not authorize a CPU-wait presentation fallback.
69func (d *Device) SubmitEmpty(signal *BinaryFence, wait *BinaryFence) error {
70 info := vulkan.SubmitInfo2{SType: vulkan.StructureTypeSubmitInfo2}
71 if signal != nil {
72 s := vulkan.SemaphoreSubmitInfo{SType: vulkan.StructureTypeSemaphoreSubmitInfo, Semaphore: signal.Semaphore, StageMask: vulkan.PipelineStage2AllCommandsBit}
73 info.SignalSemaphoreInfoCount = 1
74 info.SignalSemaphoreInfos = &s
75 }
76 if wait != nil {
77 w := vulkan.SemaphoreSubmitInfo{SType: vulkan.StructureTypeSemaphoreSubmitInfo, Semaphore: wait.Semaphore, StageMask: vulkan.PipelineStage2AllCommandsBit}
78 info.WaitSemaphoreInfoCount = 1
79 info.WaitSemaphoreInfos = &w
80 }
81 if err := vulkan.Check(d.Dispatch.QueueSubmit2(d.Queue, 1, &info, 0)); err != nil {
82 return fmt.Errorf("submit SYNC_FD semaphore: %w", err)
83 }
84 return vulkan.Check(d.Dispatch.QueueWaitIdle(d.Queue))
85}