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