• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**************************************************************************
2  *
3  * Copyright 2010-2021 VMWare, Inc.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  **************************************************************************/
27 
28 /**
29  * Look for common topology patterns which can be converted into rectangles.
30  */
31 
32 
33 #include "lp_setup_context.h"
34 #include "draw/draw_vbuf.h"
35 #include "draw/draw_vertex.h"
36 #include "util/u_memory.h"
37 #include "util/u_math.h"
38 #include "lp_state_fs.h"
39 #include "lp_state_setup.h"
40 #include "lp_perf.h"
41 
42 
43 /**
44  * Duplicated from lp_setup_vbuf.c.
45  */
46 typedef const float (*const_float4_ptr)[4];
47 
48 
49 static inline
get_vert(const void * vertex_buffer,int index,int stride)50 const_float4_ptr get_vert(const void *vertex_buffer, int index, int stride)
51 {
52    return (const_float4_ptr)((char *)vertex_buffer + index * stride);
53 }
54 
55 
56 /* Aero sends these weird zero area triangles.  Test for them here.
57  */
58 static boolean
is_zero_area(const float (* v0)[4],const float (* v1)[4],const float (* v2)[4])59 is_zero_area(const float (*v0)[4],
60              const float (*v1)[4],
61              const float (*v2)[4])
62 {
63    /* Specialized test for v0.y == v1.y == v2.y.
64     */
65    return (v0[0][1] == v1[0][1] &&
66            v0[0][1] == v2[0][1]);
67 }
68 
69 
70 /**
71  * Assuming axis-aligned stretch blit (s a function of x alone, t a
72  * function of y alone), create a new vertex as in:
73  *
74  *   vx------+
75  *    |      |
76  *    |      |
77  *   out-----vy
78  */
79 static void
make_vert(const float (* vx)[4],const float (* vy)[4],float (* out)[4])80 make_vert(const float (*vx)[4],
81           const float (*vy)[4],
82           float (*out)[4])
83 {
84    out[0][0] = vx[0][0];
85    out[0][1] = vy[0][1];
86    out[0][2] = vx[0][2];
87    out[0][3] = vx[0][3];
88    out[1][0] = vx[1][0];
89    out[1][1] = vy[1][1];
90 }
91 
92 
93 /* Calculate axis-aligned interpolant for s as a function of x.
94  */
95 static void
calc_interps(float x0,float x1,float s0,float s1,float * a,float * b)96 calc_interps(float x0, float x1,
97              float s0, float s1,
98              float *a, float *b)
99 {
100    assert(x0 != x1);
101    *a = (s0 - s1) / (x0 - x1);
102    *b = s0 - *a * x0;
103 }
104 
105 
106 /* Validate axis-aligned interpolant for s and t as functions of x and
107  * y respectively.
108  */
109 static boolean
test_interps(const_float4_ptr v,float as,float bs,float at,float bt)110 test_interps(const_float4_ptr v,
111              float as, float bs,
112              float at, float bt)
113 {
114    float s = as * v[0][0] + bs;
115    float t = at * v[0][1] + bt;
116    return (util_is_approx(s, v[1][0], 1/4096.0) &&
117            util_is_approx(t, v[1][1], 1/4096.0));
118 }
119 
120 
121 static void
rect(struct lp_setup_context * setup,const float (* v0)[4],const float (* v1)[4],const float (* v2)[4])122 rect(struct lp_setup_context *setup,
123      const float (*v0)[4],
124      const float (*v1)[4],
125      const float (*v2)[4])
126 {
127    ASSERTED int culled = LP_COUNT_GET(nr_culled_rects);
128 
129    if (0) {
130       float as, bs, at, bt;
131       calc_interps(v0[0][0], v2[0][0], v0[1][0], v2[1][0], &as, &bs);
132       calc_interps(v0[0][1], v2[0][1], v0[1][1], v2[1][1], &at, &bt);
133       assert(test_interps(v1, as, bs, at, bt));
134    }
135 
136    assert(v0[0][0] == v1[0][0]);
137    assert(v1[0][1] == v2[0][1]);
138 
139    lp_rect_cw(setup, v0, v1, v2, TRUE);
140 
141    assert(culled == LP_COUNT_GET(nr_culled_rects));
142 }
143 
144 
145 /**
146  * Check this is an axis-aligned rectangle as in:
147  *
148  *   v3------v0
149  *    |      |
150  *    |      |
151  *   v2------v1
152  */
153 static boolean
test_rect(const_float4_ptr v0,const_float4_ptr v1,const_float4_ptr v2,const_float4_ptr v3)154 test_rect(const_float4_ptr v0,
155           const_float4_ptr v1,
156           const_float4_ptr v2,
157           const_float4_ptr v3)
158 {
159    if (v0[0][0] != v1[0][0] ||
160        v1[0][1] != v2[0][1] ||
161        v2[0][0] != v3[0][0] ||
162        v3[0][1] != v0[0][1])
163       return FALSE;
164 
165    if (v0[0][3] != 1.0 ||
166        v1[0][3] != 1.0 ||
167        v2[0][3] != 1.0 ||
168        v3[0][3] != 1.0)
169       return FALSE;
170 
171    return TRUE;
172 }
173 
174 
175 /**
176  * Aero sends the following shape as
177  *
178  *  18                                                        12
179  *    +-------------------------------------------------/----+
180  *    |\                                      /---------    /|
181  *    | \                           /---------             / |
182  *    |  \                /---------                      /  |
183  *    |   \     /---------                               /   |
184  * vA +    +--------------------------------------------+    + vC
185  *    |    | 9                                        6 |    |
186  *    |   /|                                            |\   |
187  *    |   ||                                            ||   |
188  *    |  / |                                            | \  |
189  *    |  | |                                            | |  |
190  *    |  | | 3                                        0 |  \ |
191  * vB + /  +--------------------------------------------+  | + vD
192  *    | | /                               ---------/     \ | |
193  *    |/ /                      ---------/                \ \|
194  *    ||/             ---------/                           \||
195  *    |/    ---------/                                      \|
196  *    +----/-------------------------------------------------+
197  *   1                                                        2
198  *
199  * and in the following decomposition:
200  *   (0, 1, 2)
201  *   (3, 0, 1),
202  *   (6, 0, 2),
203  *   (9, 3, 1),
204  *   (12, 2, 6),
205  *   (12, 6, 9),
206  *   (18, 1, 9),
207  *   (18, 9, 12),
208  *
209  * There's no straight-forward way to interpret the existing vertices
210  * as rectangles.  Instead we convert this into four axis-aligned
211  * rectangles by introducing new vertices at vA, vB, vC and vD, and
212  * then drawing rectangles.
213  */
214 static boolean
check_elts24(struct lp_setup_context * setup,const void * vb,int stride)215 check_elts24(struct lp_setup_context *setup, const void *vb, int stride)
216 {
217    const int count = 24;
218    const int uniq[8] = { 0, 1, 2, 3, 6, 9, 12, 18 };
219    const int elts[24] = {
220       0, 1, 2,
221       3, 0, 1,
222       6, 0, 2,
223       9, 3, 1,
224       12, 2, 6,
225       12, 6, 9,
226       18, 1, 9,
227       18, 9, 12
228    };
229    const_float4_ptr v0  = get_vert(vb, stride, 0);
230    const_float4_ptr v1  = get_vert(vb, stride, 1);
231    const_float4_ptr v2  = get_vert(vb, stride, 2);
232    const_float4_ptr v3  = get_vert(vb, stride, 3);
233    const_float4_ptr v6  = get_vert(vb, stride, 6);
234    const_float4_ptr v9  = get_vert(vb, stride, 9);
235    const_float4_ptr v12 = get_vert(vb, stride, 12);
236    const_float4_ptr v18 = get_vert(vb, stride, 18);
237 
238    /* Could just calculate a set of interpolants and bin rectangle
239     * commands for this triangle list directly.  Instead, introduce
240     * some new vertices and feed to the rectangle setup code:
241     */
242    alignas(16) float vA[2][4];
243    alignas(16) float vB[2][4];
244    alignas(16) float vC[2][4];
245    alignas(16) float vD[2][4];
246 
247    float as, bs;
248    float at, bt;
249    int i;
250 
251    if (stride != 32)
252       return FALSE;
253 
254    /* Check the shape is two rectangles:
255     */
256    if (!test_rect(v12, v2, v1, v18))
257       return FALSE;
258 
259    if (!test_rect(v6, v0, v3, v9))
260       return FALSE;
261 
262    /* XXX: check one rect is inside the other?
263     */
264 
265    /* Check our tesselation matches:
266     */
267    for (i = 0; i < count; i++) {
268       if (memcmp(get_vert(vb, i, stride),
269                  get_vert(vb, elts[i], stride),
270                  6 * sizeof(float)) != 0)
271          return FALSE;
272    }
273 
274    /* Test that this is a stretch blit, meaning we should be able to
275     * introduce vertices at will.
276     */
277    calc_interps(v0[0][0], v2[0][0], v0[1][0], v2[1][0], &as, &bs);
278    calc_interps(v0[0][1], v2[0][1], v0[1][1], v2[1][1], &at, &bt);
279 
280    for (i = 0; i < ARRAY_SIZE(uniq); i++) {
281       const_float4_ptr v = get_vert(vb, stride, i);
282       if (!test_interps(v, as, bs, at, bt))
283          return FALSE;
284    }
285 
286    make_vert(v18, v9, vA);
287    make_vert(v18, v3, vB);
288    make_vert(v12, v9, vC);
289    make_vert(v12, v3, vD);
290 
291    assert(test_interps((const_float4_ptr)vA, as, bs, at, bt));
292    assert(test_interps((const_float4_ptr)vB, as, bs, at, bt));
293    assert(test_interps((const_float4_ptr)vC, as, bs, at, bt));
294    assert(test_interps((const_float4_ptr)vD, as, bs, at, bt));
295 
296    rect(setup,
297         (const_float4_ptr)v12,
298         (const_float4_ptr)vC,
299         (const_float4_ptr)vA);
300 
301    rect(setup,
302         (const_float4_ptr)v9,
303         (const_float4_ptr)v3,
304         (const_float4_ptr)vB);
305 
306    rect(setup,
307         (const_float4_ptr)vD,
308         (const_float4_ptr)v2,
309         (const_float4_ptr)v1);
310 
311    rect(setup,
312         (const_float4_ptr)vC,
313         (const_float4_ptr)vD,
314         (const_float4_ptr)v0);
315 
316    return TRUE;
317 }
318 
319 boolean
lp_setup_analyse_triangles(struct lp_setup_context * setup,const void * vb,int stride,int nr)320 lp_setup_analyse_triangles(struct lp_setup_context *setup,
321                            const void *vb,
322                            int stride,
323                            int nr)
324 {
325    int i;
326    const boolean variant_blit = setup->fs.current.variant->blit;
327 
328    if (0) {
329       debug_printf("%s %d\n", __FUNCTION__, nr);
330 
331       if (variant_blit) {
332          debug_printf("  - blit variant\n");
333       }
334 
335       for (i = 0; i < nr; i += 3) {
336          const_float4_ptr v0 = get_vert(vb, i, stride);
337          const_float4_ptr v1 = get_vert(vb, i+1, stride);
338          const_float4_ptr v2 = get_vert(vb, i+2, stride);
339          lp_setup_print_triangle(setup, v0, v1, v2);
340       }
341    }
342 
343    /* When drawing some window navigator bars, aero sends a mixed up
344     * rectangle:
345     *
346     *    - first triangle ccw
347     *    - second triangle cw
348     *    - third triangle zero area.
349     */
350    if (nr == 9 &&
351        is_zero_area(get_vert(vb, nr-1, stride),
352                     get_vert(vb, nr-2, stride),
353                     get_vert(vb, nr-3, stride)))
354    {
355       const float (*v0)[4] = get_vert(vb, 0, stride);
356       const float (*v1)[4] = get_vert(vb, 1, stride);
357       const float (*v2)[4] = get_vert(vb, 2, stride);
358       const float (*v3)[4] = get_vert(vb, 3, stride);
359       const float (*v4)[4] = get_vert(vb, 4, stride);
360       const float (*v5)[4] = get_vert(vb, 5, stride);
361 
362       /*
363        * [   v0,       v1,       v2   ]  [   v3,       v4,       v5   ]
364        * [(X0, Y0), (X0, Y1), (X1, Y1)]  [(X1, Y0), (X1, Y1), (X0, Y0)]
365        */
366       if (v0[0][0] == v1[0][0] && v0[0][0] == v5[0][0] &&
367           v2[0][0] == v3[0][0] && v2[0][0] == v4[0][0] &&
368           v0[0][1] == v3[0][1] && v0[0][1] == v5[0][1] &&
369           v1[0][1] == v2[0][1] && v1[0][1] == v4[0][1]) {
370 
371          lp_rect_cw(setup, v0, v1, v2, TRUE);
372       }
373       return TRUE;
374    }
375 
376    /* When highlighting (?) windows, aero sends a window border
377     * comprised of non-rectangular triangles, but which as a whole can
378     * be decomposed into rectangles.
379     *
380     * Again, with a zero-area trailing triangle.
381     *
382     * This requires introducing a couple of new vertices, which are
383     * luckily easy to compute.
384     */
385    if (nr == 27 &&
386        variant_blit &&
387        setup->setup.variant->key.inputs[0].src_index == 1 &&
388        setup->setup.variant->key.inputs[0].usage_mask == 0x3 &&
389        is_zero_area(get_vert(vb, nr-1, stride),
390                     get_vert(vb, nr-2, stride),
391                     get_vert(vb, nr-3, stride)) &&
392        check_elts24(setup, vb, stride))
393    {
394       return TRUE;
395    }
396 
397    return FALSE;
398 }
399