12 #include <GL/internal/glx.h>
13 #include <X11/extensions/XShm.h>
14 /*----------------------------------------------------------------------------*/
16 #define REDIRECT_LOG_TO_TRACE_FILE
17 /*----------------------------------------------------------------------------*/
18 #define u8 unsigned char
19 #define u16 unsigned short
20 #define u32 unsigned int
23 /*----------------------------------------------------------------------------*/
24 #include "log_trace.c"
25 /*----------------------------------------------------------------------------*/
26 static pthread_once_t prolog_once
= PTHREAD_ONCE_INIT
;
27 static pthread_mutex_t global_mutex
;
28 static pthread_key_t thd_data_key
;
29 static void thd_data_cleanup(void *key_value
);
30 /*----------------------------------------------------------------------------*/
31 /* very limited, but should be more than enough */
32 static bool glx_extension_enabled
= false;
33 /*----------------------------------------------------------------------------*/
35 extern void glTexXImage2D_avx2(void* input
);
36 extern void clearcolor_avx2(void* input
);
37 extern void minmax_avx2(void* ctx
);
38 extern void alphablend_rgba_avx2(void* input
);
40 /*----------------------------------------------------------------------------*/
42 /* stale ipc... wonder why this is still a thing */
43 static struct ipcs_tracker
{
50 static void ipc_track(int id
, void *mem
)
53 /* try to get an existing slot */
56 if (i
== ipcs_tracker
.n
|| ipcs_tracker
.slots
[i
].id
== -1)
60 if (i
== ipcs_tracker
.n
) {
61 ipcs_tracker
.slots
= realloc(ipcs_tracker
.slots
,
62 sizeof(*ipcs_tracker
.slots
) * (i
+ 1));
65 ipcs_tracker
.slots
[i
].id
= id
;
66 ipcs_tracker
.slots
[i
].mem
= mem
;
67 TRACE("tracking id=%d mem=%p", ipcs_tracker
.slots
[i
].id
, ipcs_tracker
.slots
[i
].mem
);
69 static void ipc_untrack(int id
)
75 if (i
== ipcs_tracker
.n
)
77 if (ipcs_tracker
.slots
[i
].id
== id
)
81 TRACE("untracking id=%d mem=%p", ipcs_tracker
.slots
[i
].id
, ipcs_tracker
.slots
[i
].mem
);
82 ipcs_tracker
.slots
[i
].id
= -1;
84 static void ipcs_rm(void)
90 if (i
== ipcs_tracker
.n
)
92 TRACE("slot[%u]->id=%d:mem=%p", i
, ipcs_tracker
.slots
[i
].id
, ipcs_tracker
.slots
[i
].mem
);
93 if (ipcs_tracker
.slots
[i
].id
!= -1) {
94 TRACE("removing id=%d mem=%p", ipcs_tracker
.slots
[i
].id
, ipcs_tracker
.slots
[i
].mem
);
95 shmdt(ipcs_tracker
.slots
[i
].mem
);
96 shmctl(ipcs_tracker
.slots
[i
].id
, IPC_RMID
, 0);
101 static void ipcs_tracker_init(void)
105 ipcs_tracker
.slots
= 0;
109 LOG("ERROR:unable to register IPC cleanup handler, your system will have stale IPCs on abnormal termination");
110 TRACE("tracker of IPCs installed");
114 #define LOCK lock((void*)__func__)
115 #define UNLOCK unlock((void*)__func__)
116 static void lock(void *fn
)
120 r
= pthread_mutex_lock(&global_mutex
);
122 LOG("ERROR:from %s:unable to lock the global mutex", fn
);
126 static void unlock(void *fn
)
130 r
= pthread_mutex_unlock(&global_mutex
);
132 LOG("ERROR:from %s:unable to unlock the global mutex", fn
);
137 /*{{{ drawable tracker and listener */
139 * XXX: it is not clear how a drawable is "removed" from glx/gl, then this array
140 * will grow infinitely
143 int id
; /* -1 means available slot */
151 int id
; /* if -1, no shm segment attached */
156 struct drawable_t
*slots
;
159 static void ds_init(void)
164 static struct drawable_t
*ds_get(int id
)
172 if (ds
.slots
[d
].id
== id
)
177 /* must be called with the global mutex locked */
178 static void ds_destroy(int slot
)
180 if (ds
.slots
[slot
].shm
.id
!= -1) {
181 shmdt(ds
.slots
[slot
].shm
.m
);
182 shmctl(ds
.slots
[slot
].shm
.id
, IPC_RMID
, 0);
183 ipc_untrack(ds
.slots
[slot
].shm
.id
);
184 ds
.slots
[slot
].shm
.id
= -1;
186 ds
.slots
[slot
].id
= -1;
188 struct drawable_destroy_listener_arg
{
192 static void *drawable_destroy_listener(void *arg
)
194 struct drawable_destroy_listener_arg
*a
;
200 memset(&e
, 0, sizeof(e
));
201 XNextEvent(a
->dpy
, &e
);
202 if (e
.type
== DestroyNotify
) {
206 if (e
.xdestroywindow
.window
== ds
.slots
[a
->slot
].id
) {
207 TRACE("destroying drawable 0x%x", ds
.slots
[a
->slot
].id
);
208 XCloseDisplay(a
->dpy
);
218 static void drawable_destroy_listener_spawn(Display
*dpy
, int slot
)
221 XSetWindowAttributes attrs
;
223 struct drawable_destroy_listener_arg
*arg
;
226 local_dpy
= XOpenDisplay(DisplayString(dpy
));
227 if (local_dpy
== 0) {
228 LOG("ERROR:unable to get a local display connexion for drawable 0x%x from display %p, will leak ipc shared memory segment, will probably leak ipc shared memory segments", ds
.slots
[slot
].id
, dpy
);
231 /* the event mask is specific to our local dpy */
232 memset(&attrs
, 0, sizeof(attrs
));
233 attrs
.event_mask
= StructureNotifyMask
;
234 XChangeWindowAttributes(local_dpy
, ds
.slots
[slot
].id
, CWEventMask
,
236 arg
= malloc(sizeof(*arg
));
237 arg
->dpy
= local_dpy
;
239 TRACE("spawing destroy listener thread for drawable 0x%x", ds
.slots
[slot
].id
);
240 r
= pthread_create(&listener
, 0, drawable_destroy_listener
, arg
);
242 LOG("ERROR:unable to spawn the destroy listener for drawable 0x%x, will probably leak the ipc shared memory segments", ds
.slots
[slot
].id
);
246 TRACE("listener thread for drawable 0x%x is 0x%x", ds
.slots
[slot
].id
, listener
);
248 static struct drawable_t
*ds_new(Display
*dpy
, int id
, int bytes_per_line
,
249 int height
, int width
, int depth
, Visual
*visual
, int screen
)
251 struct drawable_t
*new;
257 ds
.slots
= realloc(ds
.slots
, sizeof(*ds
.slots
)
263 if (ds
.slots
[s
].id
== -1)
268 new->shm
.id
= shmget(IPC_PRIVATE
, bytes_per_line
* height
,
270 if (new->shm
.id
== -1) {
271 LOG("ERROR:unable to create a new ipc shared memory segment for drawable 0x%x", id
);
274 new->shm
.m
= shmat(new->shm
.id
, 0, 0);
275 if (new->shm
.m
== 0) {
276 shmctl(new->shm
.id
, IPC_RMID
, 0);
280 ipc_track(new->shm
.id
, new->shm
.m
);
281 new->bytes_per_line
= bytes_per_line
;
282 new->height
= height
;
285 new->visual
= visual
;
286 new->screen
= screen
;
288 drawable_destroy_listener_spawn(dpy
, s
);
292 /*{{{ init/fini entry/leave */
293 static void init(void)
298 r
= pthread_mutex_init(&global_mutex
, 0);
300 LOG("ERROR:unable to init the global mutex");
303 LOG("global mutex initialized");
304 r
= pthread_key_create(&thd_data_key
, thd_data_cleanup
);
306 LOG("ERROR:unable to create the thread data key");
309 TRACE("thread data key created");
310 glx_extension_enabled
= false;
314 static void enter(void *fn
, int *old_cancel_state
)
318 /* XXX: we hijack -1 as "failed" to set on entry */
319 *old_cancel_state
= -1;
320 r
= pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, old_cancel_state
);
322 LOG("WARNING:from %s:unable to disable thread cancellation", fn
);
325 static void leave(void *fn
, int old_cancel_state
)
330 /* we failed to set the cancel state on entry, don't touch it */
331 if (old_cancel_state
== -1)
333 r
= pthread_setcancelstate(old_cancel_state
, 0);
335 LOG("WARNING:from %s:unable to restore client thread cancel state", fn
);
337 #define ONCE_AND_ENTER \
338 int old_cancel_state; \
340 pthread_once(&prolog_once, init); \
341 enter((void*)__func__, &old_cancel_state)
342 #define ENTER enter((void*)__func__, &old_cancel_state)
343 #define LEAVE leave((void*)__func__, old_cancel_state)
354 GLint align
; /* initial value is 4 */
355 GLint row_pixels_n
; /* initial value is 0 */
356 } read_from_client
; /* we are the "server" here */
358 GLint align
; /* initial value is 4 */
361 struct ctx_t
{ /* it is inited with zeros, cleanup once unused */
362 bool was_already_made_current
;
364 void *dpy
; /* XXX: for record only, in theory, do not use */
367 GLXDrawable drawable
;
368 GLXDrawable read
; /* the drawable to read from */
369 struct { /* set initially to the window size of the drawable */
376 unsigned int current
;
378 struct { /* record the last glTranslate */
383 double mv
[4*4]; /* model view */
385 struct { /* record the last glOrtho */
393 double proj
[4*4]; /* projection */
395 double tex
[4*4]; /* texture */
396 double col
[4*4]; /* color */
397 } mtxs
; /* matrixes */
404 /* defines the default vertex primary color attribute */
415 Display
*dpy
; /* display at glXMakeCurrent */
417 /* the index in the array + 1 is actually the name of the texture */
422 * texture name which is bound to the GL_TEXTURE_2D on the
423 * active texture unit, 0 is the default texture.
429 /* XXX: we should record also the current color attribute */
432 struct { /* between 0.0 and 1.0 */
433 /* opengl = top->bottom */
437 struct { /* viewport "mapped" */
448 int i
; /* current vs for coords recording */
451 bool blending_enabled
;
458 static struct thd_data_t
*self_data_get(void)
460 struct thd_data_t
*thd_data
;
462 thd_data
= pthread_getspecific(thd_data_key
);
466 thd_data
= calloc(1, sizeof(*thd_data
));
467 r
= pthread_setspecific(thd_data_key
, thd_data
);
469 LOG("ERROR:unable to attach self thread specific data");
472 TRACE("%p thd_data attached to self thread", thd_data
);
477 static void self_detach_ctx_with(Display
*dpy
, struct thd_data_t
*self_data
)
481 self
= pthread_self();
482 if (self_data
->ctx
== 0)
485 if (!self_data
->ctx
->current
.attached
) {
486 LOG("ERROR:the self thread current context %p is not tagged as being current to any thread, ignoring and continuing", self_data
->ctx
);
489 /* self_data->ctx->current.attached == true */
490 if (!pthread_equal(self
, self_data
->ctx
->current
.thd
)) {
491 LOG("ERROR:the self thread, 0x%x, current context %p has the wrong thread 0x%x", self
, self_data
->ctx
, self_data
->ctx
->current
.thd
);
494 self_data
->ctx
->current
.thd
= 0;
495 self_data
->ctx
->current
.dpy
= 0;
496 self_data
->ctx
->current
.attached
= false;
500 * XXX: the self thread must not have a current context, the provided context
501 * must not be current to no thread
503 static void self_attach_ctx(Display
*dpy
, struct ctx_t
*ctx
)
505 struct thd_data_t
*self_data
;
508 self
= pthread_self();
509 self_data
= self_data_get();
510 if (self_data
->ctx
!= 0) {
511 LOG("ERROR:the context %p cannot be attached to the self thread 0x%x because the self thread has already a current context %p", ctx
, self
, self_data
->ctx
);
514 /* self_data->ctx == 0 */
515 if (ctx
->current
.attached
) {
516 LOG("ERROR:the context %p cannot be attached to the self thread 0x%x because the context is already current the thread 0x%x", ctx
, self
, self_data
->ctx
->current
.thd
);
519 ctx
->current
.thd
= self
;
520 ctx
->current
.dpy
= dpy
;
521 ctx
->current
.attached
= true;
522 self_data
->ctx
= ctx
;
524 static void ctx_free(Display
*dpy
, struct ctx_t
*ctx
)
530 if (i
== ctx
->texs
.n
)
532 free(ctx
->texs
.a
[i
].pixels
);
539 * XXX: a thread cannot be cancelled while holding the global mutex, namely this
540 * destructor cannot be called while holding this very thread is holding the
542 * XXX: thd_data is guaranted to be non 0
544 static void thd_data_cleanup(void *key_value
)
546 struct thd_data_t
*self_data
;
549 self
= pthread_self();
550 self_data
= key_value
;
552 if (self_data
->ctx
== 0)
553 TRACE("the cancelled thread 0x%x has no current ctx", self
);
555 LOG("WARNING:the cancelled thread 0x%x has a current context %p, trying to free using the current display stored 0x%x", self
, self_data
->ctx
, self_data
->ctx
->current
.dpy
);
556 /* XXX: will crash if display is gone... may have to leak */
557 ctx_free(self_data
->ctx
->current
.dpy
, self_data
->ctx
);
561 static GLfloat
min_of_quad_plane_xs(struct ctx_t
*ctx
)
564 x
= ctx
->begin_end_blk
.quad
.vs
[0].plane
.x
;
565 if (ctx
->begin_end_blk
.quad
.vs
[1].plane
.x
< x
)
566 x
= ctx
->begin_end_blk
.quad
.vs
[1].plane
.x
;
567 if (ctx
->begin_end_blk
.quad
.vs
[2].plane
.x
< x
)
568 x
= ctx
->begin_end_blk
.quad
.vs
[2].plane
.x
;
569 if (ctx
->begin_end_blk
.quad
.vs
[3].plane
.x
< x
)
570 x
= ctx
->begin_end_blk
.quad
.vs
[3].plane
.x
;
573 static GLfloat
min_of_quad_plane_ys(struct ctx_t
*ctx
)
576 y
= ctx
->begin_end_blk
.quad
.vs
[0].plane
.y
;
577 if (ctx
->begin_end_blk
.quad
.vs
[1].plane
.y
< y
)
578 y
= ctx
->begin_end_blk
.quad
.vs
[1].plane
.y
;
579 if (ctx
->begin_end_blk
.quad
.vs
[2].plane
.y
< y
)
580 y
= ctx
->begin_end_blk
.quad
.vs
[2].plane
.x
;
581 if (ctx
->begin_end_blk
.quad
.vs
[3].plane
.y
< y
)
582 y
= ctx
->begin_end_blk
.quad
.vs
[3].plane
.y
;
585 static GLfloat
max_of_quad_plane_xs(struct ctx_t
*ctx
)
588 x
= ctx
->begin_end_blk
.quad
.vs
[0].plane
.x
;
589 if (ctx
->begin_end_blk
.quad
.vs
[1].plane
.x
> x
)
590 x
= ctx
->begin_end_blk
.quad
.vs
[1].plane
.x
;
591 if (ctx
->begin_end_blk
.quad
.vs
[2].plane
.x
> x
)
592 x
= ctx
->begin_end_blk
.quad
.vs
[2].plane
.x
;
593 if (ctx
->begin_end_blk
.quad
.vs
[3].plane
.x
> x
)
594 x
= ctx
->begin_end_blk
.quad
.vs
[3].plane
.x
;
597 static GLfloat
max_of_quad_plane_ys(struct ctx_t
*ctx
)
600 y
= ctx
->begin_end_blk
.quad
.vs
[0].plane
.y
;
601 if (ctx
->begin_end_blk
.quad
.vs
[1].plane
.y
> y
)
602 y
= ctx
->begin_end_blk
.quad
.vs
[1].plane
.y
;
603 if (ctx
->begin_end_blk
.quad
.vs
[2].plane
.y
> y
)
604 y
= ctx
->begin_end_blk
.quad
.vs
[2].plane
.y
;
605 if (ctx
->begin_end_blk
.quad
.vs
[3].plane
.y
> y
)
606 y
= ctx
->begin_end_blk
.quad
.vs
[3].plane
.y
;
609 static GLfloat
min_of_quad_tex_ss(struct ctx_t
*ctx
)
612 s
= ctx
->begin_end_blk
.quad
.vs
[0].tex
.s
;
613 if (ctx
->begin_end_blk
.quad
.vs
[1].tex
.s
< s
)
614 s
= ctx
->begin_end_blk
.quad
.vs
[1].tex
.s
;
615 if (ctx
->begin_end_blk
.quad
.vs
[2].tex
.s
< s
)
616 s
= ctx
->begin_end_blk
.quad
.vs
[2].tex
.s
;
617 if (ctx
->begin_end_blk
.quad
.vs
[3].tex
.s
< s
)
618 s
= ctx
->begin_end_blk
.quad
.vs
[3].tex
.s
;
621 static GLfloat
min_of_quad_tex_ts(struct ctx_t
*ctx
)
624 t
= ctx
->begin_end_blk
.quad
.vs
[0].tex
.t
;
625 if (ctx
->begin_end_blk
.quad
.vs
[1].tex
.t
< t
)
626 t
= ctx
->begin_end_blk
.quad
.vs
[1].tex
.t
;
627 if (ctx
->begin_end_blk
.quad
.vs
[2].tex
.t
< t
)
628 t
= ctx
->begin_end_blk
.quad
.vs
[2].tex
.t
;
629 if (ctx
->begin_end_blk
.quad
.vs
[3].tex
.t
< t
)
630 t
= ctx
->begin_end_blk
.quad
.vs
[3].tex
.t
;
633 static GLfloat
max_of_quad_tex_ss(struct ctx_t
*ctx
)
636 s
= ctx
->begin_end_blk
.quad
.vs
[0].tex
.s
;
637 if (ctx
->begin_end_blk
.quad
.vs
[1].tex
.s
> s
)
638 s
= ctx
->begin_end_blk
.quad
.vs
[1].tex
.s
;
639 if (ctx
->begin_end_blk
.quad
.vs
[2].tex
.s
> s
)
640 s
= ctx
->begin_end_blk
.quad
.vs
[2].tex
.s
;
641 if (ctx
->begin_end_blk
.quad
.vs
[3].tex
.s
> s
)
642 s
= ctx
->begin_end_blk
.quad
.vs
[3].tex
.s
;
645 static GLfloat
max_of_quad_tex_ts(struct ctx_t
*ctx
)
648 t
= ctx
->begin_end_blk
.quad
.vs
[0].tex
.t
;
649 if (ctx
->begin_end_blk
.quad
.vs
[1].tex
.t
> t
)
650 t
= ctx
->begin_end_blk
.quad
.vs
[1].tex
.t
;
651 if (ctx
->begin_end_blk
.quad
.vs
[2].tex
.t
> t
)
652 t
= ctx
->begin_end_blk
.quad
.vs
[2].tex
.t
;
653 if (ctx
->begin_end_blk
.quad
.vs
[3].tex
.t
> t
)
654 t
= ctx
->begin_end_blk
.quad
.vs
[3].tex
.t
;
657 static GLfloat
max_of_quad_alpha(struct ctx_t
*ctx
)
660 alpha
= ctx
->begin_end_blk
.quad
.vs
[0].color
.alpha
;
661 if (ctx
->begin_end_blk
.quad
.vs
[1].color
.alpha
> alpha
)
662 alpha
= ctx
->begin_end_blk
.quad
.vs
[1].color
.alpha
;
663 if (ctx
->begin_end_blk
.quad
.vs
[2].color
.alpha
> alpha
)
664 alpha
= ctx
->begin_end_blk
.quad
.vs
[2].color
.alpha
;
665 if (ctx
->begin_end_blk
.quad
.vs
[3].color
.alpha
> alpha
)
666 alpha
= ctx
->begin_end_blk
.quad
.vs
[3].color
.alpha
;
669 static GLfloat
min_of_quad_alpha(struct ctx_t
*ctx
)
672 alpha
= ctx
->begin_end_blk
.quad
.vs
[0].color
.alpha
;
673 if (ctx
->begin_end_blk
.quad
.vs
[1].color
.alpha
< alpha
)
674 alpha
= ctx
->begin_end_blk
.quad
.vs
[1].color
.alpha
;
675 if (ctx
->begin_end_blk
.quad
.vs
[2].color
.alpha
< alpha
)
676 alpha
= ctx
->begin_end_blk
.quad
.vs
[2].color
.alpha
;
677 if (ctx
->begin_end_blk
.quad
.vs
[3].color
.alpha
< alpha
)
678 alpha
= ctx
->begin_end_blk
.quad
.vs
[3].color
.alpha
;
681 static void ctx_viewport_set_to_drawable_sz(Display
*dpy
, struct ctx_t
*ctx
)
684 XWindowAttributes attrs
;
686 memset(&attrs
, 0, sizeof(attrs
));
687 r
= XGetWindowAttributes(dpy
, ctx
->drawable
, &attrs
);
689 LOG("%p:ERROR:unable to get the attributes of drawable 0x%x from display %p", ctx
, ctx
->drawable
, dpy
);
697 ctx
->vp
.width
= attrs
.width
;
698 ctx
->vp
.height
= attrs
.height
;
700 TRACE("%p:x=%d:y=%d:width=%d:height=%d", ctx
, ctx
->vp
.x
, ctx
->vp
.y
, ctx
->vp
.width
, ctx
->vp
.height
);
703 * The no_cpy flag is to notify the buffer will be fully redrawn and it is
704 * useless to perform an _image_ copy, for instance if we come from a clear
706 * If we fail to adjust the shm, nothing was changed.
707 * There must be a shm attached to the drawable.
709 static bool drawable_shm_adjust(struct drawable_t
*d
, int new_bytes_per_line
,
710 int new_height
, int new_width
, int new_depth
, Visual
*new_visual
,
711 int new_screen
, bool no_cpy
)
716 /* XXX: we presume the screen and visual did not change */
717 TRACE("drawable 0x%x adjust:bytes_per_line=%d->%d:height=%d->%d:width=%d->%d:depth=%d->%d:no_copy=%s", d
->id
, d
->bytes_per_line
, new_bytes_per_line
, d
->height
, new_height
, d
->width
, new_width
, d
->depth
, new_depth
, no_cpy
? "true" : "false");
719 if ((d
->bytes_per_line
* d
->height
)
720 > (new_bytes_per_line
* new_height
)) {
721 TRACE("no_cpy:no reallocation of ipc shared memory segment");
724 TRACE("no_cpy:reallocation of ipc shared memory segment because the buffer size did change: old buffer size=%d/new buffer size=%d", d
->bytes_per_line
* d
->height
, new_bytes_per_line
* new_height
);
726 /* pixel format still presumed the same, though */
727 if (d
->bytes_per_line
== new_bytes_per_line
728 && d
->height
== new_height
&& d
->width
== new_width
) {
729 TRACE("cpy:no change in buffer format");
732 TRACE("cpy:reallocation of ipc shared memory segment because the buffer format did change");
734 TRACE("allocation of a new ipc shared memory segment");
735 new_shm_id
= shmget(IPC_PRIVATE
, new_bytes_per_line
* new_height
,
737 if (new_shm_id
== -1) {
738 LOG("ERROR:unable to create a new ipc shared memory segment, keeping the old buffer");
741 new_shm_m
= shmat(new_shm_id
, 0, 0);
742 if (new_shm_m
== 0) {
743 shmctl(new_shm_id
, IPC_RMID
, 0);
744 LOG("ERROR:unable to attach the new ipc shared memory segment to the process, keeping the old buffer");
747 ipc_track(new_shm_id
, new_shm_m
);
757 u32 dst_width_pixs_n
;
768 /* construct the intersection of the new and old rectangles */
769 if (d
->height
> new_height
)
770 min_height
= new_height
;
772 min_height
= d
->height
;
773 if (d
->width
> new_width
)
774 min_width
= new_width
;
776 min_width
= d
->width
;
777 TRACE("copying the intersection h/w=%d/%d\n", min_height
, min_width
);
779 avx2_input
.dst
= (void*)new_shm_m
;
780 avx2_input
.dst_x_offset
= 0;
781 avx2_input
.dst_y_offset
= 0;
782 avx2_input
.dst_width_pixs_n
= min_width
;
783 avx2_input
.dst_line_pixs_n
= new_bytes_per_line
>> 2;
784 avx2_input
.src
= (void*)d
->shm
.m
;
785 avx2_input
.src_line_pixs_n
= d
->bytes_per_line
>> 2;
786 avx2_input
.height_lines_n
= min_height
;
787 glTexXImage2D_avx2(&avx2_input
);
800 old_pix
= (u32
*)(d
->shm
.m
+ y
801 * d
->bytes_per_line
+ (x
<< 2));
802 new_pix
= (u32
*)(new_shm_m
+ y
803 * new_bytes_per_line
+ (x
<< 2));
804 new_pix
[0] = old_pix
[0];
811 TRACE("getting rid of the previous ipc shared memory segment");
813 shmctl(d
->shm
.id
, IPC_RMID
, 0);
814 ipc_untrack(d
->shm
.id
);
815 d
->shm
.m
= new_shm_m
;
816 d
->shm
.id
= new_shm_id
;
818 d
->bytes_per_line
= new_bytes_per_line
;
819 d
->height
= new_height
;
820 d
->width
= new_width
;
821 d
->depth
= new_depth
;
822 d
->visual
= new_visual
;
823 d
->screen
= new_screen
;
826 static struct drawable_t
*get_synced_drawable_noxlock(Display
*dpy
, int id
,
829 struct drawable_t
*d
;
831 XWindowAttributes attrs
;
833 XShmSegmentInfo info
;
837 memset(&attrs
, 0, sizeof(attrs
));
838 r
= XGetWindowAttributes(dpy
, id
, &attrs
);
840 LOG("ERROR:unable to get the attributes of drawable 0x%x from display %p", id
, dpy
);
843 TRACE("drawable=0x%x:dpy=%p:x=%d:y=%d:width=%u:height=%u:depth=%u", id
, dpy
, attrs
.x
, attrs
.y
, attrs
.width
, attrs
.height
, attrs
.depth
);
844 img
= XShmCreateImage(dpy
, attrs
.visual
, attrs
.depth
, ZPixmap
, 0, &info
,
845 attrs
.width
, attrs
.height
);
847 LOG("ERROR:unable to get the XSHM image bytes per line for drawable 0x%x from display %p", id
, dpy
);
851 if (d
== 0) { /* brand new one */
852 d
= ds_new(dpy
, id
, img
->bytes_per_line
, attrs
.height
, attrs
.width
,
853 attrs
.depth
, attrs
.visual
,
854 XScreenNumberOfScreen(attrs
.screen
));
856 LOG("ERROR:unable to create an ipc shared memory segment for the drawable 0x%x from display", id
, dpy
);
857 goto destroy_shm_img
;
859 TRACE("new drawable 0x%u, bytes_per_line=%d, height=%d, width=%d", d
->id
, d
->bytes_per_line
, d
->height
, d
->width
);
860 } else if (d
->shm
.id
!= -1) {/* XXX: can be expensive */
863 shm_adjust_ok
= drawable_shm_adjust(d
, img
->bytes_per_line
,
864 attrs
.height
, attrs
.width
, attrs
.depth
, attrs
.visual
,
865 XScreenNumberOfScreen(attrs
.screen
), no_cpy
);
868 } else { /* d->shm.id == -1 */
869 TRACE("drawable 0x%x without an ipc shared memory segment, attaching a new one", id
);
870 d
->shm
.id
= shmget(IPC_PRIVATE
, d
->bytes_per_line
* d
->height
,
872 if (d
->shm
.id
== -1) {
873 LOG("ERROR:unable to create a new ipc shared memory segment for drawable 0x%x", id
);
876 d
->shm
.m
= shmat(d
->shm
.id
, 0, 0);
878 shmctl(d
->shm
.id
, IPC_RMID
, 0);
880 LOG("ERROR:unable to attached memory to ipc shared memory segment id=%d for drawable 0x%x", d
->shm
.id
, id
);
883 ipc_track(d
->shm
.id
, d
->shm
.m
);
890 static struct drawable_t
*get_synced_drawable(Display
*dpy
, int id
, bool no_cpy
)
892 struct drawable_t
*d
;
895 d
= get_synced_drawable_noxlock(dpy
, id
, no_cpy
);
899 #define DONT_COPY true
900 static void color_buffer_clear(struct ctx_t
*ctx
)
902 struct drawable_t
*d
;
906 u32 dst_width_pixs_n
;
907 u32 dst_line_bytes_n
;
908 u32 dst_height_lines_n
;
910 TRACE("%p:clearing the color buffer:b=0x%02x:g=0x%02x:r=0x%02x:a=0x%02x", ctx
, 0x25, 0x25, 0x25, 0xff);
919 /* XXX: we hardcode a reasonable clear color */
920 /* b = (u8)(255. * ctx->clear_color.blue);
921 * g = (u8)(255. * ctx->clear_color.green);
922 * r = (u8)(255. * ctx->clear_color.red);
923 * a = (u8)(255. * ctx->clear_color.alpha);
929 TRACE("%p:clearing the color buffer:b=0x%02x:g=0x%02x:r=0x%02x:a=0x%02x", ctx
, b
, g
, r
, a
);
931 d
= get_synced_drawable(ctx
->current
.dpy
, ctx
->drawable
, DONT_COPY
);
933 LOG("%p:ERROR:cannot clear drawable 0x%x from display %p", ctx
, ctx
->drawable
, ctx
->current
.dpy
);
937 avx2_input
.dst
= d
->shm
.m
;
938 avx2_input
.dst_width_pixs_n
= d
->width
;
939 avx2_input
.dst_line_bytes_n
= d
->bytes_per_line
;
940 avx2_input
.dst_height_lines_n
= d
->height
;
941 clearcolor_avx2(&avx2_input
);
953 ppixel
= d
->shm
.m
+ y
* d
->bytes_per_line
+ (x
<< 2);
967 * The smooth-interpolated vertex color from the fragment, including the alpha
968 * channel is multiplied by the texture values. Alpha not being 1 is very
969 * important. The resulting values from right above are then classically
970 * blended in the framebuffer.
973 * This is _not_ texture sampling with framebuffer pixel blending:
974 * - for a bgra src tex, this is only a rect cpy.
975 * - for a rgba src tex, this is only a rect pixel blending.
978 #define DST_START_X minmax_ctx_avx2.vsi[0]
979 #define DST_START_Y minmax_ctx_avx2.vsi[1]
980 #define DST_END_X minmax_ctx_avx2.vsi[4]
981 #define DST_END_Y minmax_ctx_avx2.vsi[5]
982 #define SRC_START_X minmax_ctx_avx2.vsi[2]
983 #define SRC_START_Y minmax_ctx_avx2.vsi[3]
984 #define SRC_END_X minmax_ctx_avx2.vsi[6]
985 #define SRC_END_Y minmax_ctx_avx2.vsi[7]
986 static void render_in_shm(struct ctx_t
*ctx
)
990 struct drawable_t
*d
;
992 union {/* 4 xmm regs */
993 GLfloat vsf
[4 * 4]; /* in */
994 s32 vsi
[4 * 4]; /* out */
996 u32 scale
[1 * 4]; /* in, 1 xmm reg */
997 } minmax_ctx_avx2
; /* should be packed already */
1000 texture2d
= ctx
->texs
.texture2d
;
1001 if (texture2d
== 0) /* default texture, let's make it empty */
1003 tex
= &ctx
->texs
.a
[texture2d
- 1];
1005 * heuristic: 1 pixel texture = transparent/solid gradient using vertex
1006 * color interpolation.We
1007 * The 1 pixel texture is usually white and the texture GL_MODULATE
1008 * will multiply the interpolated vertex color/alpha by this solid white
1009 * color actually passing verbatim the interpolated colors/alpha.
1011 if (tex
->width
== 1 && tex
->height
== 1) {
1012 //TRACE("gradient using vertex color/alpha interpolation detected, skipping rendering");
1013 TRACE("gradient using vertex color/alpha interpolation detected, skipping rendering");
1017 d
= get_synced_drawable(ctx
->current
.dpy
, ctx
->drawable
, false);
1019 LOG("%p:ERROR:cannot render drawable 0x%x from display %p", ctx
, ctx
->drawable
, ctx
->current
.dpy
);
1022 /* minmax-es of quad vs attrs */
1023 minmax_ctx_avx2
.vsf
[0] = ctx
->begin_end_blk
.quad
.vs
[0].plane
.x
;
1024 minmax_ctx_avx2
.vsf
[1] = ctx
->begin_end_blk
.quad
.vs
[0].plane
.y
;
1025 minmax_ctx_avx2
.vsf
[2] = ctx
->begin_end_blk
.quad
.vs
[0].tex
.s
;
1026 minmax_ctx_avx2
.vsf
[3] = ctx
->begin_end_blk
.quad
.vs
[0].tex
.t
;
1028 minmax_ctx_avx2
.vsf
[4] = ctx
->begin_end_blk
.quad
.vs
[1].plane
.x
;
1029 minmax_ctx_avx2
.vsf
[5] = ctx
->begin_end_blk
.quad
.vs
[1].plane
.y
;
1030 minmax_ctx_avx2
.vsf
[6] = ctx
->begin_end_blk
.quad
.vs
[1].tex
.s
;
1031 minmax_ctx_avx2
.vsf
[7] = ctx
->begin_end_blk
.quad
.vs
[1].tex
.t
;
1033 minmax_ctx_avx2
.vsf
[8] = ctx
->begin_end_blk
.quad
.vs
[2].plane
.x
;
1034 minmax_ctx_avx2
.vsf
[9] = ctx
->begin_end_blk
.quad
.vs
[2].plane
.y
;
1035 minmax_ctx_avx2
.vsf
[10] = ctx
->begin_end_blk
.quad
.vs
[2].tex
.s
;
1036 minmax_ctx_avx2
.vsf
[11] = ctx
->begin_end_blk
.quad
.vs
[2].tex
.t
;
1038 minmax_ctx_avx2
.vsf
[12] = ctx
->begin_end_blk
.quad
.vs
[3].plane
.x
;
1039 minmax_ctx_avx2
.vsf
[13] = ctx
->begin_end_blk
.quad
.vs
[3].plane
.y
;
1040 minmax_ctx_avx2
.vsf
[14] = ctx
->begin_end_blk
.quad
.vs
[3].tex
.s
;
1041 minmax_ctx_avx2
.vsf
[15] = ctx
->begin_end_blk
.quad
.vs
[3].tex
.t
;
1043 minmax_ctx_avx2
.scale
[0] = 1;
1044 minmax_ctx_avx2
.scale
[1] = 1;
1045 minmax_ctx_avx2
.scale
[2] = tex
->width
;
1046 minmax_ctx_avx2
.scale
[3] = tex
->height
;
1047 minmax_avx2(&minmax_ctx_avx2
);
1050 /* in opengl, x=0/y=0 is the bottom left of the texture */
1051 TRACE("AVX2RENDER:ctx=%p:drawable=0x%x:tex_width=%u:tex_height=%u", ctx
, ctx
->drawable
, tex
->width
, tex
->height
);
1052 TRACE("AVX2RENDER:ctx=%p:drawable=0x%x:dst_start_x=%u dst_end_x=%u src_start_x=%u src_end_x=%u", ctx
, ctx
->drawable
, DST_START_X
, DST_END_X
, SRC_START_X
, SRC_END_X
);
1053 TRACE("AVX2RENDER:ctx=%p:drawable=0x%x:dst_start_y=%u dst_end_y=%u src_start_y=%u src_end_y=%u", ctx
, ctx
->drawable
, DST_START_Y
, DST_END_Y
, SRC_START_Y
, SRC_END_Y
);
1054 /*--------------------------------------------------------------------*/
1055 /* we need to clamp to the drawable, just in case */
1056 if (d
->height
<= DST_START_Y
|| d
->width
<= DST_START_X
)
1058 if (d
->height
< DST_END_Y
) {
1059 DST_END_Y
= d
->height
;
1060 TRACE("AVX2RENDER:clamping dst_end_y to %u due to drawable size", DST_END_Y
);
1062 if (d
->width
< DST_END_X
) {
1063 DST_END_X
= d
->width
;
1064 TRACE("AVX2RENDER:clamping dst_end_x to %u due to drawable size", DST_END_X
);
1066 /*--------------------------------------------------------------------*/
1068 * We don't do texture sampling, just 1 to 1 pixel blending. Then we
1069 * adjust the SRC_ENDs.
1071 SRC_END_X
= SRC_START_X
+ DST_END_X
- DST_START_X
;
1072 SRC_END_Y
= SRC_START_Y
+ DST_END_Y
- DST_START_Y
;
1073 /*--------------------------------------------------------------------*/
1074 /* we need to clamp to the texture, just in case */
1075 if (tex
->height
<= SRC_START_Y
|| tex
->width
<= SRC_START_X
)
1077 if (tex
->height
< SRC_END_Y
) {
1078 DST_END_Y
= DST_START_Y
+ tex
->height
;
1079 TRACE("AVX2RENDER:clamping dst_end_y to %u due to texture size", DST_END_Y
);
1081 if (tex
->width
< SRC_END_X
) {
1082 DST_END_X
= DST_START_X
+ tex
->width
;
1083 TRACE("AVX2RENDER:clamping dst_end_x to %u due to texture size", DST_END_X
);
1085 /*--------------------------------------------------------------------*/
1086 if (tex
->format
== GL_BGRA
) {
1091 u32 dst_width_pixs_n
;
1092 u32 dst_line_pixs_n
;
1094 u32 src_line_pixs_n
;
1098 avx2_input
.dst
= d
->shm
.m
;
1099 avx2_input
.dst_x_offset
= DST_START_X
;
1100 avx2_input
.dst_y_offset
= DST_START_Y
;
1101 avx2_input
.dst_width_pixs_n
= DST_END_X
- DST_START_X
;
1102 avx2_input
.dst_line_pixs_n
= d
->bytes_per_line
>> 2;
1103 avx2_input
.src
= tex
->pixels
+
1104 ((SRC_START_Y
* tex
->width
+ SRC_START_X
) << 2);
1105 avx2_input
.src_line_pixs_n
= tex
->width
;
1106 avx2_input
.height_lines_n
= DST_END_Y
- DST_START_Y
;
1107 glTexXImage2D_avx2(&avx2_input
);
1109 } else { /* tex->format == GL_RGBA */
1112 u32 dst_adjust_bytes_n
;
1114 u32 src_adjust_bytes_n
;
1117 } alphablend_rgba_input_avx2
;
1119 /* the width and heiht driver values are DST_a_[XY] */
1120 alphablend_rgba_input_avx2
.dst
= d
->shm
.m
+ DST_START_Y
1121 * d
->bytes_per_line
+ (DST_START_X
<< 2);
1123 alphablend_rgba_input_avx2
.dst_adjust_bytes_n
=
1124 d
->bytes_per_line
- ((DST_END_X
- DST_START_X
) << 2);
1126 alphablend_rgba_input_avx2
.src
= tex
->pixels
+
1127 ((SRC_START_Y
* tex
->width
+ SRC_START_X
) << 2);
1129 alphablend_rgba_input_avx2
.src_adjust_bytes_n
=
1130 (tex
->width
- (DST_END_X
- DST_START_X
)) << 2;
1132 alphablend_rgba_input_avx2
.width_pixs_n
=
1133 DST_END_X
- DST_START_X
;
1134 alphablend_rgba_input_avx2
.height_lines_n
=
1135 DST_END_Y
- DST_START_Y
;
1136 alphablend_rgba_avx2(&alphablend_rgba_input_avx2
);
1140 static void render_in_shm(struct ctx_t
*ctx
)
1144 GLfloat src_primary_alpha_min
;
1145 GLfloat src_primary_alpha_max
;
1146 struct drawable_t
*d
;
1149 int dst_shm_bytes_per_line
;
1154 GLfloat dst_line_bytes_n
;
1159 GLfloat dst_start_x
;
1160 GLfloat dst_start_y
;
1163 GLfloat src_start_s
;
1164 GLfloat src_start_t
;
1167 GLfloat src_start_x
;
1168 GLfloat src_start_y
;
1173 texture2d
= ctx
->texs
.texture2d
;
1174 if (texture2d
== 0) /* default texture, let's make it empty */
1176 /*--------------------------------------------------------------------*/
1177 src_primary_alpha_min
= min_of_quad_alpha(ctx
);
1178 src_primary_alpha_max
= max_of_quad_alpha(ctx
);
1179 TRACE("alpha_min=%f alpha_max=%f", src_primary_alpha_min
, src_primary_alpha_max
);
1181 /*--------------------------------------------------------------------*/
1182 d
= get_synced_drawable(ctx
->current
.dpy
, ctx
->drawable
, false);
1184 LOG("%p:ERROR:cannot render drawable 0x%x from display %p", ctx
, ctx
->drawable
, ctx
->current
.dpy
);
1187 /*--------------------------------------------------------------------*/
1188 tex
= &ctx
->texs
.a
[texture2d
- 1];
1190 src_f
= tex
->format
;
1192 src_h
= tex
->height
;
1194 * The following actually gives use the quad orientation.
1195 * Those are 3D vertex coords, _not_ pixel indexes.
1196 * "end" is supposed to be the index of the pixel past the last
1199 dst_start_x
= min_of_quad_plane_xs(ctx
);
1200 dst_start_y
= min_of_quad_plane_ys(ctx
);
1201 dst_end_x
= max_of_quad_plane_xs(ctx
);
1202 dst_end_y
= max_of_quad_plane_ys(ctx
);
1204 src_start_s
= min_of_quad_tex_ss(ctx
);
1205 src_start_t
= min_of_quad_tex_ts(ctx
);
1206 src_end_s
= max_of_quad_tex_ss(ctx
);
1207 src_end_t
= max_of_quad_tex_ts(ctx
);
1209 src_start_x
= src_start_s
* (GLfloat
)src_w
;
1210 src_start_y
= src_start_t
* (GLfloat
)src_h
;
1211 src_end_x
= src_end_s
* (GLfloat
)src_w
;
1212 src_end_y
= src_end_t
* (GLfloat
)src_h
;
1213 /* in opengl, x=0/y=0 is the bottom left of the texture */
1214 TRACE("ctx=%p:drawable=0x%x:tex_width=%u:tex_height=%u", ctx
, ctx
->drawable
, tex
->width
, tex
->height
);
1215 TRACE("ctx=%p:drawable=0x%x:dst_start_x=%f dst_end_x=%f src_start_x=%f src_end_x=%f", ctx
, ctx
->drawable
, dst_start_x
, dst_end_x
, src_start_x
, src_end_x
);
1216 TRACE("ctx=%p:drawable=0x%x:dst_start_y=%f dst_end_y=%f src_start_y=%f src_end_y=%f", ctx
, ctx
->drawable
, dst_start_y
, dst_end_y
, src_start_y
, src_end_y
);
1217 /*--------------------------------------------------------------------*/
1219 dst_w
= (GLfloat
)d
->width
;
1220 dst_h
= (GLfloat
)d
->height
;
1221 dst_line_bytes_n
= (GLfloat
)d
->bytes_per_line
;
1222 /*--------------------------------------------------------------------*/
1224 * we need to clamp to the drawable, just in case, they are supposed to
1225 * be perfectly matched due to the orthogonal projection.
1227 if (dst_h
<= dst_start_y
|| dst_w
<= dst_start_x
)
1229 if (dst_h
< dst_end_y
) {
1231 TRACE("clamping dst_end_y to %f", dst_end_y
);
1233 if (dst_w
< dst_end_x
) {
1235 TRACE("clamping dst_end_x to %f", dst_end_x
);
1237 /*--------------------------------------------------------------------*/
1239 * heuristic: 1 pixel texture = transparent/solid gradient using vertex
1240 * color interpolation.We
1241 * The 1 pixel texture is usually white and the texture GL_MODULATE
1242 * will multiply the interpolated vertex color/alpha by this solid white
1243 * color actually passing verbatim the interpolated colors/alpha.
1245 if (src_w
== 1 && src_h
== 1) {
1246 TRACE("gradient using vertex color/alpha interpolation detected, skipping rendering");
1249 dst_y
= dst_start_y
;
1252 if (dst_y
>= dst_end_y
)
1255 dst_x
= dst_start_x
;
1260 if (dst_x
>= dst_end_x
)
1262 src_x
= src_start_x
+ dst_x
- dst_start_x
;
1263 src_y
= src_start_y
+ dst_y
- dst_start_y
;
1264 if (src_x
< src_end_x
&& src_y
< src_end_y
) {
1268 dst_pix
= dst
+ (int)(dst_y
* dst_line_bytes_n
1270 src_pix
= src
+ (int)((src_y
* src_w
+ src_x
)
1272 /* XXX: dst_f is presumed BGRA */
1273 if (src_f
== GL_RGBA
) {
1278 if (src_pix
[3] != 255) {
1279 src_alpha
= src_pix
[3];
1282 dst_color
= dst_pix
[0];
1283 src_color
= src_pix
[2];
1284 dst_color
= dst_color
* (1. - src_alpha
) + src_color
* src_alpha
;
1285 if (dst_color
> 255.)
1287 dst_pix
[0] = (u8
)dst_color
;
1289 dst_color
= dst_pix
[1];
1290 src_color
= src_pix
[1];
1291 dst_color
= dst_color
* (1. - src_alpha
) + src_color
* src_alpha
;
1292 if (dst_color
> 255.)
1294 dst_pix
[1] = (u8
)dst_color
;
1296 dst_color
= dst_pix
[2];
1297 src_color
= src_pix
[0];
1298 dst_color
= dst_color
* (1. - src_alpha
) + src_color
* src_alpha
;
1299 if (dst_color
> 255.)
1301 dst_pix
[2] = (u8
)dst_color
;
1303 dst_pix
[3] = src_pix
[3];
1305 dst_pix
[0]=src_pix
[2];
1306 dst_pix
[1]=src_pix
[1];
1307 dst_pix
[2]=src_pix
[0];
1308 dst_pix
[3]=src_pix
[3];
1310 } else if (src_f
== GL_BGRA
) {
1315 if (src_pix
[3] != 255) {
1316 src_alpha
= src_pix
[3];
1319 dst_color
= dst_pix
[0];
1320 src_color
= src_pix
[0];
1321 dst_color
= dst_color
* (1. - src_alpha
) + src_color
* src_alpha
;
1322 if (dst_color
> 255.)
1324 dst_pix
[0] = (u8
)dst_color
;
1326 dst_color
= dst_pix
[1];
1327 src_color
= src_pix
[1];
1328 dst_color
= dst_color
* (1. - src_alpha
) + src_color
* src_alpha
;
1329 if (dst_color
> 255.)
1331 dst_pix
[1] = (u8
)dst_color
;
1333 dst_color
= dst_pix
[2];
1334 src_color
= src_pix
[2];
1335 dst_color
= dst_color
* (1. - src_alpha
) + src_color
* src_alpha
;
1336 if (dst_color
> 255.)
1338 dst_pix
[2] = (u8
)dst_color
;
1340 dst_pix
[3] = src_pix
[3];
1342 dst_pix
[0]=src_pix
[0];
1343 dst_pix
[1]=src_pix
[1];
1344 dst_pix
[2]=src_pix
[2];
1345 dst_pix
[3]=src_pix
[3];