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