xref: /aosp_15_r20/external/libva/va/x11/va_dri2.c (revision 54e60f844a168e9a219354de272cd517ee8cd4b7)
1 /*
2  * Copyright © 2008 Red Hat, Inc.
3  * Copyright (c) 2023 Emil Velikov
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Soft-
7  * ware"), to deal in the Software without restriction, including without
8  * limitation the rights to use, copy, modify, merge, publish, distribute,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, provided that the above copyright
11  * notice(s) and this permission notice appear in all copies of the Soft-
12  * ware and that both the above copyright notice(s) and this permission
13  * notice appear in supporting documentation.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
17  * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
18  * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
19  * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE-
20  * QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR-
23  * MANCE OF THIS SOFTWARE.
24  *
25  * Except as contained in this notice, the name of a copyright holder shall
26  * not be used in advertising or otherwise to promote the sale, use or
27  * other dealings in this Software without prior written authorization of
28  * the copyright holder.
29  *
30  * Authors:
31  *   Kristian Høgsberg ([email protected])
32  */
33 
34 
35 #define NEED_REPLIES
36 #include <X11/Xlibint.h>
37 #include <X11/extensions/Xext.h>
38 #include <X11/extensions/extutil.h>
39 #include "xf86drm.h"
40 #include "va_dri2.h"
41 #include "va_dri2_priv.h"
42 #include "va_dricommon.h"
43 #include "va_dri2str.h"
44 #include "va_dri2tokens.h"
45 
46 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
47 
48 #ifndef DRI2DriverDRI
49 #define DRI2DriverDRI 0
50 #endif
51 
52 static int
53 VA_DRI2Error(Display *dpy, xError *err, XExtCodes *codes, int *ret_code);
54 
55 static VA_DRI2Buffer *
56 VA_DRI2GetBuffers_internal(XExtDisplayInfo *info,
57                            Display *dpy, XID drawable,
58                            int *width, int *height,
59                            unsigned int *attachments,
60                            int count,
61                            int *outCount);
62 
63 static char va_dri2ExtensionName[] = DRI2_NAME;
64 static XExtensionInfo _va_dri2_info_data;
65 static XExtensionInfo *va_dri2Info = &_va_dri2_info_data;
66 static XEXT_GENERATE_CLOSE_DISPLAY(VA_DRI2CloseDisplay, va_dri2Info)
67 static /* const */ XExtensionHooks va_dri2ExtensionHooks = {
68     NULL,               /* create_gc */
69     NULL,               /* copy_gc */
70     NULL,               /* flush_gc */
71     NULL,               /* free_gc */
72     NULL,               /* create_font */
73     NULL,               /* free_font */
74     VA_DRI2CloseDisplay,        /* close_display */
75     NULL,               /* wire_to_event */
76     NULL,               /* event_to_wire */
77     VA_DRI2Error,           /* error */
78     NULL,               /* error_string */
79 };
80 
81 static XEXT_GENERATE_FIND_DISPLAY(DRI2FindDisplay, va_dri2Info,
82                                   va_dri2ExtensionName,
83                                   &va_dri2ExtensionHooks,
84                                   0, NULL)
85 
86 static CARD32 _va_resource_x_error_drawable = 0;
87 static Bool   _va_resource_x_error_matched = False;
88 
89 #define VA_EnterResourceError(drawable)                 \
90   do {                                                  \
91     _va_resource_x_error_drawable = (drawable);         \
92     _va_resource_x_error_matched = False;               \
93   } while (0)
94 
95 #define VA_LeaveResourceError()                 \
96   do {                                          \
97     _va_resource_x_error_drawable = 0;          \
98   } while (0)
99 
100 #define VA_ResourceErrorMatched()               \
101   (_va_resource_x_error_matched)
102 
103 static int
VA_DRI2Error(Display * dpy,xError * err,XExtCodes * codes,int * ret_code)104 VA_DRI2Error(Display *dpy, xError *err, XExtCodes *codes, int *ret_code)
105 {
106     if (_va_resource_x_error_drawable == err->resourceID) {
107         _va_resource_x_error_matched = True;
108         return True;
109     }
110 
111     return False;
112 }
113 
VA_DRI2QueryExtension(Display * dpy,int * eventBase,int * errorBase)114 Bool VA_DRI2QueryExtension(Display *dpy, int *eventBase, int *errorBase)
115 {
116     XExtDisplayInfo *info = DRI2FindDisplay(dpy);
117 
118     if (XextHasExtension(info)) {
119         *eventBase = info->codes->first_event;
120         *errorBase = info->codes->first_error;
121         return True;
122     }
123 
124     return False;
125 }
126 
VA_DRI2QueryVersion(Display * dpy,int * major,int * minor)127 Bool VA_DRI2QueryVersion(Display *dpy, int *major, int *minor)
128 {
129     XExtDisplayInfo *info = DRI2FindDisplay(dpy);
130     xDRI2QueryVersionReply rep;
131     xDRI2QueryVersionReq *req;
132 
133     XextCheckExtension(dpy, info, va_dri2ExtensionName, False);
134 
135     LockDisplay(dpy);
136     GetReq(DRI2QueryVersion, req);
137     req->reqType = info->codes->major_opcode;
138     req->dri2ReqType = X_DRI2QueryVersion;
139     req->majorVersion = DRI2_MAJOR;
140     req->minorVersion = DRI2_MINOR;
141     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
142         UnlockDisplay(dpy);
143         SyncHandle();
144         return False;
145     }
146     *major = rep.majorVersion;
147     *minor = rep.minorVersion;
148     UnlockDisplay(dpy);
149     SyncHandle();
150 
151     return True;
152 }
153 
VA_DRI2Connect(Display * dpy,XID window,char ** driverName,char ** deviceName)154 Bool VA_DRI2Connect(Display *dpy, XID window,
155                     char **driverName, char **deviceName)
156 {
157     XExtDisplayInfo *info = DRI2FindDisplay(dpy);
158     xDRI2ConnectReply rep;
159     xDRI2ConnectReq *req;
160 
161     XextCheckExtension(dpy, info, va_dri2ExtensionName, False);
162 
163     LockDisplay(dpy);
164     GetReq(DRI2Connect, req);
165     req->reqType = info->codes->major_opcode;
166     req->dri2ReqType = X_DRI2Connect;
167     req->window = window;
168     req->driverType = DRI2DriverDRI;
169     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
170         UnlockDisplay(dpy);
171         SyncHandle();
172         return False;
173     }
174 
175     if (rep.driverNameLength == 0 && rep.deviceNameLength == 0) {
176         UnlockDisplay(dpy);
177         SyncHandle();
178         return False;
179     }
180 
181     *driverName = Xmalloc(rep.driverNameLength + 1);
182     if (*driverName == NULL) {
183         _XEatData(dpy,
184                   ((rep.driverNameLength + 3) & ~3) +
185                   ((rep.deviceNameLength + 3) & ~3));
186         UnlockDisplay(dpy);
187         SyncHandle();
188         return False;
189     }
190     _XReadPad(dpy, *driverName, rep.driverNameLength);
191     (*driverName)[rep.driverNameLength] = '\0';
192 
193     *deviceName = Xmalloc(rep.deviceNameLength + 1);
194     if (*deviceName == NULL) {
195         Xfree(*driverName);
196         _XEatData(dpy, ((rep.deviceNameLength + 3) & ~3));
197         UnlockDisplay(dpy);
198         SyncHandle();
199         return False;
200     }
201     _XReadPad(dpy, *deviceName, rep.deviceNameLength);
202     (*deviceName)[rep.deviceNameLength] = '\0';
203 
204     UnlockDisplay(dpy);
205     SyncHandle();
206 
207     return True;
208 }
209 
VA_DRI2Authenticate(Display * dpy,XID window,drm_magic_t magic)210 Bool VA_DRI2Authenticate(Display *dpy, XID window, drm_magic_t magic)
211 {
212     XExtDisplayInfo *info = DRI2FindDisplay(dpy);
213     xDRI2AuthenticateReq *req;
214     xDRI2AuthenticateReply rep;
215 
216     XextCheckExtension(dpy, info, va_dri2ExtensionName, False);
217 
218     LockDisplay(dpy);
219     GetReq(DRI2Authenticate, req);
220     req->reqType = info->codes->major_opcode;
221     req->dri2ReqType = X_DRI2Authenticate;
222     req->window = window;
223     req->magic = magic;
224 
225     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
226         UnlockDisplay(dpy);
227         SyncHandle();
228         return False;
229     }
230 
231     UnlockDisplay(dpy);
232     SyncHandle();
233 
234     return rep.authenticated;
235 }
236 
VA_DRI2CreateDrawable(Display * dpy,XID drawable)237 void VA_DRI2CreateDrawable(Display *dpy, XID drawable)
238 {
239     XExtDisplayInfo *info = DRI2FindDisplay(dpy);
240     xDRI2CreateDrawableReq *req;
241 
242     XextSimpleCheckExtension(dpy, info, va_dri2ExtensionName);
243 
244     LockDisplay(dpy);
245     GetReq(DRI2CreateDrawable, req);
246     req->reqType = info->codes->major_opcode;
247     req->dri2ReqType = X_DRI2CreateDrawable;
248     req->drawable = drawable;
249     UnlockDisplay(dpy);
250     SyncHandle();
251 }
252 
VA_DRI2DestroyDrawable(Display * dpy,XID drawable)253 void VA_DRI2DestroyDrawable(Display *dpy, XID drawable)
254 {
255     XExtDisplayInfo *info = DRI2FindDisplay(dpy);
256     xDRI2DestroyDrawableReq *req;
257     unsigned int attachement = 0; // FRONT_LEFT
258     VA_DRI2Buffer *buffers;
259 
260     XextSimpleCheckExtension(dpy, info, va_dri2ExtensionName);
261 
262     XSync(dpy, False);
263 
264     LockDisplay(dpy);
265     /*
266      * We have no way of catching DRI2DestroyDrawable errors because
267      * this message doesn't have a defined answer. So we test whether
268      * the drawable is still alive by sending DRIGetBuffers first and
269      * checking whether we get an error.
270      */
271     VA_EnterResourceError(drawable);
272 
273     buffers = VA_DRI2GetBuffers_internal(info, dpy, drawable,
274                                          NULL, NULL,
275                                          &attachement, 1, NULL);
276     VA_LeaveResourceError();
277     if (buffers)
278         XFree(buffers);
279     if (VA_ResourceErrorMatched()) {
280         UnlockDisplay(dpy);
281         SyncHandle();
282         return;
283     }
284 
285     GetReq(DRI2DestroyDrawable, req);
286     req->reqType = info->codes->major_opcode;
287     req->dri2ReqType = X_DRI2DestroyDrawable;
288     req->drawable = drawable;
289     UnlockDisplay(dpy);
290     SyncHandle();
291 }
292 
VA_DRI2GetBuffers_internal(XExtDisplayInfo * info,Display * dpy,XID drawable,int * width,int * height,unsigned int * attachments,int count,int * outCount)293 VA_DRI2Buffer *VA_DRI2GetBuffers_internal(XExtDisplayInfo *info,
294         Display *dpy, XID drawable,
295         int *width, int *height,
296         unsigned int *attachments, int count,
297         int *outCount)
298 {
299     xDRI2GetBuffersReply rep;
300     xDRI2GetBuffersReq *req;
301     VA_DRI2Buffer *buffers;
302     xDRI2Buffer repBuffer;
303     CARD32 *p;
304     int i;
305 
306     GetReqExtra(DRI2GetBuffers, count * 4, req);
307     req->reqType = info->codes->major_opcode;
308     req->dri2ReqType = X_DRI2GetBuffers;
309     req->drawable = drawable;
310     req->count = count;
311     p = (CARD32 *) &req[1];
312     for (i = 0; i < count; i++)
313         p[i] = attachments[i];
314 
315     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
316         return NULL;
317     }
318 
319     if (width)
320         *width = rep.width;
321     if (height)
322         *height = rep.height;
323     if (outCount)
324         *outCount = rep.count;
325 
326     buffers = Xmalloc(rep.count * sizeof buffers[0]);
327     if (buffers == NULL) {
328         _XEatData(dpy, rep.count * sizeof repBuffer);
329         return NULL;
330     }
331 
332     for (i = 0; i < rep.count; i++) {
333         _XReadPad(dpy, (char *) &repBuffer, sizeof repBuffer);
334         buffers[i].attachment = repBuffer.attachment;
335         buffers[i].name = repBuffer.name;
336         buffers[i].pitch = repBuffer.pitch;
337         buffers[i].cpp = repBuffer.cpp;
338         buffers[i].flags = repBuffer.flags;
339     }
340 
341     return buffers;
342 }
343 
VA_DRI2GetBuffers(Display * dpy,XID drawable,int * width,int * height,unsigned int * attachments,int count,int * outCount)344 VA_DRI2Buffer *VA_DRI2GetBuffers(Display *dpy, XID drawable,
345                                  int *width, int *height,
346                                  unsigned int *attachments, int count,
347                                  int *outCount)
348 {
349     XExtDisplayInfo *info = DRI2FindDisplay(dpy);
350     VA_DRI2Buffer *buffers;
351 
352     XextCheckExtension(dpy, info, va_dri2ExtensionName, False);
353 
354     LockDisplay(dpy);
355 
356     buffers = VA_DRI2GetBuffers_internal(info, dpy, drawable, width, height,
357                                          attachments, count, outCount);
358 
359     UnlockDisplay(dpy);
360     SyncHandle();
361 
362     return buffers;
363 }
364 
VA_DRI2CopyRegion(Display * dpy,XID drawable,XserverRegion region,CARD32 dest,CARD32 src)365 void VA_DRI2CopyRegion(Display *dpy, XID drawable, XserverRegion region,
366                        CARD32 dest, CARD32 src)
367 {
368     XExtDisplayInfo *info = DRI2FindDisplay(dpy);
369     xDRI2CopyRegionReq *req;
370     xDRI2CopyRegionReply rep;
371 
372     XextSimpleCheckExtension(dpy, info, va_dri2ExtensionName);
373 
374     LockDisplay(dpy);
375     GetReq(DRI2CopyRegion, req);
376     req->reqType = info->codes->major_opcode;
377     req->dri2ReqType = X_DRI2CopyRegion;
378     req->drawable = drawable;
379     req->region = region;
380     req->dest = dest;
381     req->src = src;
382 
383     _XReply(dpy, (xReply *)&rep, 0, xFalse);
384 
385     UnlockDisplay(dpy);
386     SyncHandle();
387 }
388 
389 static void
load_swap_req(xDRI2SwapBuffersReq * req,CARD64 target,CARD64 divisor,CARD64 remainder)390 load_swap_req(xDRI2SwapBuffersReq *req, CARD64 target, CARD64 divisor,
391               CARD64 remainder)
392 {
393     req->target_msc_hi = target >> 32;
394     req->target_msc_lo = target & 0xffffffff;
395     req->divisor_hi = divisor >> 32;
396     req->divisor_lo = divisor & 0xffffffff;
397     req->remainder_hi = remainder >> 32;
398     req->remainder_lo = remainder & 0xffffffff;
399 }
400 
401 static CARD64
vals_to_card64(CARD32 lo,CARD32 hi)402 vals_to_card64(CARD32 lo, CARD32 hi)
403 {
404     return (CARD64)hi << 32 | lo;
405 }
406 
VA_DRI2SwapBuffers(Display * dpy,XID drawable,CARD64 target_msc,CARD64 divisor,CARD64 remainder,CARD64 * count)407 void VA_DRI2SwapBuffers(Display *dpy, XID drawable, CARD64 target_msc,
408                         CARD64 divisor, CARD64 remainder, CARD64 *count)
409 {
410     XExtDisplayInfo *info = DRI2FindDisplay(dpy);
411     xDRI2SwapBuffersReq *req;
412     xDRI2SwapBuffersReply rep;
413 
414     XextSimpleCheckExtension(dpy, info, va_dri2ExtensionName);
415 
416     LockDisplay(dpy);
417     GetReq(DRI2SwapBuffers, req);
418     req->reqType = info->codes->major_opcode;
419     req->dri2ReqType = X_DRI2SwapBuffers;
420     req->drawable = drawable;
421     load_swap_req(req, target_msc, divisor, remainder);
422 
423     _XReply(dpy, (xReply *)&rep, 0, xFalse);
424 
425     *count = vals_to_card64(rep.swap_lo, rep.swap_hi);
426 
427     UnlockDisplay(dpy);
428     SyncHandle();
429 }
430 
va_DRI2_GetDriverNames(VADisplayContextP pDisplayContext,char ** drivers,unsigned * num_drivers)431 VAStatus va_DRI2_GetDriverNames(
432     VADisplayContextP pDisplayContext,
433     char **drivers,
434     unsigned *num_drivers
435 )
436 {
437 #define MAX_NAMES 2 // Adjust if needed
438 
439     static const struct {
440         const char * const dri_driver;
441         const char * const va_driver[MAX_NAMES];
442     } map[] = {
443         { "i965",       { "iHD", "i965"      } }, // Intel Media and OTC GenX
444         { "iris",       { "iHD", "i965"      } }, // Intel Media and OTC GenX
445         { "crocus",     { "i965"             } }, // OTC GenX
446     };
447 
448     VADriverContextP ctx = pDisplayContext->pDriverContext;
449     char *dri_driver;
450     unsigned count = 0;
451 
452     if (!(va_isDRI2Connected(ctx, &dri_driver) && dri_driver))
453         return VA_STATUS_ERROR_UNKNOWN;
454 
455     for (unsigned i = 0; i < ARRAY_SIZE(map); i++) {
456         if (strcmp(map[i].dri_driver, dri_driver) == 0) {
457             const char * const *va_drivers = map[i].va_driver;
458 
459             for (; count < MAX_NAMES && va_drivers[count] && count < *num_drivers; count++)
460                 drivers[count] = strdup(va_drivers[count]);
461 
462             break;
463         }
464     }
465 
466     /* Fallback to the dri driver, if there's no va equivalent in the map. */
467     if (!count) {
468         drivers[count] = dri_driver;
469         count++;
470     } else {
471         free(dri_driver);
472     }
473 
474     *num_drivers = count;
475 
476     return VA_STATUS_SUCCESS;
477 }
478