1//go:build linux
2
3package vkdevice
4
5import (
6 "bytes"
7 "fmt"
8 "os"
9 "sync"
10 "unsafe"
11
12 "github.com/bnema/purego"
13 "github.com/bnema/purego-vulkan/vulkan"
14)
15
16const validationLayer = "VK_LAYER_KHRONOS_validation"
17const debugExtension = "VK_EXT_debug_utils"
18
19type Validation struct {
20 mu sync.Mutex
21 errors []string
22 callback uintptr
23 messenger vulkan.DebugUtilsMessengerEXT
24}
25
26func (v *Validation) Messages() []string {
27 if v == nil {
28 return nil
29 }
30 v.mu.Lock()
31 defer v.mu.Unlock()
32 return append([]string(nil), v.errors...)
33}
34func cBytes(b []byte) string { return string(bytes.TrimRight(b, "\x00")) }
35func (v *Validation) enabled() (*byte, *byte, error) {
36 g := vulkan.Global()
37 var n uint32
38 if err := vulkan.Check(g.EnumerateInstanceLayerProperties(&n, nil)); err != nil {
39 return nil, nil, err
40 }
41 props := make([]vulkan.LayerProperties, n)
42 if n > 0 {
43 if err := vulkan.Check(g.EnumerateInstanceLayerProperties(&n, &props[0])); err != nil {
44 return nil, nil, err
45 }
46 }
47 found := false
48 for _, p := range props {
49 if cBytes(p.LayerName[:]) == validationLayer {
50 found = true
51 break
52 }
53 }
54 if !found {
55 return nil, nil, fmt.Errorf("validation layer %s unavailable", validationLayer)
56 }
57 if err := vulkan.Check(g.EnumerateInstanceExtensionProperties(nil, &n, nil)); err != nil {
58 return nil, nil, err
59 }
60 exts := make([]vulkan.ExtensionProperties, n)
61 if n > 0 {
62 if err := vulkan.Check(g.EnumerateInstanceExtensionProperties(nil, &n, &exts[0])); err != nil {
63 return nil, nil, err
64 }
65 }
66 found = false
67 for _, p := range exts {
68 if cBytes(p.ExtensionName[:]) == debugExtension {
69 found = true
70 break
71 }
72 }
73 if !found {
74 return nil, nil, fmt.Errorf("instance extension %s unavailable", debugExtension)
75 }
76 // The backing bytes live until CreateInstance returns.
77 return nil, nil, nil
78}
79
80func (v *Validation) create(id *vulkan.InstanceDispatch, instance vulkan.Instance) error {
81 if !id.HasCreateDebugUtilsMessengerEXT() {
82 return fmt.Errorf("VK_EXT_debug_utils messenger entrypoint unavailable")
83 }
84 v.callback = purego.NewCallback(func(severity vulkan.DebugUtilsMessageSeverityFlagsEXT, _ vulkan.DebugUtilsMessageTypeFlagsEXT, data *vulkan.DebugUtilsMessengerCallbackDataEXT, _ unsafe.Pointer) vulkan.Bool32 {
85 if data != nil && data.Message != nil {
86 msg := copyCString(data.Message)
87 level := "WARNING"
88 if severity&vulkan.DebugUtilsMessageSeverityFlagsEXT(vulkan.DebugUtilsMessageSeverityErrorBitEXT) != 0 {
89 level = "ERROR"
90 v.mu.Lock()
91 v.errors = append(v.errors, msg)
92 v.mu.Unlock()
93 }
94 _, _ = fmt.Fprintln(os.Stderr, "NEFERGUI_VULKAN_VALIDATION_"+level+":", msg)
95 }
96 return 0
97 })
98 ci := vulkan.DebugUtilsMessengerCreateInfoEXT{SType: vulkan.StructureTypeDebugUtilsMessengerCreateInfoEXT, MessageSeverity: vulkan.DebugUtilsMessageSeverityFlagsEXT(vulkan.DebugUtilsMessageSeverityErrorBitEXT | vulkan.DebugUtilsMessageSeverityWarningBitEXT), MessageType: vulkan.DebugUtilsMessageTypeGeneralBitEXT | vulkan.DebugUtilsMessageTypeValidationBitEXT | vulkan.DebugUtilsMessageTypePerformanceBitEXT, PfnUserCallback: vulkan.PFN_vkDebugUtilsMessengerCallbackEXT(v.callback)}
99 if err := vulkan.Check(id.CreateDebugUtilsMessengerEXT(instance, &ci, nil, &v.messenger)); err != nil {
100 purego.UnrefCallback(v.callback)
101 v.callback = 0
102 return err
103 }
104 return nil
105}
106func (v *Validation) destroy(id *vulkan.InstanceDispatch, instance vulkan.Instance) {
107 if v == nil {
108 return
109 }
110 if v.messenger != 0 {
111 id.DestroyDebugUtilsMessengerEXT(instance, v.messenger, nil)
112 v.messenger = 0
113 }
114 if v.callback != 0 {
115 purego.UnrefCallback(v.callback)
116 v.callback = 0
117 }
118}
119func copyCString(p *byte) string {
120 if p == nil {
121 return ""
122 }
123 const max = 4096
124 b := make([]byte, 0, 128)
125 for i := 0; i < max; i++ {
126 c := *(*byte)(unsafe.Add(unsafe.Pointer(p), i))
127 if c == 0 {
128 break
129 }
130 b = append(b, c)
131 }
132 return string(b)
133}