]> git.sesse.net Git - nageru/blob - shared/va_display.cpp
Unify VA-API initialization.
[nageru] / shared / va_display.cpp
1 #include "va_display.h"
2 #include <assert.h>
3 #include <fcntl.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <va/va.h>
7 #include <va/va_drm.h>
8 #include <va/va_x11.h>
9
10 #include <string>
11 #include <vector>
12
13 using namespace std;
14
15 VADisplayWithCleanup::~VADisplayWithCleanup()
16 {
17         if (va_dpy != nullptr) {
18                 vaTerminate(va_dpy);
19         }
20         if (x11_display != nullptr) {
21                 XCloseDisplay(x11_display);
22         }
23         if (drm_fd != -1) {
24                 close(drm_fd);
25         }
26 }
27
28 unique_ptr<VADisplayWithCleanup> va_open_display(const string &va_display)
29 {
30         if (va_display.empty() || va_display[0] != '/') {  // An X display.
31                 Display *x11_display = XOpenDisplay(va_display.empty() ? nullptr : va_display.c_str());
32                 if (x11_display == nullptr) {
33                         fprintf(stderr, "error: can't connect to X server!\n");
34                         return nullptr;
35                 }
36
37                 unique_ptr<VADisplayWithCleanup> ret(new VADisplayWithCleanup);
38                 ret->x11_display = x11_display;
39                 ret->can_use_zerocopy = true;
40                 ret->va_dpy = vaGetDisplay(x11_display);
41                 if (ret->va_dpy == nullptr) {
42                         return nullptr;
43                 }
44                 return ret;
45         } else {  // A DRM node on the filesystem (e.g. /dev/dri/renderD128).
46                 int drm_fd = open(va_display.c_str(), O_RDWR);
47                 if (drm_fd == -1) {
48                         perror(va_display.c_str());
49                         return NULL;
50                 }
51                 unique_ptr<VADisplayWithCleanup> ret(new VADisplayWithCleanup);
52                 ret->drm_fd = drm_fd;
53                 ret->can_use_zerocopy = false;
54                 ret->va_dpy = vaGetDisplayDRM(drm_fd);
55                 if (ret->va_dpy == nullptr) {
56                         return nullptr;
57                 }
58                 return ret;
59         }
60 }
61
62 unique_ptr<VADisplayWithCleanup> try_open_va(
63         const string &va_display, const vector<VAProfile> &desired_profiles, VAEntrypoint entrypoint,
64         const vector<ConfigRequest> &desired_configs, VAProfile *chosen_profile, string *error)
65 {
66         unique_ptr<VADisplayWithCleanup> va_dpy = va_open_display(va_display);
67         if (va_dpy == nullptr) {
68                 if (error) *error = "Opening VA display failed";
69                 return nullptr;
70         }
71         int major_ver, minor_ver;
72         VAStatus va_status = vaInitialize(va_dpy->va_dpy, &major_ver, &minor_ver);
73         if (va_status != VA_STATUS_SUCCESS) {
74                 char buf[256];
75                 snprintf(buf, sizeof(buf), "vaInitialize() failed with status %d\n", va_status);
76                 if (error != nullptr) *error = buf;
77                 return nullptr;
78         }
79
80         int num_entrypoints = vaMaxNumEntrypoints(va_dpy->va_dpy);
81         unique_ptr<VAEntrypoint[]> entrypoints(new VAEntrypoint[num_entrypoints]);
82         if (entrypoints == nullptr) {
83                 if (error != nullptr) *error = "Failed to allocate memory for VA entry points";
84                 return nullptr;
85         }
86
87         // Try the profiles from highest to lowest until we find one that can be used.
88         VAProfile found_profile = VAProfileNone;
89         for (VAProfile profile : desired_profiles) {
90                 vaQueryConfigEntrypoints(va_dpy->va_dpy, profile, entrypoints.get(), &num_entrypoints);
91                 for (int slice_entrypoint = 0; slice_entrypoint < num_entrypoints; slice_entrypoint++) {
92                         if (entrypoints[slice_entrypoint] != entrypoint) {
93                                 continue;
94                         }
95
96                         // We found a usable encoder/decoder, so return it.
97                         if (chosen_profile != nullptr) {
98                                 *chosen_profile = profile;
99                         }
100                         found_profile = profile;
101                         break;
102                 }
103                 if (found_profile != VAProfileNone) {
104                         break;
105                 }
106         }
107         if (!found_profile) {
108                 if (error != nullptr) *error = "Can't find entry points for suitable codec profile";
109                 return nullptr;
110         }
111
112         int num_formats = vaMaxNumImageFormats(va_dpy->va_dpy);
113         assert(num_formats > 0);
114
115         unique_ptr<VAImageFormat[]> formats(new VAImageFormat[num_formats]);
116         va_status = vaQueryImageFormats(va_dpy->va_dpy, formats.get(), &num_formats);
117         if (va_status != VA_STATUS_SUCCESS) {
118                 char buf[256];
119                 snprintf(buf, sizeof(buf), "vaQueryImageFormats() failed with status %d\n", va_status);
120                 if (error != nullptr) *error = buf;
121                 return nullptr;
122         }
123
124         for (const ConfigRequest &request : desired_configs) {
125                 // Create the config.
126                 VAConfigAttrib attr = { VAConfigAttribRTFormat, request.rt_format };
127                 va_status = vaCreateConfig(va_dpy->va_dpy, found_profile, entrypoint,
128                         &attr, 1, request.config_id);
129                 if (va_status == VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT) {
130                         if (error != nullptr) *error = "No " + request.name + " hardware support";
131                         return nullptr;
132                 } else if (va_status != VA_STATUS_SUCCESS) {
133                         char buf[256];
134                         snprintf(buf, sizeof(buf), "vaCreateConfig() for %s failed with status %d\n", request.name.c_str(), va_status);
135                         if (error != nullptr) *error = buf;
136                         return nullptr;
137                 }
138
139                 // Find out which image format we're going to be using.
140                 bool format_found = false;
141                 for (int i = 0; i < num_formats; ++i) {
142                         if (formats[i].fourcc == request.fourcc) {
143                                 memcpy(request.image_format, &formats[i], sizeof(VAImageFormat));
144                                 format_found = true;
145                                 break;
146                         }
147                 }
148                 if (!format_found) {
149                         if (error != nullptr) *error = "Format for " + request.name + " not found";
150                         return nullptr;
151                 }
152         }
153
154         return va_dpy;
155 }