1/*
2 * Copyright (C) 2018 Rob Clark <robclark@freedesktop.org>
3 * Copyright © 2018 Google, Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * Authors:
25 *    Rob Clark <robclark@freedesktop.org>
26 */
27
28#include "drm-uapi/drm_fourcc.h"
29
30#include "fd6_resource.h"
31#include "fdl/fd6_format_table.h"
32
33#include "a6xx.xml.h"
34
35/* A subset of the valid tiled formats can be compressed.  We do
36 * already require tiled in order to be compressed, but just because
37 * it can be tiled doesn't mean it can be compressed.
38 */
39static bool
40ok_ubwc_format(struct pipe_screen *pscreen, enum pipe_format pfmt)
41{
42   const struct fd_dev_info *info = fd_screen(pscreen)->info;
43
44   switch (pfmt) {
45   case PIPE_FORMAT_X24S8_UINT:
46   case PIPE_FORMAT_Z24_UNORM_S8_UINT:
47      /* We can't sample stencil with UBWC on a630, and we may need to be able
48       * to sample stencil at some point.  We can't just use
49       * fd_resource_uncompress() at the point of stencil sampling because
50       * that itself uses stencil sampling in the fd_blitter_blit path.
51       */
52      return info->a6xx.has_z24uint_s8uint;
53
54   case PIPE_FORMAT_R8_G8B8_420_UNORM:
55      return true;
56
57   default:
58      break;
59   }
60
61   switch (fd6_color_format(pfmt, TILE6_LINEAR)) {
62   case FMT6_10_10_10_2_UINT:
63   case FMT6_10_10_10_2_UNORM_DEST:
64   case FMT6_11_11_10_FLOAT:
65   case FMT6_16_FLOAT:
66   case FMT6_16_16_16_16_FLOAT:
67   case FMT6_16_16_16_16_SINT:
68   case FMT6_16_16_16_16_UINT:
69   case FMT6_16_16_FLOAT:
70   case FMT6_16_16_SINT:
71   case FMT6_16_16_UINT:
72   case FMT6_16_SINT:
73   case FMT6_16_UINT:
74   case FMT6_32_32_32_32_SINT:
75   case FMT6_32_32_32_32_UINT:
76   case FMT6_32_32_SINT:
77   case FMT6_32_32_UINT:
78   case FMT6_5_6_5_UNORM:
79   case FMT6_5_5_5_1_UNORM:
80   case FMT6_8_8_8_8_SINT:
81   case FMT6_8_8_8_8_UINT:
82   case FMT6_8_8_8_8_UNORM:
83   case FMT6_8_8_8_X8_UNORM:
84   case FMT6_8_8_SINT:
85   case FMT6_8_8_UINT:
86   case FMT6_8_8_UNORM:
87   case FMT6_Z24_UNORM_S8_UINT:
88   case FMT6_Z24_UNORM_S8_UINT_AS_R8G8B8A8:
89      return true;
90   case FMT6_8_UNORM:
91      return info->a6xx.has_8bpp_ubwc;
92   default:
93      return false;
94   }
95}
96
97static bool
98can_do_ubwc(struct pipe_resource *prsc)
99{
100   /* limit things to simple single level 2d for now: */
101   if ((prsc->depth0 != 1) || (prsc->array_size != 1) ||
102       (prsc->last_level != 0))
103      return false;
104   if (prsc->target != PIPE_TEXTURE_2D)
105      return false;
106   if (!ok_ubwc_format(prsc->screen, prsc->format))
107      return false;
108   return true;
109}
110
111static bool
112is_norm(enum pipe_format format)
113{
114   const struct util_format_description *desc = util_format_description(format);
115
116   return desc->is_snorm || desc->is_unorm;
117}
118
119static bool
120valid_format_cast(struct fd_resource *rsc, enum pipe_format format)
121{
122   /* Special case "casting" format in hw: */
123   if (format == PIPE_FORMAT_Z24_UNORM_S8_UINT_AS_R8G8B8A8)
124      return true;
125
126   /* For some color values (just "solid white") compression metadata maps to
127    * different pixel values for uint/sint vs unorm/snorm, so we can't reliably
128    * "cast" u/snorm to u/sint and visa versa:
129    */
130   if (is_norm(format) != is_norm(rsc->b.b.format))
131      return false;
132
133   /* The UBWC formats can be re-interpreted so long as the components
134    * have the same # of bits
135    */
136   for (unsigned i = 0; i < 4; i++) {
137      unsigned sb, db;
138
139      sb = util_format_get_component_bits(rsc->b.b.format, UTIL_FORMAT_COLORSPACE_RGB, i);
140      db = util_format_get_component_bits(format, UTIL_FORMAT_COLORSPACE_RGB, i);
141
142      if (sb != db)
143         return false;
144   }
145
146   return true;
147}
148
149/**
150 * R8G8 have a different block width/height and height alignment from other
151 * formats that would normally be compatible (like R16), and so if we are
152 * trying to, for example, sample R16 as R8G8 we need to demote to linear.
153 */
154static bool
155is_r8g8(enum pipe_format format)
156{
157   return (util_format_get_blocksize(format) == 2) &&
158         (util_format_get_nr_components(format) == 2);
159}
160
161/**
162 * Ensure the rsc is in an ok state to be used with the specified format.
163 * This handles the case of UBWC buffers used with non-UBWC compatible
164 * formats, by triggering an uncompress.
165 */
166void
167fd6_validate_format(struct fd_context *ctx, struct fd_resource *rsc,
168                    enum pipe_format format)
169{
170   enum pipe_format orig_format = rsc->b.b.format;
171
172   tc_assert_driver_thread(ctx->tc);
173
174   if (orig_format == format)
175      return;
176
177   if (rsc->layout.tile_mode && (is_r8g8(orig_format) != is_r8g8(format))) {
178      perf_debug_ctx(ctx,
179                     "%" PRSC_FMT ": demoted to linear+uncompressed due to use as %s",
180                     PRSC_ARGS(&rsc->b.b), util_format_short_name(format));
181
182      fd_resource_uncompress(ctx, rsc, true);
183      return;
184   }
185
186   if (!rsc->layout.ubwc)
187      return;
188
189   if (ok_ubwc_format(rsc->b.b.screen, format) && valid_format_cast(rsc, format))
190      return;
191
192   perf_debug_ctx(ctx,
193                  "%" PRSC_FMT ": demoted to uncompressed due to use as %s",
194                  PRSC_ARGS(&rsc->b.b), util_format_short_name(format));
195
196   fd_resource_uncompress(ctx, rsc, false);
197}
198
199static void
200setup_lrz(struct fd_resource *rsc)
201{
202   struct fd_screen *screen = fd_screen(rsc->b.b.screen);
203   unsigned width0 = rsc->b.b.width0;
204   unsigned height0 = rsc->b.b.height0;
205
206   /* LRZ buffer is super-sampled: */
207   switch (rsc->b.b.nr_samples) {
208   case 4:
209      width0 *= 2;
210      FALLTHROUGH;
211   case 2:
212      height0 *= 2;
213   }
214
215   unsigned lrz_pitch = align(DIV_ROUND_UP(width0, 8), 32);
216   unsigned lrz_height = align(DIV_ROUND_UP(height0, 8), 16);
217
218   unsigned size = lrz_pitch * lrz_height * 2;
219
220   rsc->lrz_height = lrz_height;
221   rsc->lrz_width = lrz_pitch;
222   rsc->lrz_pitch = lrz_pitch;
223   rsc->lrz = fd_bo_new(screen->dev, size, FD_BO_NOMAP, "lrz");
224}
225
226static uint32_t
227fd6_setup_slices(struct fd_resource *rsc)
228{
229   struct pipe_resource *prsc = &rsc->b.b;
230
231   if (!FD_DBG(NOLRZ) && has_depth(rsc->b.b.format))
232      setup_lrz(rsc);
233
234   if (rsc->layout.ubwc && !ok_ubwc_format(rsc->b.b.screen, rsc->b.b.format))
235      rsc->layout.ubwc = false;
236
237   fdl6_layout(&rsc->layout, prsc->format, fd_resource_nr_samples(prsc),
238               prsc->width0, prsc->height0, prsc->depth0, prsc->last_level + 1,
239               prsc->array_size, prsc->target == PIPE_TEXTURE_3D, NULL);
240
241   return rsc->layout.size;
242}
243
244static int
245fill_ubwc_buffer_sizes(struct fd_resource *rsc)
246{
247   struct pipe_resource *prsc = &rsc->b.b;
248   struct fdl_explicit_layout explicit = {
249      .offset = rsc->layout.slices[0].offset,
250      .pitch = rsc->layout.pitch0,
251   };
252
253   if (!can_do_ubwc(prsc))
254      return -1;
255
256   rsc->layout.ubwc = true;
257   rsc->layout.tile_mode = TILE6_3;
258
259   if (!fdl6_layout(&rsc->layout, prsc->format, fd_resource_nr_samples(prsc),
260                    prsc->width0, prsc->height0, prsc->depth0,
261                    prsc->last_level + 1, prsc->array_size, false, &explicit))
262      return -1;
263
264   if (rsc->layout.size > fd_bo_size(rsc->bo))
265      return -1;
266
267   return 0;
268}
269
270static int
271fd6_layout_resource_for_modifier(struct fd_resource *rsc, uint64_t modifier)
272{
273   switch (modifier) {
274   case DRM_FORMAT_MOD_QCOM_COMPRESSED:
275      return fill_ubwc_buffer_sizes(rsc);
276   case DRM_FORMAT_MOD_LINEAR:
277      if (can_do_ubwc(&rsc->b.b)) {
278         perf_debug("%" PRSC_FMT
279                    ": not UBWC: imported with DRM_FORMAT_MOD_LINEAR!",
280                    PRSC_ARGS(&rsc->b.b));
281      }
282      return 0;
283   case DRM_FORMAT_MOD_INVALID:
284      if (can_do_ubwc(&rsc->b.b)) {
285         perf_debug("%" PRSC_FMT
286                    ": not UBWC: imported with DRM_FORMAT_MOD_INVALID!",
287                    PRSC_ARGS(&rsc->b.b));
288      }
289      return 0;
290   default:
291      return -1;
292   }
293}
294
295static const uint64_t supported_modifiers[] = {
296   DRM_FORMAT_MOD_LINEAR,
297   DRM_FORMAT_MOD_QCOM_COMPRESSED,
298};
299
300void
301fd6_resource_screen_init(struct pipe_screen *pscreen)
302{
303   struct fd_screen *screen = fd_screen(pscreen);
304
305   screen->setup_slices = fd6_setup_slices;
306   screen->layout_resource_for_modifier = fd6_layout_resource_for_modifier;
307   screen->supported_modifiers = supported_modifiers;
308   screen->num_supported_modifiers = ARRAY_SIZE(supported_modifiers);
309}
310