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