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