1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright © 2020 Valve Corporation
3bf215546Sopenharmony_ci *
4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci *
11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next
12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13bf215546Sopenharmony_ci * Software.
14bf215546Sopenharmony_ci *
15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20bf215546Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21bf215546Sopenharmony_ci * SOFTWARE.
22bf215546Sopenharmony_ci */
23bf215546Sopenharmony_ci
24bf215546Sopenharmony_ci#ifndef __FREEDRENO_GUARDBAND_H__
25bf215546Sopenharmony_ci#define __FREEDRENO_GUARDBAND_H__
26bf215546Sopenharmony_ci
27bf215546Sopenharmony_ci#include <assert.h>
28bf215546Sopenharmony_ci#include <math.h>
29bf215546Sopenharmony_ci#include <stdbool.h>
30bf215546Sopenharmony_ci
31bf215546Sopenharmony_cistatic inline unsigned
32bf215546Sopenharmony_cifd_calc_guardband(float offset, float scale, bool is_a3xx)
33bf215546Sopenharmony_ci{
34bf215546Sopenharmony_ci   /* On a3xx, the viewport max is 4k and the docs say the max guardband
35bf215546Sopenharmony_ci    * width is 8k. That is, GRAS cannot handle triangle coordinates more than
36bf215546Sopenharmony_ci    * 8k, positive or negative. On a4xx+ the viewport width was bumped to
37bf215546Sopenharmony_ci    * 16k, and so the guardband width was necessarily also bumped. Note that
38bf215546Sopenharmony_ci    * the numbers here should correspond to
39bf215546Sopenharmony_ci    * VkPhysicalDeviceLimits::viewportBoundsRange in Vulkan.
40bf215546Sopenharmony_ci    */
41bf215546Sopenharmony_ci   const float gb_min = is_a3xx ? -8192. : -32768.;
42bf215546Sopenharmony_ci   const float gb_max = is_a3xx ? 8191. : 32767.;
43bf215546Sopenharmony_ci
44bf215546Sopenharmony_ci   /* Clipping happens in normalized device coordinates, so we have to
45bf215546Sopenharmony_ci    * transform gb_min and gb_max to ndc using the inverse of the viewport
46bf215546Sopenharmony_ci    * transform. Avoid flipping min and max by using the absolute value of
47bf215546Sopenharmony_ci    * the scale.
48bf215546Sopenharmony_ci    */
49bf215546Sopenharmony_ci   const float gb_min_ndc = (gb_min - offset) / fabsf(scale);
50bf215546Sopenharmony_ci   const float gb_max_ndc = (gb_max - offset) / fabsf(scale);
51bf215546Sopenharmony_ci
52bf215546Sopenharmony_ci   /* There's only one GB_ADJ field, so presumably the guardband is
53bf215546Sopenharmony_ci    * [-GB_ADJ, GB_ADJ] like on Radeon. It's always safe to make the
54bf215546Sopenharmony_ci    * guardband smaller, so we have to take the min to get the largest range
55bf215546Sopenharmony_ci    * contained in [gb_min_ndc, gb_max_ndc].
56bf215546Sopenharmony_ci    */
57bf215546Sopenharmony_ci   const float gb_adj = fminf(-gb_min_ndc, gb_max_ndc);
58bf215546Sopenharmony_ci
59bf215546Sopenharmony_ci   /* The viewport should always be contained in the guardband. */
60bf215546Sopenharmony_ci   assert(gb_adj >= 1.0);
61bf215546Sopenharmony_ci
62bf215546Sopenharmony_ci   /* frexp returns an unspecified value if given an infinite value, which
63bf215546Sopenharmony_ci    * can happen if scale == 0.
64bf215546Sopenharmony_ci    */
65bf215546Sopenharmony_ci   if (isinf(gb_adj))
66bf215546Sopenharmony_ci      return 0x1ff;
67bf215546Sopenharmony_ci
68bf215546Sopenharmony_ci   /* Convert gb_adj to 3.6 floating point, rounding down since it's always
69bf215546Sopenharmony_ci    * safe to make the guard band smaller (but not the other way around!).
70bf215546Sopenharmony_ci    *
71bf215546Sopenharmony_ci    * Note: After converting back to a float, the value the blob returns here
72bf215546Sopenharmony_ci    * is sometimes a little smaller than the value we return. This seems to
73bf215546Sopenharmony_ci    * happen around the boundary between two different rounded values. For
74bf215546Sopenharmony_ci    * example, using the a6xx blob:
75bf215546Sopenharmony_ci    *
76bf215546Sopenharmony_ci    * min  | width  | unrounded gb_adj | blob result | mesa result
77bf215546Sopenharmony_ci    * ------------------------------------------------------------
78bf215546Sopenharmony_ci    * 0    | 510    |          127.498 |        127. |        127.
79bf215546Sopenharmony_ci    * 0    | 511    |          127.247 |        126. |        127.
80bf215546Sopenharmony_ci    * 0    | 512    |          126.996 |        126. |        126.
81bf215546Sopenharmony_ci    *
82bf215546Sopenharmony_ci    * The guardband must be 32767 wide, since that's what the blob reports
83bf215546Sopenharmony_ci    * for viewportBoundsRange, so I'm guessing that they're rounding slightly
84bf215546Sopenharmony_ci    * more conservatively somehow.
85bf215546Sopenharmony_ci    */
86bf215546Sopenharmony_ci   int gb_adj_exp;
87bf215546Sopenharmony_ci   float gb_adj_mantissa = frexpf(gb_adj, &gb_adj_exp);
88bf215546Sopenharmony_ci   assert(gb_adj_exp > 0);
89bf215546Sopenharmony_ci
90bf215546Sopenharmony_ci   /* Round non-representable numbers down to the largest possible number. */
91bf215546Sopenharmony_ci   if (gb_adj_exp > 8)
92bf215546Sopenharmony_ci      return 0x1ff;
93bf215546Sopenharmony_ci
94bf215546Sopenharmony_ci   return ((gb_adj_exp - 1) << 6) |
95bf215546Sopenharmony_ci          ((unsigned)truncf(gb_adj_mantissa * (1 << 7)) - (1 << 6));
96bf215546Sopenharmony_ci}
97bf215546Sopenharmony_ci
98bf215546Sopenharmony_ci#endif /* __FREEDRENO_GUARDBAND_H__ */
99