• 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_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 
52 static struct pipe_resource *
lima_resource_create_scanout(struct pipe_screen * pscreen,const struct pipe_resource * templat,unsigned width,unsigned height)53 lima_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 
88 static uint32_t
setup_miptree(struct lima_resource * res,unsigned width0,unsigned height0,bool should_align_dimensions)89 setup_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 
143 static struct pipe_resource *
lima_resource_create_bo(struct pipe_screen * pscreen,const struct pipe_resource * templat,unsigned width,unsigned height,bool should_align_dimensions)144 lima_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 
175 static struct pipe_resource *
_lima_resource_create_with_modifiers(struct pipe_screen * pscreen,const struct pipe_resource * templat,const uint64_t * modifiers,int count)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 
240 static struct pipe_resource *
lima_resource_create(struct pipe_screen * pscreen,const struct pipe_resource * templat)241 lima_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 
249 static struct pipe_resource *
lima_resource_create_with_modifiers(struct pipe_screen * pscreen,const struct pipe_resource * templat,const uint64_t * modifiers,int count)250 lima_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 
268 static void
lima_resource_destroy(struct pipe_screen * pscreen,struct pipe_resource * pres)269 lima_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 
289 static struct pipe_resource *
lima_resource_from_handle(struct pipe_screen * pscreen,const struct pipe_resource * templat,struct winsys_handle * handle,unsigned usage)290 lima_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 
398 err_out:
399    lima_resource_destroy(pscreen, pres);
400    return NULL;
401 }
402 
403 static bool
lima_resource_get_handle(struct pipe_screen * pscreen,struct pipe_context * pctx,struct pipe_resource * pres,struct winsys_handle * handle,unsigned usage)404 lima_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 
430 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)431 lima_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 
459 static void
get_scissor_from_box(struct pipe_scissor_state * s,const struct pipe_box * b,int h)460 get_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 
471 static void
get_damage_bound_box(struct pipe_resource * pres,const struct pipe_box * rects,unsigned int nrects,struct pipe_scissor_state * bound)472 get_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 
489 static void
lima_resource_set_damage_region(struct pipe_screen * pscreen,struct pipe_resource * pres,unsigned int nrects,const struct pipe_box * rects)490 lima_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 
546 void
lima_resource_screen_init(struct lima_screen * screen)547 lima_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 
558 static struct pipe_surface *
lima_surface_create(struct pipe_context * pctx,struct pipe_resource * pres,const struct pipe_surface * surf_tmpl)559 lima_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 
598 static void
lima_surface_destroy(struct pipe_context * pctx,struct pipe_surface * psurf)599 lima_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 
607 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)608 lima_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 
714 static void
lima_transfer_flush_region(struct pipe_context * pctx,struct pipe_transfer * ptrans,const struct pipe_box * box)715 lima_transfer_flush_region(struct pipe_context *pctx,
716                            struct pipe_transfer *ptrans,
717                            const struct pipe_box *box)
718 {
719 
720 }
721 
722 static bool
lima_should_convert_linear(struct lima_resource * res,struct pipe_transfer * ptrans)723 lima_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 
754 static void
lima_transfer_unmap_inner(struct lima_context * ctx,struct pipe_transfer * ptrans)755 lima_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 
801 static void
lima_transfer_unmap(struct pipe_context * pctx,struct pipe_transfer * ptrans)802 lima_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 
818 static void
lima_util_blitter_save_states(struct lima_context * ctx)819 lima_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 
845 static void
lima_blit(struct pipe_context * pctx,const struct pipe_blit_info * blit_info)846 lima_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 
872 static void
lima_flush_resource(struct pipe_context * pctx,struct pipe_resource * resource)873 lima_flush_resource(struct pipe_context *pctx, struct pipe_resource *resource)
874 {
875 
876 }
877 
878 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,unsigned layer_stride)879 lima_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 
919 void
lima_resource_context_init(struct lima_context * ctx)920 lima_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