1/*
2 * Copyright © 2014 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include "glamor_priv.h"
24#include "glamor_transform.h"
25#include "glamor_transfer.h"
26
27glamor_program  fill_spans_progs[4];
28
29static const glamor_facet glamor_facet_fillspans_130 = {
30    .name = "fill_spans",
31    .version = 130,
32    .vs_vars =  "attribute vec3 primitive;\n",
33    .vs_exec = ("       vec2 pos = vec2(primitive.z,1) * vec2(gl_VertexID&1, (gl_VertexID&2)>>1);\n"
34                GLAMOR_POS(gl_Position, (primitive.xy + pos))),
35};
36
37static const glamor_facet glamor_facet_fillspans_120 = {
38    .name = "fill_spans",
39    .vs_vars =  "attribute vec2 primitive;\n",
40    .vs_exec = ("       vec2 pos = vec2(0,0);\n"
41                GLAMOR_POS(gl_Position, primitive.xy)),
42};
43
44static Bool
45glamor_fill_spans_gl(DrawablePtr drawable,
46                     GCPtr gc,
47                     int n, DDXPointPtr points, int *widths, int sorted)
48{
49    ScreenPtr screen = drawable->pScreen;
50    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
51    PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
52    glamor_pixmap_private *pixmap_priv;
53    glamor_program *prog;
54    int off_x, off_y;
55    GLshort *v;
56    char *vbo_offset;
57    int c;
58    int box_index;
59    Bool ret = FALSE;
60
61    pixmap_priv = glamor_get_pixmap_private(pixmap);
62    if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv))
63        goto bail;
64
65    glamor_make_current(glamor_priv);
66
67    if (glamor_glsl_has_ints(glamor_priv)) {
68        prog = glamor_use_program_fill(pixmap, gc, &glamor_priv->fill_spans_program,
69                                       &glamor_facet_fillspans_130);
70
71        if (!prog)
72            goto bail;
73
74        /* Set up the vertex buffers for the points */
75
76        v = glamor_get_vbo_space(drawable->pScreen, n * (4 * sizeof (GLshort)), &vbo_offset);
77
78        glEnableVertexAttribArray(GLAMOR_VERTEX_POS);
79        glVertexAttribDivisor(GLAMOR_VERTEX_POS, 1);
80        glVertexAttribPointer(GLAMOR_VERTEX_POS, 3, GL_SHORT, GL_FALSE,
81                              4 * sizeof (GLshort), vbo_offset);
82
83        for (c = 0; c < n; c++) {
84            v[0] = points->x;
85            v[1] = points->y;
86            v[2] = *widths++;
87            points++;
88            v += 4;
89        }
90
91        glamor_put_vbo_space(screen);
92    } else {
93        prog = glamor_use_program_fill(pixmap, gc, &glamor_priv->fill_spans_program,
94                                       &glamor_facet_fillspans_120);
95
96        if (!prog)
97            goto bail;
98
99        /* Set up the vertex buffers for the points */
100
101        v = glamor_get_vbo_space(drawable->pScreen, n * 8 * sizeof (short), &vbo_offset);
102
103        glEnableVertexAttribArray(GLAMOR_VERTEX_POS);
104        glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_SHORT, GL_FALSE,
105                              2 * sizeof (short), vbo_offset);
106
107        for (c = 0; c < n; c++) {
108            v[0] = points->x;           v[1] = points->y;
109            v[2] = points->x;           v[3] = points->y + 1;
110            v[4] = points->x + *widths; v[5] = points->y + 1;
111            v[6] = points->x + *widths; v[7] = points->y;
112
113            widths++;
114            points++;
115            v += 8;
116        }
117
118        glamor_put_vbo_space(screen);
119    }
120
121    glEnable(GL_SCISSOR_TEST);
122
123    glamor_pixmap_loop(pixmap_priv, box_index) {
124        int nbox = RegionNumRects(gc->pCompositeClip);
125        BoxPtr box = RegionRects(gc->pCompositeClip);
126
127        if (!glamor_set_destination_drawable(drawable, box_index, FALSE, FALSE,
128                                             prog->matrix_uniform, &off_x, &off_y))
129            goto bail;
130
131        while (nbox--) {
132            glScissor(box->x1 + off_x,
133                      box->y1 + off_y,
134                      box->x2 - box->x1,
135                      box->y2 - box->y1);
136            box++;
137            if (glamor_glsl_has_ints(glamor_priv))
138                glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, n);
139            else {
140                glamor_glDrawArrays_GL_QUADS(glamor_priv, n);
141            }
142        }
143    }
144
145    ret = TRUE;
146
147bail:
148    glDisable(GL_SCISSOR_TEST);
149    if (glamor_glsl_has_ints(glamor_priv))
150        glVertexAttribDivisor(GLAMOR_VERTEX_POS, 0);
151    glDisableVertexAttribArray(GLAMOR_VERTEX_POS);
152
153    return ret;
154}
155
156static void
157glamor_fill_spans_bail(DrawablePtr drawable,
158                       GCPtr gc,
159                       int n, DDXPointPtr points, int *widths, int sorted)
160{
161    if (glamor_prepare_access(drawable, GLAMOR_ACCESS_RW) &&
162        glamor_prepare_access_gc(gc)) {
163        fbFillSpans(drawable, gc, n, points, widths, sorted);
164    }
165    glamor_finish_access_gc(gc);
166    glamor_finish_access(drawable);
167}
168
169void
170glamor_fill_spans(DrawablePtr drawable,
171                  GCPtr gc,
172                  int n, DDXPointPtr points, int *widths, int sorted)
173{
174    if (glamor_fill_spans_gl(drawable, gc, n, points, widths, sorted))
175        return;
176    glamor_fill_spans_bail(drawable, gc, n, points, widths, sorted);
177}
178
179static Bool
180glamor_get_spans_gl(DrawablePtr drawable, int wmax,
181                    DDXPointPtr points, int *widths, int count, char *dst)
182{
183    ScreenPtr screen = drawable->pScreen;
184    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
185    PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
186    glamor_pixmap_private *pixmap_priv;
187    int box_index;
188    int n;
189    char *d;
190    int off_x, off_y;
191    const struct glamor_format *f = glamor_format_for_pixmap(pixmap);
192
193    pixmap_priv = glamor_get_pixmap_private(pixmap);
194    if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv))
195        goto bail;
196
197    glamor_get_drawable_deltas(drawable, pixmap, &off_x, &off_y);
198
199    glamor_make_current(glamor_priv);
200
201    glamor_pixmap_loop(pixmap_priv, box_index) {
202        BoxPtr                  box = glamor_pixmap_box_at(pixmap_priv, box_index);
203        glamor_pixmap_fbo       *fbo = glamor_pixmap_fbo_at(pixmap_priv, box_index);
204
205        glBindFramebuffer(GL_FRAMEBUFFER, fbo->fb);
206        glPixelStorei(GL_PACK_ALIGNMENT, 4);
207
208        d = dst;
209        for (n = 0; n < count; n++) {
210            int x1 = points[n].x + off_x;
211            int y = points[n].y + off_y;
212            int w = widths[n];
213            int x2 = x1 + w;
214            char *l;
215
216            l = d;
217            d += PixmapBytePad(w, drawable->depth);
218
219            /* clip */
220            if (x1 < box->x1) {
221                l += (box->x1 - x1) * (drawable->bitsPerPixel >> 3);
222                x1 = box->x1;
223            }
224            if (x2 > box->x2)
225                x2 = box->x2;
226
227            if (x1 >= x2)
228                continue;
229            if (y < box->y1)
230                continue;
231            if (y >= box->y2)
232                continue;
233
234            glReadPixels(x1 - box->x1, y - box->y1, x2 - x1, 1,
235                         f->format, f->type, l);
236        }
237    }
238
239    return TRUE;
240bail:
241    return FALSE;
242}
243
244static void
245glamor_get_spans_bail(DrawablePtr drawable, int wmax,
246                 DDXPointPtr points, int *widths, int count, char *dst)
247{
248    if (glamor_prepare_access(drawable, GLAMOR_ACCESS_RO))
249        fbGetSpans(drawable, wmax, points, widths, count, dst);
250    glamor_finish_access(drawable);
251}
252
253void
254glamor_get_spans(DrawablePtr drawable, int wmax,
255                 DDXPointPtr points, int *widths, int count, char *dst)
256{
257    if (glamor_get_spans_gl(drawable, wmax, points, widths, count, dst))
258        return;
259    glamor_get_spans_bail(drawable, wmax, points, widths, count, dst);
260}
261
262static Bool
263glamor_set_spans_gl(DrawablePtr drawable, GCPtr gc, char *src,
264                    DDXPointPtr points, int *widths, int numPoints, int sorted)
265{
266    ScreenPtr screen = drawable->pScreen;
267    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
268    PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
269    glamor_pixmap_private *pixmap_priv;
270    const struct glamor_format *f = glamor_format_for_pixmap(pixmap);
271    int box_index;
272    int n;
273    char *s;
274    int off_x, off_y;
275
276    pixmap_priv = glamor_get_pixmap_private(pixmap);
277    if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv))
278        goto bail;
279
280    if (gc->alu != GXcopy)
281        goto bail;
282
283    if (!glamor_pm_is_solid(gc->depth, gc->planemask))
284        goto bail;
285
286    glamor_get_drawable_deltas(drawable, pixmap, &off_x, &off_y);
287
288    glamor_make_current(glamor_priv);
289
290    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
291
292    glamor_pixmap_loop(pixmap_priv, box_index) {
293        BoxPtr              box = glamor_pixmap_box_at(pixmap_priv, box_index);
294        glamor_pixmap_fbo  *fbo = glamor_pixmap_fbo_at(pixmap_priv, box_index);
295
296        glamor_bind_texture(glamor_priv, GL_TEXTURE0, fbo, TRUE);
297
298        s = src;
299        for (n = 0; n < numPoints; n++) {
300
301            BoxPtr      clip_box = RegionRects(gc->pCompositeClip);
302            int         nclip_box = RegionNumRects(gc->pCompositeClip);
303            int         w = widths[n];
304            int         y = points[n].y;
305            int         x = points[n].x;
306
307            while (nclip_box--) {
308                int x1 = x;
309                int x2 = x + w;
310                int y1 = y;
311                char *l = s;
312
313                /* clip to composite clip */
314                if (x1 < clip_box->x1) {
315                    l += (clip_box->x1 - x1) * (drawable->bitsPerPixel >> 3);
316                    x1 = clip_box->x1;
317                }
318                if (x2 > clip_box->x2)
319                    x2 = clip_box->x2;
320
321                if (y < clip_box->y1)
322                    continue;
323                if (y >= clip_box->y2)
324                    continue;
325
326                /* adjust to pixmap coordinates */
327                x1 += off_x;
328                x2 += off_x;
329                y1 += off_y;
330
331                if (x1 < box->x1) {
332                    l += (box->x1 - x1) * (drawable->bitsPerPixel >> 3);
333                    x1 = box->x1;
334                }
335                if (x2 > box->x2)
336                    x2 = box->x2;
337
338                if (x1 >= x2)
339                    continue;
340                if (y1 < box->y1)
341                    continue;
342                if (y1 >= box->y2)
343                    continue;
344
345                glTexSubImage2D(GL_TEXTURE_2D, 0,
346                                x1 - box->x1, y1 - box->y1, x2 - x1, 1,
347                                f->format, f->type,
348                                l);
349            }
350            s += PixmapBytePad(w, drawable->depth);
351        }
352    }
353
354    return TRUE;
355
356bail:
357    return FALSE;
358}
359
360static void
361glamor_set_spans_bail(DrawablePtr drawable, GCPtr gc, char *src,
362                      DDXPointPtr points, int *widths, int numPoints, int sorted)
363{
364    if (glamor_prepare_access(drawable, GLAMOR_ACCESS_RW) && glamor_prepare_access_gc(gc))
365        fbSetSpans(drawable, gc, src, points, widths, numPoints, sorted);
366    glamor_finish_access_gc(gc);
367    glamor_finish_access(drawable);
368}
369
370void
371glamor_set_spans(DrawablePtr drawable, GCPtr gc, char *src,
372                 DDXPointPtr points, int *widths, int numPoints, int sorted)
373{
374    if (glamor_set_spans_gl(drawable, gc, src, points, widths, numPoints, sorted))
375        return;
376    glamor_set_spans_bail(drawable, gc, src, points, widths, numPoints, sorted);
377}
378