lima_resource.c revision 7ec681f3
1/*
2 * Copyright (c) 2017-2019 Lima Project
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sub license,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the
12 * next paragraph) shall be included in all copies or substantial portions
13 * of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25#include "util/u_memory.h"
26#include "util/u_blitter.h"
27#include "util/format/u_format.h"
28#include "util/u_inlines.h"
29#include "util/u_math.h"
30#include "util/u_debug.h"
31#include "util/u_transfer.h"
32#include "util/u_surface.h"
33#include "util/hash_table.h"
34#include "util/ralloc.h"
35#include "util/u_drm.h"
36#include "renderonly/renderonly.h"
37
38#include "frontend/drm_driver.h"
39
40#include "drm-uapi/drm_fourcc.h"
41#include "drm-uapi/lima_drm.h"
42
43#include "lima_screen.h"
44#include "lima_context.h"
45#include "lima_resource.h"
46#include "lima_bo.h"
47#include "lima_util.h"
48
49#include "pan_minmax_cache.h"
50#include "pan_tiling.h"
51
52static struct pipe_resource *
53lima_resource_create_scanout(struct pipe_screen *pscreen,
54                             const struct pipe_resource *templat,
55                             unsigned width, unsigned height)
56{
57   struct lima_screen *screen = lima_screen(pscreen);
58   struct renderonly_scanout *scanout;
59   struct winsys_handle handle;
60   struct pipe_resource *pres;
61
62   struct pipe_resource scanout_templat = *templat;
63   scanout_templat.width0 = width;
64   scanout_templat.height0 = height;
65   scanout_templat.screen = pscreen;
66
67   scanout = renderonly_scanout_for_resource(&scanout_templat,
68                                             screen->ro, &handle);
69   if (!scanout)
70      return NULL;
71
72   assert(handle.type == WINSYS_HANDLE_TYPE_FD);
73   pres = pscreen->resource_from_handle(pscreen, templat, &handle,
74                                        PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE);
75
76   close(handle.handle);
77   if (!pres) {
78      renderonly_scanout_destroy(scanout, screen->ro);
79      return NULL;
80   }
81
82   struct lima_resource *res = lima_resource(pres);
83   res->scanout = scanout;
84
85   return pres;
86}
87
88static uint32_t
89setup_miptree(struct lima_resource *res,
90              unsigned width0, unsigned height0,
91              bool should_align_dimensions)
92{
93   struct pipe_resource *pres = &res->base;
94   unsigned level;
95   unsigned width = width0;
96   unsigned height = height0;
97   unsigned depth = pres->depth0;
98   uint32_t size = 0;
99
100   for (level = 0; level <= pres->last_level; level++) {
101      uint32_t actual_level_size;
102      uint32_t stride;
103      unsigned aligned_width;
104      unsigned aligned_height;
105
106      if (should_align_dimensions) {
107         aligned_width = align(width, 16);
108         aligned_height = align(height, 16);
109      } else {
110         aligned_width = width;
111         aligned_height = height;
112      }
113
114      stride = util_format_get_stride(pres->format, aligned_width);
115      actual_level_size = stride *
116         util_format_get_nblocksy(pres->format, aligned_height) *
117         pres->array_size * depth;
118
119      res->levels[level].width = aligned_width;
120      res->levels[level].stride = stride;
121      res->levels[level].offset = size;
122      res->levels[level].layer_stride = util_format_get_stride(pres->format, align(width, 16)) * align(height, 16);
123
124      if (util_format_is_compressed(pres->format))
125         res->levels[level].layer_stride /= 4;
126
127      /* The start address of each level except the last level
128       * must be 64-aligned in order to be able to pass the
129       * addresses to the hardware. */
130      if (level != pres->last_level)
131         size += align(actual_level_size, 64);
132      else
133         size += actual_level_size;  /* Save some memory */
134
135      width = u_minify(width, 1);
136      height = u_minify(height, 1);
137      depth = u_minify(depth, 1);
138   }
139
140   return size;
141}
142
143static struct pipe_resource *
144lima_resource_create_bo(struct pipe_screen *pscreen,
145                        const struct pipe_resource *templat,
146                        unsigned width, unsigned height,
147                        bool should_align_dimensions)
148{
149   struct lima_screen *screen = lima_screen(pscreen);
150   struct lima_resource *res;
151   struct pipe_resource *pres;
152
153   res = CALLOC_STRUCT(lima_resource);
154   if (!res)
155      return NULL;
156
157   res->base = *templat;
158   res->base.screen = pscreen;
159   pipe_reference_init(&res->base.reference, 1);
160
161   pres = &res->base;
162
163   uint32_t size = setup_miptree(res, width, height, should_align_dimensions);
164   size = align(size, LIMA_PAGE_SIZE);
165
166   res->bo = lima_bo_create(screen, size, 0);
167   if (!res->bo) {
168      FREE(res);
169      return NULL;
170   }
171
172   return pres;
173}
174
175static struct pipe_resource *
176_lima_resource_create_with_modifiers(struct pipe_screen *pscreen,
177                                     const struct pipe_resource *templat,
178                                     const uint64_t *modifiers,
179                                     int count)
180{
181   struct lima_screen *screen = lima_screen(pscreen);
182   bool should_tile = lima_debug & LIMA_DEBUG_NO_TILING ? false : true;
183   unsigned width, height;
184   bool should_align_dimensions;
185   bool has_user_modifiers = true;
186
187   if (count == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID)
188      has_user_modifiers = false;
189
190   /* VBOs/PBOs are untiled (and 1 height). */
191   if (templat->target == PIPE_BUFFER)
192      should_tile = false;
193
194   if (templat->bind & (PIPE_BIND_LINEAR | PIPE_BIND_SCANOUT))
195      should_tile = false;
196
197   /* If there's no user modifiers and buffer is shared we use linear */
198   if (!has_user_modifiers && (templat->bind & PIPE_BIND_SHARED))
199      should_tile = false;
200
201   if (has_user_modifiers &&
202      !drm_find_modifier(DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED,
203                         modifiers, count))
204      should_tile = false;
205
206   if (should_tile || (templat->bind & PIPE_BIND_RENDER_TARGET) ||
207       (templat->bind & PIPE_BIND_DEPTH_STENCIL)) {
208      should_align_dimensions = true;
209      width = align(templat->width0, 16);
210      height = align(templat->height0, 16);
211   }
212   else {
213      should_align_dimensions = false;
214      width = templat->width0;
215      height = templat->height0;
216   }
217
218   struct pipe_resource *pres;
219   if (screen->ro && (templat->bind & PIPE_BIND_SCANOUT))
220      pres = lima_resource_create_scanout(pscreen, templat, width, height);
221   else
222      pres = lima_resource_create_bo(pscreen, templat, width, height,
223                                     should_align_dimensions);
224
225   if (pres) {
226      struct lima_resource *res = lima_resource(pres);
227      res->tiled = should_tile;
228
229      if (templat->bind & PIPE_BIND_INDEX_BUFFER)
230         res->index_cache = CALLOC_STRUCT(panfrost_minmax_cache);
231
232      debug_printf("%s: pres=%p width=%u height=%u depth=%u target=%d "
233                   "bind=%x usage=%d tile=%d last_level=%d\n", __func__,
234                   pres, pres->width0, pres->height0, pres->depth0,
235                   pres->target, pres->bind, pres->usage, should_tile, templat->last_level);
236   }
237   return pres;
238}
239
240static struct pipe_resource *
241lima_resource_create(struct pipe_screen *pscreen,
242                     const struct pipe_resource *templat)
243{
244   const uint64_t mod = DRM_FORMAT_MOD_INVALID;
245
246   return _lima_resource_create_with_modifiers(pscreen, templat, &mod, 1);
247}
248
249static struct pipe_resource *
250lima_resource_create_with_modifiers(struct pipe_screen *pscreen,
251                                    const struct pipe_resource *templat,
252                                    const uint64_t *modifiers,
253                                    int count)
254{
255   struct pipe_resource tmpl = *templat;
256
257   /* gbm_bo_create_with_modifiers & gbm_surface_create_with_modifiers
258    * don't have usage parameter, but buffer created by these functions
259    * may be used for scanout. So we assume buffer created by this
260    * function always enable scanout if linear modifier is permitted.
261    */
262   if (drm_find_modifier(DRM_FORMAT_MOD_LINEAR, modifiers, count))
263      tmpl.bind |= PIPE_BIND_SCANOUT;
264
265   return _lima_resource_create_with_modifiers(pscreen, &tmpl, modifiers, count);
266}
267
268static void
269lima_resource_destroy(struct pipe_screen *pscreen, struct pipe_resource *pres)
270{
271   struct lima_screen *screen = lima_screen(pscreen);
272   struct lima_resource *res = lima_resource(pres);
273
274   if (res->bo)
275      lima_bo_unreference(res->bo);
276
277   if (res->scanout)
278      renderonly_scanout_destroy(res->scanout, screen->ro);
279
280   if (res->damage.region)
281      FREE(res->damage.region);
282
283   if (res->index_cache)
284      FREE(res->index_cache);
285
286   FREE(res);
287}
288
289static struct pipe_resource *
290lima_resource_from_handle(struct pipe_screen *pscreen,
291        const struct pipe_resource *templat,
292        struct winsys_handle *handle, unsigned usage)
293{
294   if (templat->bind & (PIPE_BIND_SAMPLER_VIEW |
295                        PIPE_BIND_RENDER_TARGET |
296                        PIPE_BIND_DEPTH_STENCIL)) {
297      /* sampler hardware need offset alignment 64, while render hardware
298       * need offset alignment 8, but due to render target may be reloaded
299       * which uses the sampler, set alignment requrement to 64 for all
300       */
301      if (handle->offset & 0x3f) {
302         debug_error("import buffer offset not properly aligned\n");
303         return NULL;
304      }
305   }
306
307   struct lima_resource *res = CALLOC_STRUCT(lima_resource);
308   if (!res)
309      return NULL;
310
311   struct pipe_resource *pres = &res->base;
312   *pres = *templat;
313   pres->screen = pscreen;
314   pipe_reference_init(&pres->reference, 1);
315   res->levels[0].offset = handle->offset;
316   res->levels[0].stride = handle->stride;
317
318   struct lima_screen *screen = lima_screen(pscreen);
319   res->bo = lima_bo_import(screen, handle);
320   if (!res->bo) {
321      FREE(res);
322      return NULL;
323   }
324
325   res->modifier_constant = true;
326
327   switch (handle->modifier) {
328   case DRM_FORMAT_MOD_LINEAR:
329      res->tiled = false;
330      break;
331   case DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED:
332      res->tiled = true;
333      break;
334   case DRM_FORMAT_MOD_INVALID:
335      /* Modifier wasn't specified and it's shared buffer. We create these
336       * as linear, so disable tiling.
337       */
338      res->tiled = false;
339      break;
340   default:
341      fprintf(stderr, "Attempted to import unsupported modifier 0x%llx\n",
342                  (long long)handle->modifier);
343      goto err_out;
344   }
345
346   /* check alignment for the buffer */
347   if (res->tiled ||
348       (pres->bind & (PIPE_BIND_RENDER_TARGET | PIPE_BIND_DEPTH_STENCIL))) {
349      unsigned width, height, stride, size;
350
351      width = align(pres->width0, 16);
352      height = align(pres->height0, 16);
353      stride = util_format_get_stride(pres->format, width);
354      size = util_format_get_2d_size(pres->format, stride, height);
355
356      if (res->tiled && res->levels[0].stride != stride) {
357         fprintf(stderr, "tiled imported buffer has mismatching stride: %d (BO) != %d (expected)",
358                     res->levels[0].stride, stride);
359         goto err_out;
360      }
361
362      if (!res->tiled && (res->levels[0].stride % 8)) {
363         fprintf(stderr, "linear imported buffer stride is not aligned to 8 bytes: %d\n",
364                 res->levels[0].stride);
365      }
366
367      if (!res->tiled && res->levels[0].stride < stride) {
368         fprintf(stderr, "linear imported buffer stride is smaller than minimal: %d (BO) < %d (min)",
369                 res->levels[0].stride, stride);
370         goto err_out;
371      }
372
373      if ((res->bo->size - res->levels[0].offset) < size) {
374         fprintf(stderr, "imported bo size is smaller than expected: %d (BO) < %d (expected)\n",
375                 (res->bo->size - res->levels[0].offset), size);
376         goto err_out;
377      }
378
379      res->levels[0].width = width;
380   }
381   else
382      res->levels[0].width = pres->width0;
383
384   if (screen->ro) {
385      /* Make sure that renderonly has a handle to our buffer in the
386       * display's fd, so that a later renderonly_get_handle()
387       * returns correct handles or GEM names.
388       */
389      res->scanout =
390         renderonly_create_gpu_import_for_resource(pres,
391                                                   screen->ro,
392                                                   NULL);
393      /* ignore failiure to allow importing non-displayable buffer */
394   }
395
396   return pres;
397
398err_out:
399   lima_resource_destroy(pscreen, pres);
400   return NULL;
401}
402
403static bool
404lima_resource_get_handle(struct pipe_screen *pscreen,
405                         struct pipe_context *pctx,
406                         struct pipe_resource *pres,
407                         struct winsys_handle *handle, unsigned usage)
408{
409   struct lima_screen *screen = lima_screen(pscreen);
410   struct lima_resource *res = lima_resource(pres);
411
412   if (res->tiled)
413      handle->modifier = DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED;
414   else
415      handle->modifier = DRM_FORMAT_MOD_LINEAR;
416
417   res->modifier_constant = true;
418
419   if (handle->type == WINSYS_HANDLE_TYPE_KMS && screen->ro)
420      return renderonly_get_handle(res->scanout, handle);
421
422   if (!lima_bo_export(res->bo, handle))
423      return false;
424
425   handle->offset = res->levels[0].offset;
426   handle->stride = res->levels[0].stride;
427   return true;
428}
429
430static bool
431lima_resource_get_param(struct pipe_screen *pscreen,
432                        struct pipe_context *pctx,
433                        struct pipe_resource *pres,
434                        unsigned plane, unsigned layer, unsigned level,
435                        enum pipe_resource_param param,
436                        unsigned usage, uint64_t *value)
437{
438   struct lima_resource *res = lima_resource(pres);
439
440   switch (param) {
441   case PIPE_RESOURCE_PARAM_STRIDE:
442      *value = res->levels[level].stride;
443      return true;
444   case PIPE_RESOURCE_PARAM_OFFSET:
445      *value = res->levels[level].offset;
446      return true;
447   case PIPE_RESOURCE_PARAM_MODIFIER:
448      if (res->tiled)
449         *value = DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED;
450      else
451         *value = DRM_FORMAT_MOD_LINEAR;
452
453      return true;
454   default:
455      return false;
456   }
457}
458
459static void
460get_scissor_from_box(struct pipe_scissor_state *s,
461                     const struct pipe_box *b, int h)
462{
463   int y = h - (b->y + b->height);
464   /* region in tile unit */
465   s->minx = b->x >> 4;
466   s->miny = y >> 4;
467   s->maxx = (b->x + b->width + 0xf) >> 4;
468   s->maxy = (y + b->height + 0xf) >> 4;
469}
470
471static void
472get_damage_bound_box(struct pipe_resource *pres,
473                     const struct pipe_box *rects,
474                     unsigned int nrects,
475                     struct pipe_scissor_state *bound)
476{
477   struct pipe_box b = rects[0];
478
479   for (int i = 1; i < nrects; i++)
480      u_box_union_2d(&b, &b, rects + i);
481
482   int ret = u_box_clip_2d(&b, &b, pres->width0, pres->height0);
483   if (ret < 0)
484      memset(bound, 0, sizeof(*bound));
485   else
486      get_scissor_from_box(bound, &b, pres->height0);
487}
488
489static void
490lima_resource_set_damage_region(struct pipe_screen *pscreen,
491                                struct pipe_resource *pres,
492                                unsigned int nrects,
493                                const struct pipe_box *rects)
494{
495   struct lima_resource *res = lima_resource(pres);
496   struct lima_damage_region *damage = &res->damage;
497   int i;
498
499   if (damage->region) {
500      FREE(damage->region);
501      damage->region = NULL;
502      damage->num_region = 0;
503   }
504
505   if (!nrects)
506      return;
507
508   /* check full damage
509    *
510    * TODO: currently only check if there is any single damage
511    * region that can cover the full render target; there may
512    * be some accurate way, but a single window size damage
513    * region is most of the case from weston
514    */
515   for (i = 0; i < nrects; i++) {
516      if (rects[i].x <= 0 && rects[i].y <= 0 &&
517          rects[i].x + rects[i].width >= pres->width0 &&
518          rects[i].y + rects[i].height >= pres->height0)
519         return;
520   }
521
522   struct pipe_scissor_state *bound = &damage->bound;
523   get_damage_bound_box(pres, rects, nrects, bound);
524
525   damage->region = CALLOC(nrects, sizeof(*damage->region));
526   if (!damage->region)
527      return;
528
529   for (i = 0; i < nrects; i++)
530      get_scissor_from_box(damage->region + i, rects + i,
531                           pres->height0);
532
533   /* is region aligned to tiles? */
534   damage->aligned = true;
535   for (i = 0; i < nrects; i++) {
536      if (rects[i].x & 0xf || rects[i].y & 0xf ||
537          rects[i].width & 0xf || rects[i].height & 0xf) {
538         damage->aligned = false;
539         break;
540      }
541   }
542
543   damage->num_region = nrects;
544}
545
546void
547lima_resource_screen_init(struct lima_screen *screen)
548{
549   screen->base.resource_create = lima_resource_create;
550   screen->base.resource_create_with_modifiers = lima_resource_create_with_modifiers;
551   screen->base.resource_from_handle = lima_resource_from_handle;
552   screen->base.resource_destroy = lima_resource_destroy;
553   screen->base.resource_get_handle = lima_resource_get_handle;
554   screen->base.resource_get_param = lima_resource_get_param;
555   screen->base.set_damage_region = lima_resource_set_damage_region;
556}
557
558static struct pipe_surface *
559lima_surface_create(struct pipe_context *pctx,
560                    struct pipe_resource *pres,
561                    const struct pipe_surface *surf_tmpl)
562{
563   struct lima_surface *surf = CALLOC_STRUCT(lima_surface);
564
565   if (!surf)
566      return NULL;
567
568   assert(surf_tmpl->u.tex.first_layer == surf_tmpl->u.tex.last_layer);
569
570   struct pipe_surface *psurf = &surf->base;
571   unsigned level = surf_tmpl->u.tex.level;
572
573   pipe_reference_init(&psurf->reference, 1);
574   pipe_resource_reference(&psurf->texture, pres);
575
576   psurf->context = pctx;
577   psurf->format = surf_tmpl->format;
578   psurf->width = u_minify(pres->width0, level);
579   psurf->height = u_minify(pres->height0, level);
580   psurf->u.tex.level = level;
581   psurf->u.tex.first_layer = surf_tmpl->u.tex.first_layer;
582   psurf->u.tex.last_layer = surf_tmpl->u.tex.last_layer;
583
584   surf->tiled_w = align(psurf->width, 16) >> 4;
585   surf->tiled_h = align(psurf->height, 16) >> 4;
586
587   surf->reload = 0;
588   if (util_format_has_stencil(util_format_description(psurf->format)))
589      surf->reload |= PIPE_CLEAR_STENCIL;
590   if (util_format_has_depth(util_format_description(psurf->format)))
591      surf->reload |= PIPE_CLEAR_DEPTH;
592   if (!util_format_is_depth_or_stencil(psurf->format))
593      surf->reload |= PIPE_CLEAR_COLOR0;
594
595   return &surf->base;
596}
597
598static void
599lima_surface_destroy(struct pipe_context *pctx, struct pipe_surface *psurf)
600{
601   struct lima_surface *surf = lima_surface(psurf);
602
603   pipe_resource_reference(&psurf->texture, NULL);
604   FREE(surf);
605}
606
607static void *
608lima_transfer_map(struct pipe_context *pctx,
609                  struct pipe_resource *pres,
610                  unsigned level,
611                  unsigned usage,
612                  const struct pipe_box *box,
613                  struct pipe_transfer **pptrans)
614{
615   struct lima_screen *screen = lima_screen(pres->screen);
616   struct lima_context *ctx = lima_context(pctx);
617   struct lima_resource *res = lima_resource(pres);
618   struct lima_bo *bo = res->bo;
619   struct lima_transfer *trans;
620   struct pipe_transfer *ptrans;
621
622   /* No direct mappings of tiled, since we need to manually
623    * tile/untile.
624    */
625   if (res->tiled && (usage & PIPE_MAP_DIRECTLY))
626      return NULL;
627
628   /* bo might be in use in a previous stream draw. Allocate a new
629    * one for the resource to avoid overwriting data in use. */
630   if (usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE) {
631      struct lima_bo *new_bo;
632      assert(res->bo && res->bo->size);
633
634      new_bo = lima_bo_create(screen, res->bo->size, res->bo->flags);
635      if (!new_bo)
636         return NULL;
637
638      lima_bo_unreference(res->bo);
639      res->bo = new_bo;
640
641      if (pres->bind & PIPE_BIND_VERTEX_BUFFER)
642         ctx->dirty |= LIMA_CONTEXT_DIRTY_VERTEX_BUFF;
643
644      bo = res->bo;
645   }
646   else if (!(usage & PIPE_MAP_UNSYNCHRONIZED) &&
647            (usage & PIPE_MAP_READ_WRITE)) {
648      /* use once buffers are made sure to not read/write overlapped
649       * range, so no need to sync */
650      lima_flush_job_accessing_bo(ctx, bo, usage & PIPE_MAP_WRITE);
651
652      unsigned op = usage & PIPE_MAP_WRITE ?
653         LIMA_GEM_WAIT_WRITE : LIMA_GEM_WAIT_READ;
654      lima_bo_wait(bo, op, PIPE_TIMEOUT_INFINITE);
655   }
656
657   if (!lima_bo_map(bo))
658      return NULL;
659
660   trans = slab_alloc(&ctx->transfer_pool);
661   if (!trans)
662      return NULL;
663
664   memset(trans, 0, sizeof(*trans));
665   ptrans = &trans->base;
666
667   pipe_resource_reference(&ptrans->resource, pres);
668   ptrans->level = level;
669   ptrans->usage = usage;
670   ptrans->box = *box;
671
672   *pptrans = ptrans;
673
674   if (res->tiled) {
675      ptrans->stride = util_format_get_stride(pres->format, ptrans->box.width);
676      ptrans->layer_stride = ptrans->stride * ptrans->box.height;
677
678      trans->staging = malloc(ptrans->stride * ptrans->box.height * ptrans->box.depth);
679
680      if (usage & PIPE_MAP_READ) {
681         unsigned i;
682         for (i = 0; i < ptrans->box.depth; i++)
683            panfrost_load_tiled_image(
684               trans->staging + i * ptrans->stride * ptrans->box.height,
685               bo->map + res->levels[level].offset + (i + box->z) * res->levels[level].layer_stride,
686               ptrans->box.x, ptrans->box.y,
687               ptrans->box.width, ptrans->box.height,
688               ptrans->stride,
689               res->levels[level].stride,
690               pres->format);
691      }
692
693      return trans->staging;
694   } else {
695      unsigned dpw = PIPE_MAP_DIRECTLY | PIPE_MAP_WRITE |
696                     PIPE_MAP_PERSISTENT;
697      if ((usage & dpw) == dpw && res->index_cache)
698         return NULL;
699
700      ptrans->stride = res->levels[level].stride;
701      ptrans->layer_stride = res->levels[level].layer_stride;
702
703      if ((usage & PIPE_MAP_WRITE) && (usage & PIPE_MAP_DIRECTLY))
704         panfrost_minmax_cache_invalidate(res->index_cache, ptrans);
705
706      return bo->map + res->levels[level].offset +
707         box->z * res->levels[level].layer_stride +
708         box->y / util_format_get_blockheight(pres->format) * ptrans->stride +
709         box->x / util_format_get_blockwidth(pres->format) *
710         util_format_get_blocksize(pres->format);
711   }
712}
713
714static void
715lima_transfer_flush_region(struct pipe_context *pctx,
716                           struct pipe_transfer *ptrans,
717                           const struct pipe_box *box)
718{
719
720}
721
722static bool
723lima_should_convert_linear(struct lima_resource *res,
724                           struct pipe_transfer *ptrans)
725{
726   if (res->modifier_constant)
727          return false;
728
729   /* Overwriting the entire resource indicates streaming, for which
730    * linear layout is most efficient due to the lack of expensive
731    * conversion.
732    *
733    * For now we just switch to linear after a number of complete
734    * overwrites to keep things simple, but we could do better.
735    */
736
737   unsigned depth = res->base.target == PIPE_TEXTURE_3D ?
738                    res->base.depth0 : res->base.array_size;
739   bool entire_overwrite =
740          res->base.last_level == 0 &&
741          ptrans->box.width == res->base.width0 &&
742          ptrans->box.height == res->base.height0 &&
743          ptrans->box.depth == depth &&
744          ptrans->box.x == 0 &&
745          ptrans->box.y == 0 &&
746          ptrans->box.z == 0;
747
748   if (entire_overwrite)
749          ++res->full_updates;
750
751   return res->full_updates >= LAYOUT_CONVERT_THRESHOLD;
752}
753
754static void
755lima_transfer_unmap_inner(struct lima_context *ctx,
756                          struct pipe_transfer *ptrans)
757{
758   struct lima_resource *res = lima_resource(ptrans->resource);
759   struct lima_transfer *trans = lima_transfer(ptrans);
760   struct lima_bo *bo = res->bo;
761   struct pipe_resource *pres;
762
763   if (trans->staging) {
764      pres = &res->base;
765      if (trans->base.usage & PIPE_MAP_WRITE) {
766         unsigned i;
767         if (lima_should_convert_linear(res, ptrans)) {
768            /* It's safe to re-use the same BO since tiled BO always has
769             * aligned dimensions */
770            for (i = 0; i < trans->base.box.depth; i++) {
771               util_copy_rect(bo->map + res->levels[0].offset +
772                                 (i + trans->base.box.z) * res->levels[0].stride,
773                              res->base.format,
774                              res->levels[0].stride,
775                              0, 0,
776                              ptrans->box.width,
777                              ptrans->box.height,
778                              trans->staging + i * ptrans->stride * ptrans->box.height,
779                              ptrans->stride,
780                              0, 0);
781            }
782            res->tiled = false;
783            res->modifier_constant = true;
784            /* Update texture descriptor */
785            ctx->dirty |= LIMA_CONTEXT_DIRTY_TEXTURES;
786         } else {
787            for (i = 0; i < trans->base.box.depth; i++)
788               panfrost_store_tiled_image(
789                  bo->map + res->levels[trans->base.level].offset + (i + trans->base.box.z) * res->levels[trans->base.level].layer_stride,
790                  trans->staging + i * ptrans->stride * ptrans->box.height,
791                  ptrans->box.x, ptrans->box.y,
792                  ptrans->box.width, ptrans->box.height,
793                  res->levels[ptrans->level].stride,
794                  ptrans->stride,
795                  pres->format);
796         }
797      }
798   }
799}
800
801static void
802lima_transfer_unmap(struct pipe_context *pctx,
803                    struct pipe_transfer *ptrans)
804{
805   struct lima_context *ctx = lima_context(pctx);
806   struct lima_transfer *trans = lima_transfer(ptrans);
807   struct lima_resource *res = lima_resource(ptrans->resource);
808
809   lima_transfer_unmap_inner(ctx, ptrans);
810   if (trans->staging)
811      free(trans->staging);
812   panfrost_minmax_cache_invalidate(res->index_cache, ptrans);
813
814   pipe_resource_reference(&ptrans->resource, NULL);
815   slab_free(&ctx->transfer_pool, trans);
816}
817
818static void
819lima_util_blitter_save_states(struct lima_context *ctx)
820{
821   util_blitter_save_blend(ctx->blitter, (void *)ctx->blend);
822   util_blitter_save_depth_stencil_alpha(ctx->blitter, (void *)ctx->zsa);
823   util_blitter_save_stencil_ref(ctx->blitter, &ctx->stencil_ref);
824   util_blitter_save_rasterizer(ctx->blitter, (void *)ctx->rasterizer);
825   util_blitter_save_fragment_shader(ctx->blitter, ctx->uncomp_fs);
826   util_blitter_save_vertex_shader(ctx->blitter, ctx->uncomp_vs);
827   util_blitter_save_viewport(ctx->blitter,
828                              &ctx->viewport.transform);
829   util_blitter_save_scissor(ctx->blitter, &ctx->scissor);
830   util_blitter_save_vertex_elements(ctx->blitter,
831                                     ctx->vertex_elements);
832   util_blitter_save_vertex_buffer_slot(ctx->blitter,
833                                        ctx->vertex_buffers.vb);
834
835   util_blitter_save_framebuffer(ctx->blitter, &ctx->framebuffer.base);
836
837   util_blitter_save_fragment_sampler_states(ctx->blitter,
838                                             ctx->tex_stateobj.num_samplers,
839                                             (void**)ctx->tex_stateobj.samplers);
840   util_blitter_save_fragment_sampler_views(ctx->blitter,
841                                            ctx->tex_stateobj.num_textures,
842                                            ctx->tex_stateobj.textures);
843}
844
845static void
846lima_blit(struct pipe_context *pctx, const struct pipe_blit_info *blit_info)
847{
848   struct lima_context *ctx = lima_context(pctx);
849   struct pipe_blit_info info = *blit_info;
850
851   if (util_try_blit_via_copy_region(pctx, &info)) {
852      return; /* done */
853   }
854
855   if (info.mask & PIPE_MASK_S) {
856      debug_printf("lima: cannot blit stencil, skipping\n");
857      info.mask &= ~PIPE_MASK_S;
858   }
859
860   if (!util_blitter_is_blit_supported(ctx->blitter, &info)) {
861      debug_printf("lima: blit unsupported %s -> %s\n",
862                   util_format_short_name(info.src.resource->format),
863                   util_format_short_name(info.dst.resource->format));
864      return;
865   }
866
867   lima_util_blitter_save_states(ctx);
868
869   util_blitter_blit(ctx->blitter, &info);
870}
871
872static void
873lima_flush_resource(struct pipe_context *pctx, struct pipe_resource *resource)
874{
875
876}
877
878static void
879lima_texture_subdata(struct pipe_context *pctx,
880                     struct pipe_resource *prsc,
881                     unsigned level,
882                     unsigned usage,
883                     const struct pipe_box *box,
884                     const void *data,
885                     unsigned stride,
886                     unsigned layer_stride)
887{
888   struct lima_context *ctx = lima_context(pctx);
889   struct lima_resource *res = lima_resource(prsc);
890
891   if (!res->tiled) {
892      u_default_texture_subdata(pctx, prsc, level, usage, box,
893                                data, stride, layer_stride);
894      return;
895   }
896
897   assert(!(usage & PIPE_MAP_READ));
898
899   struct lima_transfer t = {
900      .base = {
901         .resource = prsc,
902         .usage = PIPE_MAP_WRITE,
903         .level = level,
904         .box = *box,
905         .stride = stride,
906         .layer_stride = layer_stride,
907      },
908      .staging = (void *)data,
909   };
910
911   lima_flush_job_accessing_bo(ctx, res->bo, true);
912   lima_bo_wait(res->bo, LIMA_GEM_WAIT_WRITE, PIPE_TIMEOUT_INFINITE);
913   if (!lima_bo_map(res->bo))
914      return;
915
916   lima_transfer_unmap_inner(ctx, &t.base);
917}
918
919void
920lima_resource_context_init(struct lima_context *ctx)
921{
922   ctx->base.create_surface = lima_surface_create;
923   ctx->base.surface_destroy = lima_surface_destroy;
924
925   ctx->base.buffer_subdata = u_default_buffer_subdata;
926   ctx->base.texture_subdata = lima_texture_subdata;
927   /* TODO: optimize resource_copy_region to do copy directly
928    * between 2 tiled or tiled and linear resources instead of
929    * using staging buffer.
930    */
931   ctx->base.resource_copy_region = util_resource_copy_region;
932
933   ctx->base.blit = lima_blit;
934
935   ctx->base.buffer_map = lima_transfer_map;
936   ctx->base.texture_map = lima_transfer_map;
937   ctx->base.transfer_flush_region = lima_transfer_flush_region;
938   ctx->base.buffer_unmap = lima_transfer_unmap;
939   ctx->base.texture_unmap = lima_transfer_unmap;
940
941   ctx->base.flush_resource = lima_flush_resource;
942}
943