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 */
46typedef const float (*const_float4_ptr)[4];
47
48
49static inline
50const_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 */
58static boolean
59is_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 */
79static void
80make_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 */
95static void
96calc_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 */
109static boolean
110test_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
121static void
122rect(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 */
153static boolean
154test_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 */
214static boolean
215check_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   PIPE_ALIGN_VAR(16) float vA[2][4];
243   PIPE_ALIGN_VAR(16) float vB[2][4];
244   PIPE_ALIGN_VAR(16) float vC[2][4];
245   PIPE_ALIGN_VAR(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
319boolean
320lp_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