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