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}
19
20func (d *Device) NewBinaryFence(export bool) (*BinaryFence, error) {
21 ci := vulkan.SemaphoreCreateInfo{SType: vulkan.StructureTypeSemaphoreCreateInfo}
22 if export {
23 ex := vulkan.ExportSemaphoreCreateInfo{SType: vulkan.StructureTypeExportSemaphoreCreateInfo, HandleTypes: vulkan.ExternalSemaphoreHandleTypeSyncFDBit}
24 ci.Next = unsafe.Pointer(&ex)
25 var sem vulkan.Semaphore
26 if err := vulkan.Check(d.Dispatch.CreateSemaphore(d.Logical, &ci, nil, &sem)); err != nil {
27 return nil, fmt.Errorf("create SYNC_FD semaphore: %w", err)
28 }
29 return &BinaryFence{d, sem}, nil
30 }
31 var sem vulkan.Semaphore
32 if err := vulkan.Check(d.Dispatch.CreateSemaphore(d.Logical, &ci, nil, &sem)); err != nil {
33 return nil, err
34 }
35 return &BinaryFence{d, sem}, nil
36}
37
38func (s *BinaryFence) Export() (int, error) {
39 info := vulkan.SemaphoreGetFdInfoKHR{SType: vulkan.StructureTypeSemaphoreGetFDInfoKHR, Semaphore: s.Semaphore, HandleType: vulkan.ExternalSemaphoreHandleTypeSyncFDBit}
40 fd := int32(-1)
41 if err := vulkan.Check(s.Device.Dispatch.GetSemaphoreFdKHR(s.Device.Logical, &info, &fd)); err != nil {
42 return -1, fmt.Errorf("export Vulkan SYNC_FD: %w", err)
43 }
44 return int(fd), nil
45}
46
47func (s *BinaryFence) ImportTemporary(fd int) error {
48 info := vulkan.ImportSemaphoreFdInfoKHR{SType: vulkan.StructureTypeImportSemaphoreFDInfoKHR, Semaphore: s.Semaphore, Flags: vulkan.SemaphoreImportTemporaryBit, HandleType: vulkan.ExternalSemaphoreHandleTypeSyncFDBit, Fd: int32(fd)}
49 return vulkan.Check(s.Device.Dispatch.ImportSemaphoreFdKHR(s.Device.Logical, &info))
50}
51
52func (s *BinaryFence) Close() {
53 if s == nil || s.Semaphore == 0 {
54 return
55 }
56 s.Device.Dispatch.DestroySemaphore(s.Device.Logical, s.Semaphore, nil)
57 s.Semaphore = 0
58}
59
60// SubmitEmpty is a hardware probe for the fence bridge. The production
61// renderer must submit its draw command buffer instead of an empty submit.
62// The queue is idle before exporting so this test isolates FD interop from
63// in-flight rendering; it does not authorize a CPU-wait presentation fallback.
64func (d *Device) SubmitEmpty(signal *BinaryFence, wait *BinaryFence) error {
65 info := vulkan.SubmitInfo2{SType: vulkan.StructureTypeSubmitInfo2}
66 if signal != nil {
67 s := vulkan.SemaphoreSubmitInfo{SType: vulkan.StructureTypeSemaphoreSubmitInfo, Semaphore: signal.Semaphore, StageMask: vulkan.PipelineStage2AllCommandsBit}
68 info.SignalSemaphoreInfoCount = 1
69 info.SignalSemaphoreInfos = &s
70 }
71 if wait != nil {
72 w := vulkan.SemaphoreSubmitInfo{SType: vulkan.StructureTypeSemaphoreSubmitInfo, Semaphore: wait.Semaphore, StageMask: vulkan.PipelineStage2AllCommandsBit}
73 info.WaitSemaphoreInfoCount = 1
74 info.WaitSemaphoreInfos = &w
75 }
76 if err := vulkan.Check(d.Dispatch.QueueSubmit2(d.Queue, 1, &info, 0)); err != nil {
77 return fmt.Errorf("submit SYNC_FD semaphore: %w", err)
78 }
79 return vulkan.Check(d.Dispatch.QueueWaitIdle(d.Queue))
80}