overkill 32 bits avx2 assembly code
[mini2dgl.git] / main.c
blobbe5d73956d1e36a9a7953dbe4bafb17a6cc6946a
1 #define _GNU_SOURCE
2 #include <stdbool.h>
3 #include <fcntl.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include <pthread.h>
8 #include <unistd.h>
9 #include <sys/file.h>
10 #include <sys/ipc.h>
11 #include <sys/shm.h>
12 #include <GL/internal/glx.h>
13 #include <X11/extensions/XShm.h>
14 /*----------------------------------------------------------------------------*/
15 #undef TRACE_ON
16 #define REDIRECT_LOG_TO_TRACE_FILE
17 /*----------------------------------------------------------------------------*/
18 #define u8 unsigned char
19 #define u16 unsigned short
20 #define u32 unsigned int
21 #define s32 int
22 #define loop for(;;)
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 /*----------------------------------------------------------------------------*/
34 #ifdef AVX2_32BITS
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);
39 #endif
40 /*----------------------------------------------------------------------------*/
41 /*{{{ ipc tracker */
42 /* stale ipc... wonder why this is still a thing */
43 static struct ipcs_tracker {
44 struct {
45 int id;
46 void *mem;
47 } *slots;
48 unsigned int n;
49 } ipcs_tracker;
50 static void ipc_track(int id, void *mem)
52 unsigned int i;
53 /* try to get an existing slot */
54 i = 0;
55 loop {
56 if (i == ipcs_tracker.n || ipcs_tracker.slots[i].id == -1)
57 break;
58 ++i;
60 if (i == ipcs_tracker.n) {
61 ipcs_tracker.slots = realloc(ipcs_tracker.slots,
62 sizeof(*ipcs_tracker.slots) * (i + 1));
63 ++(ipcs_tracker.n);
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)
71 unsigned int i;
73 i = 0;
74 loop {
75 if (i == ipcs_tracker.n)
76 return;
77 if (ipcs_tracker.slots[i].id == id)
78 break;
79 ++i;
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)
86 unsigned int i;
88 i = 0;
89 loop {
90 if (i == ipcs_tracker.n)
91 return;
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);
98 ++i;
101 static void ipcs_tracker_init(void)
103 int r;
105 ipcs_tracker.slots = 0;
106 ipcs_tracker.n = 0;
107 r = atexit(ipcs_rm);
108 if (r != 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");
112 /*}}}*/
113 /*{{{ global lock */
114 #define LOCK lock((void*)__func__)
115 #define UNLOCK unlock((void*)__func__)
116 static void lock(void *fn)
118 int r;
120 r = pthread_mutex_lock(&global_mutex);
121 if (r != 0) {
122 LOG("ERROR:from %s:unable to lock the global mutex", fn);
123 abort();
126 static void unlock(void *fn)
128 int r;
130 r = pthread_mutex_unlock(&global_mutex);
131 if (r != 0) {
132 LOG("ERROR:from %s:unable to unlock the global mutex", fn);
133 abort();
136 /*}}}*/
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
142 struct drawable_t {
143 int id; /* -1 means available slot */
144 int bytes_per_line;
145 int height;
146 int width;
147 int depth;
148 Visual *visual;
149 int screen;
150 struct {
151 int id; /* if -1, no shm segment attached */
152 u8 *m;
153 } shm;
155 static struct {
156 struct drawable_t *slots;
157 int n;
158 } ds;
159 static void ds_init(void)
161 ds.slots = 0;
162 ds.n = 0;
164 static struct drawable_t *ds_get(int id)
166 int d;
168 d = 0;
169 loop {
170 if (ds.n == d)
171 return 0;
172 if (ds.slots[d].id == id)
173 return &ds.slots[d];
174 ++d;
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 {
189 Display *dpy;
190 int slot;
192 static void *drawable_destroy_listener(void *arg)
194 struct drawable_destroy_listener_arg *a;
196 a = arg;
197 loop {
198 XEvent e;
200 memset(&e, 0, sizeof(e));
201 XNextEvent(a->dpy, &e);
202 if (e.type == DestroyNotify) {
203 int s;
205 LOCK;
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);
209 ds_destroy(a->slot);
210 free(a);
211 UNLOCK;
212 pthread_exit(0);
214 UNLOCK;
218 static void drawable_destroy_listener_spawn(Display *dpy, int slot)
220 Display *local_dpy;
221 XSetWindowAttributes attrs;
222 int r;
223 struct drawable_destroy_listener_arg *arg;
224 pthread_t listener;
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);
229 return;
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,
235 &attrs);
236 arg = malloc(sizeof(*arg));
237 arg->dpy = local_dpy;
238 arg->slot = slot;
239 TRACE("spawing destroy listener thread for drawable 0x%x", ds.slots[slot].id);
240 r = pthread_create(&listener, 0, drawable_destroy_listener, arg);
241 if (r != 0) {
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);
243 free(arg);
244 return;
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;
252 int s;
254 s = 0;
255 loop {
256 if (s == ds.n) {
257 ds.slots = realloc(ds.slots, sizeof(*ds.slots)
258 * (ds.n + 1));
259 ds.slots[s].id = -1;
260 ++(ds.n);
261 break;
263 if (ds.slots[s].id == -1)
264 break;
265 ++s;
267 new = &ds.slots[s];
268 new->shm.id = shmget(IPC_PRIVATE, bytes_per_line * height,
269 IPC_CREAT|0777);
270 if (new->shm.id == -1) {
271 LOG("ERROR:unable to create a new ipc shared memory segment for drawable 0x%x", id);
272 return 0;
274 new->shm.m = shmat(new->shm.id, 0, 0);
275 if (new->shm.m == 0) {
276 shmctl(new->shm.id, IPC_RMID, 0);
277 new->shm.id = -1;
278 return 0;
280 ipc_track(new->shm.id, new->shm.m);
281 new->bytes_per_line = bytes_per_line;
282 new->height = height;
283 new->width = width;
284 new->depth = depth;
285 new->visual = visual;
286 new->screen = screen;
287 new->id = id;
288 drawable_destroy_listener_spawn(dpy, s);
289 return new;
291 /*}}}*/
292 /*{{{ init/fini entry/leave */
293 static void init(void)
295 int r;
297 trace_init();
298 r = pthread_mutex_init(&global_mutex, 0);
299 if (r != 0) {
300 LOG("ERROR:unable to init the global mutex");
301 abort();
302 } else
303 LOG("global mutex initialized");
304 r = pthread_key_create(&thd_data_key, thd_data_cleanup);
305 if (r != 0) {
306 LOG("ERROR:unable to create the thread data key");
307 abort();
308 } else
309 TRACE("thread data key created");
310 glx_extension_enabled = false;
311 ipcs_tracker_init();
312 ds_init();
314 static void enter(void *fn, int *old_cancel_state)
316 int r;
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);
321 if (r != 0)
322 LOG("WARNING:from %s:unable to disable thread cancellation", fn);
323 lock(fn);
325 static void leave(void *fn, int old_cancel_state)
327 int r;
329 unlock(fn);
330 /* we failed to set the cancel state on entry, don't touch it */
331 if (old_cancel_state == -1)
332 return;
333 r = pthread_setcancelstate(old_cancel_state, 0);
334 if (r != 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)
344 /*}}}*/
345 /*{{{ misc types */
346 struct tex_t {
347 bool bound;
348 GLenum target;
349 u8 *pixels;
350 GLuint width;
351 GLuint height;
352 GLenum format;
353 struct {
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 */
357 struct {
358 GLint align; /* initial value is 4 */
359 } write_to_client;
361 struct ctx_t { /* it is inited with zeros, cleanup once unused */
362 bool was_already_made_current;
363 struct {
364 void *dpy; /* XXX: for record only, in theory, do not use */
365 XVisualInfo visinf;
366 } create;
367 GLXDrawable drawable;
368 GLXDrawable read; /* the drawable to read from */
369 struct { /* set initially to the window size of the drawable */
370 GLint x;
371 GLint y;
372 GLint width;
373 GLint height;
374 } vp; /* viewport */
375 struct {
376 unsigned int current;
378 struct { /* record the last glTranslate */
379 float x;
380 float y;
381 float z;
382 } mv_translate;
383 double mv[4*4]; /* model view */
385 struct { /* record the last glOrtho */
386 double left;
387 double right;
388 double bottom;
389 double top;
390 double near;
391 double far;
392 } proj_ortho;
393 double proj[4*4]; /* projection */
395 double tex[4*4]; /* texture */
396 double col[4*4]; /* color */
397 } mtxs; /* matrixes */
398 struct {
399 float red;
400 float green;
401 float blue;
402 float alpha;
403 } clear_color;
404 /* defines the default vertex primary color attribute */
405 struct {
406 float red;
407 float green;
408 float blue;
409 float alpha;
410 } color;
411 bool destroyed;
412 struct {
413 bool attached;
414 pthread_t thd;
415 Display *dpy; /* display at glXMakeCurrent */
416 } current;
417 /* the index in the array + 1 is actually the name of the texture */
418 struct {
419 struct tex_t *a;
420 GLsizei n;
422 * texture name which is bound to the GL_TEXTURE_2D on the
423 * active texture unit, 0 is the default texture.
425 GLuint texture2d;
426 } texs;
427 struct {
428 bool recording;
429 /* XXX: we should record also the current color attribute */
430 struct {
431 struct {
432 struct { /* between 0.0 and 1.0 */
433 /* opengl = top->bottom */
434 GLfloat s;
435 GLfloat t;
436 } tex;
437 struct { /* viewport "mapped" */
438 GLfloat x;
439 GLfloat y;
440 } plane;
441 struct {
442 GLfloat red;
443 GLfloat green;
444 GLfloat blue;
445 GLfloat alpha;
446 } color;
447 } vs[4];
448 int i; /* current vs for coords recording */
449 } quad;
450 } begin_end_blk;
451 bool blending_enabled;
453 struct thd_data_t {
454 struct ctx_t *ctx;
456 /*}}}*/
457 /*{{{ main */
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);
463 if (thd_data == 0) {
464 int r;
466 thd_data = calloc(1, sizeof(*thd_data));
467 r = pthread_setspecific(thd_data_key, thd_data);
468 if (r != 0) {
469 LOG("ERROR:unable to attach self thread specific data");
470 abort();
471 } else {
472 TRACE("%p thd_data attached to self thread", thd_data);
475 return thd_data;
477 static void self_detach_ctx_with(Display *dpy, struct thd_data_t *self_data)
479 pthread_t self;
481 self = pthread_self();
482 if (self_data->ctx == 0)
483 return;
484 /* sanity checks */
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);
487 abort();
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);
492 abort();
494 self_data->ctx->current.thd = 0;
495 self_data->ctx->current.dpy = 0;
496 self_data->ctx->current.attached = false;
497 self_data->ctx = 0;
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;
506 pthread_t self;
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);
512 abort();
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);
517 abort();
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)
526 GLsizei i;
528 i = 0;
529 loop {
530 if (i == ctx->texs.n)
531 break;
532 free(ctx->texs.a[i].pixels);
533 ++i;
535 free(ctx->texs.a);
536 free(ctx);
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
541 * global mutex
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;
547 pthread_t self;
549 self = pthread_self();
550 self_data = key_value;
551 LOCK;
552 if (self_data->ctx == 0)
553 TRACE("the cancelled thread 0x%x has no current ctx", self);
554 else {
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);
559 UNLOCK;
561 static GLfloat min_of_quad_plane_xs(struct ctx_t *ctx)
563 GLfloat x;
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;
571 return x;
573 static GLfloat min_of_quad_plane_ys(struct ctx_t *ctx)
575 GLfloat y;
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;
583 return y;
585 static GLfloat max_of_quad_plane_xs(struct ctx_t *ctx)
587 GLfloat x;
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;
595 return x;
597 static GLfloat max_of_quad_plane_ys(struct ctx_t *ctx)
599 GLfloat y;
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;
607 return y;
609 static GLfloat min_of_quad_tex_ss(struct ctx_t *ctx)
611 GLfloat s;
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;
619 return s;
621 static GLfloat min_of_quad_tex_ts(struct ctx_t *ctx)
623 GLfloat t;
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;
631 return t;
633 static GLfloat max_of_quad_tex_ss(struct ctx_t *ctx)
635 GLfloat s;
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;
643 return s;
645 static GLfloat max_of_quad_tex_ts(struct ctx_t *ctx)
647 GLfloat t;
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;
655 return t;
657 static GLfloat max_of_quad_alpha(struct ctx_t *ctx)
659 GLfloat alpha;
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;
667 return alpha;
669 static GLfloat min_of_quad_alpha(struct ctx_t *ctx)
671 GLfloat alpha;
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;
679 return alpha;
681 static void ctx_viewport_set_to_drawable_sz(Display *dpy, struct ctx_t *ctx)
683 Status r;
684 XWindowAttributes attrs;
686 memset(&attrs, 0, sizeof(attrs));
687 r = XGetWindowAttributes(dpy, ctx->drawable, &attrs);
688 if (r == 0) {
689 LOG("%p:ERROR:unable to get the attributes of drawable 0x%x from display %p", ctx, ctx->drawable, dpy);
690 ctx->vp.x = 0;
691 ctx->vp.y = 0;
692 ctx->vp.width = 0;
693 ctx->vp.height = 0;
694 } else {
695 ctx->vp.x = 0;
696 ctx->vp.y = 0;
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
705 * buffer command.
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)
713 int new_shm_id;
714 u8 *new_shm_m;
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");
718 if (no_cpy) {
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");
722 goto meta_update;
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);
725 } else {
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");
730 goto meta_update;
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,
736 IPC_CREAT|0777);
737 if (new_shm_id == -1) {
738 LOG("ERROR:unable to create a new ipc shared memory segment, keeping the old buffer");
739 return false;
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");
745 return false;
747 ipc_track(new_shm_id, new_shm_m);
749 if (!no_cpy) {
750 int min_height;
751 int min_width;
752 #ifdef AVX2_32BITS
753 struct {
754 void *dst;
755 u32 dst_x_offset;
756 u32 dst_y_offset;
757 u32 dst_width_pixs_n;
758 u32 dst_line_pixs_n;
759 void *src;
760 u32 src_line_pixs_n;
761 u32 height_lines_n;
762 } avx2_input;
763 #else
764 int x;
765 int y;
766 #endif
768 /* construct the intersection of the new and old rectangles */
769 if (d->height > new_height)
770 min_height = new_height;
771 else
772 min_height = d->height;
773 if (d->width > new_width)
774 min_width = new_width;
775 else
776 min_width = d->width;
777 TRACE("copying the intersection h/w=%d/%d\n", min_height, min_width);
778 #ifdef AVX2_32BITS
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);
788 #else
789 y = 0;
790 loop {
791 if (y == min_height)
792 break;
793 x = 0;
794 loop {
795 u32 *old_pix;
796 u32 *new_pix;
798 if (x == min_width)
799 break;
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];
805 ++x;
807 ++y;
809 #endif
811 TRACE("getting rid of the previous ipc shared memory segment");
812 shmdt(d->shm.m);
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;
817 meta_update:
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;
824 return true;
826 static struct drawable_t *get_synced_drawable_noxlock(Display *dpy, int id,
827 bool no_cpy)
829 struct drawable_t *d;
830 Status r;
831 XWindowAttributes attrs;
832 XImage *img;
833 XShmSegmentInfo info;
835 d = 0;
837 memset(&attrs, 0, sizeof(attrs));
838 r = XGetWindowAttributes(dpy, id, &attrs);
839 if (r == 0) {
840 LOG("ERROR:unable to get the attributes of drawable 0x%x from display %p", id, dpy);
841 goto exit;
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);
846 if (img == 0) {
847 LOG("ERROR:unable to get the XSHM image bytes per line for drawable 0x%x from display %p", id, dpy);
848 goto exit;
850 d = ds_get(id);
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));
855 if (d == 0) {
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 */
861 bool shm_adjust_ok;
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);
866 if (!shm_adjust_ok)
867 d = 0;
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,
871 IPC_CREAT|0777);
872 if (d->shm.id == -1) {
873 LOG("ERROR:unable to create a new ipc shared memory segment for drawable 0x%x", id);
874 d = 0;
876 d->shm.m = shmat(d->shm.id, 0, 0);
877 if (d->shm.m == 0) {
878 shmctl(d->shm.id, IPC_RMID, 0);
879 d->shm.id = -1;
880 LOG("ERROR:unable to attached memory to ipc shared memory segment id=%d for drawable 0x%x", d->shm.id, id);
881 d = 0;
883 ipc_track(d->shm.id, d->shm.m);
885 destroy_shm_img:
886 XDestroyImage(img);
887 exit:
888 return d;
890 static struct drawable_t *get_synced_drawable(Display *dpy, int id, bool no_cpy)
892 struct drawable_t *d;
894 XLockDisplay(dpy);
895 d = get_synced_drawable_noxlock(dpy, id, no_cpy);
896 XUnlockDisplay(dpy);
897 return d;
899 #define DONT_COPY true
900 static void color_buffer_clear(struct ctx_t *ctx)
902 struct drawable_t *d;
903 #ifdef AVX2_32BITS
904 struct {
905 void *dst;
906 u32 dst_width_pixs_n;
907 u32 dst_line_bytes_n;
908 u32 dst_height_lines_n;
909 } avx2_input;
910 TRACE("%p:clearing the color buffer:b=0x%02x:g=0x%02x:r=0x%02x:a=0x%02x", ctx, 0x25, 0x25, 0x25, 0xff);
911 #else
912 u8 b;
913 u8 g;
914 u8 r;
915 u8 a;
916 unsigned int x;
917 unsigned int y;
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);
925 b = 0x25;
926 g = 0x25;
927 r = 0x25;
928 a = 0x25;
929 TRACE("%p:clearing the color buffer:b=0x%02x:g=0x%02x:r=0x%02x:a=0x%02x", ctx, b, g, r, a);
930 #endif
931 d = get_synced_drawable(ctx->current.dpy, ctx->drawable, DONT_COPY);
932 if (d == 0) {
933 LOG("%p:ERROR:cannot clear drawable 0x%x from display %p", ctx, ctx->drawable, ctx->current.dpy);
934 return;
936 #ifdef AVX2_32BITS
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);
942 #else
943 y = 0;
944 loop {
945 if (y == d->height)
946 break;
947 x = 0;
948 loop {
949 u8 *ppixel;
951 if (x == d->width)
952 break;
953 ppixel = d->shm.m + y * d->bytes_per_line + (x << 2);
954 /* BGRA */
955 ppixel[0] = b;
956 ppixel[1] = g;
957 ppixel[2] = r;
958 ppixel[3] = a;
959 ++x;
961 ++y;
963 #endif
965 #undef DONT_COPY
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.
977 #ifdef AVX2_32BITS
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)
988 GLuint texture2d;
989 struct tex_t *tex;
990 struct drawable_t *d;
991 struct {
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 */
998 s32 dst_y;
1000 texture2d = ctx->texs.texture2d;
1001 if (texture2d == 0) /* default texture, let's make it empty */
1002 return;
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");
1014 return;
1017 d = get_synced_drawable(ctx->current.dpy, ctx->drawable, false);
1018 if (d == 0) {
1019 LOG("%p:ERROR:cannot render drawable 0x%x from display %p", ctx, ctx->drawable, ctx->current.dpy);
1020 return;
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);
1048 /* ----- */
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)
1057 return;
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)
1076 return;
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) {
1087 struct {
1088 u8 *dst;
1089 u32 dst_x_offset;
1090 u32 dst_y_offset;
1091 u32 dst_width_pixs_n;
1092 u32 dst_line_pixs_n;
1093 u8 *src;
1094 u32 src_line_pixs_n;
1095 u32 height_lines_n;
1096 } avx2_input;
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 */
1110 struct {
1111 u8 *dst;
1112 u32 dst_adjust_bytes_n;
1113 u8 *src;
1114 u32 src_adjust_bytes_n;
1115 u32 width_pixs_n;
1116 u32 height_lines_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);
1139 #else
1140 static void render_in_shm(struct ctx_t *ctx)
1142 GLuint texture2d;
1143 struct tex_t *tex;
1144 GLfloat src_primary_alpha_min;
1145 GLfloat src_primary_alpha_max;
1146 struct drawable_t *d;
1147 int dst_shm_width;
1148 int dst_shm_height;
1149 int dst_shm_bytes_per_line;
1150 size_t dst_shm_sz;
1151 u8 *dst;
1152 GLfloat dst_w;
1153 GLfloat dst_h;
1154 GLfloat dst_line_bytes_n;
1155 u8 *src;
1156 GLfloat src_w;
1157 GLfloat src_h;
1158 GLenum src_f;
1159 GLfloat dst_start_x;
1160 GLfloat dst_start_y;
1161 GLfloat dst_end_x;
1162 GLfloat dst_end_y;
1163 GLfloat src_start_s;
1164 GLfloat src_start_t;
1165 GLfloat src_end_s;
1166 GLfloat src_end_t;
1167 GLfloat src_start_x;
1168 GLfloat src_start_y;
1169 GLfloat src_end_x;
1170 GLfloat src_end_y;
1171 GLfloat dst_y;
1173 texture2d = ctx->texs.texture2d;
1174 if (texture2d == 0) /* default texture, let's make it empty */
1175 return;
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);
1183 if (d == 0) {
1184 LOG("%p:ERROR:cannot render drawable 0x%x from display %p", ctx, ctx->drawable, ctx->current.dpy);
1185 return;
1187 /*--------------------------------------------------------------------*/
1188 tex = &ctx->texs.a[texture2d - 1];
1189 src = tex->pixels;
1190 src_f = tex->format;
1191 src_w = tex->width;
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
1197 * fragment pixel.
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 /*--------------------------------------------------------------------*/
1218 dst = d->shm.m;
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)
1228 return;
1229 if (dst_h < dst_end_y) {
1230 dst_end_y = dst_h;
1231 TRACE("clamping dst_end_y to %f", dst_end_y);
1233 if (dst_w < dst_end_x) {
1234 dst_end_x = dst_w;
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");
1247 return;
1249 dst_y = dst_start_y;
1250 loop {
1251 GLfloat dst_x;
1252 if (dst_y >= dst_end_y)
1253 break;
1255 dst_x = dst_start_x;
1256 loop {
1257 GLfloat src_x;
1258 GLfloat src_y;
1260 if (dst_x >= dst_end_x)
1261 break;
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) {
1265 u8 *dst_pix;
1266 u8 *src_pix;
1268 dst_pix = dst + (int)(dst_y * dst_line_bytes_n
1269 + dst_x * 4);
1270 src_pix = src + (int)((src_y * src_w + src_x)
1271 * 4);
1272 /* XXX: dst_f is presumed BGRA */
1273 if (src_f == GL_RGBA) {
1274 GLfloat dst_color;
1275 GLfloat src_color;
1276 GLfloat src_alpha;
1278 if (src_pix[3] != 255) {
1279 src_alpha = src_pix[3];
1280 src_alpha /= 255.;
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.)
1286 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.)
1293 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.)
1300 dst_color = 255.;
1301 dst_pix[2] = (u8)dst_color;
1303 dst_pix[3] = src_pix[3];
1304 } else {
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) {
1311 GLfloat dst_color;
1312 GLfloat src_color;
1313 GLfloat src_alpha;
1315 if (src_pix[3] != 255) {
1316 src_alpha = src_pix[3];
1317 src_alpha /= 255.;
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.)
1323 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.)
1330 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.)
1337 dst_color = 255.;
1338 dst_pix[2] = (u8)dst_color;
1340 dst_pix[3] = src_pix[3];
1341 } else {
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];
1349 ++dst_x;
1351 ++dst_y;
1354 #endif
1355 /*}}}*/
1356 #include "gl.c"
1357 #include "glx.c"