lima_resource.c revision 9f464c52
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/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/u_drm.h"
35#include "renderonly/renderonly.h"
36
37#include "state_tracker/drm_driver.h"
38
39#include "drm-uapi/drm_fourcc.h"
40#include "drm-uapi/lima_drm.h"
41
42#include "lima_screen.h"
43#include "lima_context.h"
44#include "lima_resource.h"
45#include "lima_bo.h"
46#include "lima_util.h"
47#include "lima_tiling.h"
48
49static struct pipe_resource *
50lima_resource_create_scanout(struct pipe_screen *pscreen,
51                             const struct pipe_resource *templat,
52                             unsigned width, unsigned height)
53{
54   struct lima_screen *screen = lima_screen(pscreen);
55   struct renderonly_scanout *scanout;
56   struct winsys_handle handle;
57   struct pipe_resource *pres;
58
59   struct pipe_resource scanout_templat = *templat;
60   scanout_templat.width0 = width;
61   scanout_templat.height0 = height;
62   scanout_templat.screen = pscreen;
63
64   scanout = renderonly_scanout_for_resource(&scanout_templat,
65                                             screen->ro, &handle);
66   if (!scanout)
67      return NULL;
68
69   assert(handle.type == WINSYS_HANDLE_TYPE_FD);
70   pres = pscreen->resource_from_handle(pscreen, templat, &handle,
71                                        PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE);
72
73   close(handle.handle);
74   if (!pres) {
75      renderonly_scanout_destroy(scanout, screen->ro);
76      return NULL;
77   }
78
79   struct lima_resource *res = lima_resource(pres);
80   res->scanout = scanout;
81
82   return pres;
83}
84
85static uint32_t
86setup_miptree(struct lima_resource *res,
87              unsigned width0, unsigned height0,
88              bool should_align_dimensions)
89{
90   struct pipe_resource *pres = &res->base;
91   unsigned level;
92   unsigned width = width0;
93   unsigned height = height0;
94   unsigned depth = pres->depth0;
95   uint32_t size = 0;
96
97   for (level = 0; level <= pres->last_level; level++) {
98      uint32_t actual_level_size;
99      uint32_t stride;
100      unsigned aligned_width;
101      unsigned aligned_height;
102
103      if (should_align_dimensions) {
104         aligned_width = align(width, 16);
105         aligned_height = align(height, 16);
106      } else {
107         aligned_width = width;
108         aligned_height = height;
109      }
110
111      stride = util_format_get_stride(pres->format, aligned_width);
112      actual_level_size = stride *
113         util_format_get_nblocksy(pres->format, aligned_height) *
114         pres->array_size * depth;
115
116      res->levels[level].width = aligned_width;
117      res->levels[level].stride = stride;
118      res->levels[level].offset = size;
119
120      /* The start address of each level <= 10 must be 64-aligned
121       * in order to be able to pass the addresses
122       * to the hardware.
123       * The start addresses of level 11 and level 12 are passed
124       * implicitely: they start at an offset of respectively
125       * 0x0400 and 0x0800 from the start address of level 10 */
126      if (level < 10)
127         size += align(actual_level_size, 64);
128      else if (level != pres->last_level)
129         size += 0x0400;
130      else
131         size += actual_level_size;  /* Save some memory */
132
133      width = u_minify(width, 1);
134      height = u_minify(height, 1);
135      depth = u_minify(depth, 1);
136   }
137
138   return size;
139}
140
141static struct pipe_resource *
142lima_resource_create_bo(struct pipe_screen *pscreen,
143                        const struct pipe_resource *templat,
144                        unsigned width, unsigned height,
145                        bool should_align_dimensions)
146{
147   struct lima_screen *screen = lima_screen(pscreen);
148   struct lima_resource *res;
149   struct pipe_resource *pres;
150
151   res = CALLOC_STRUCT(lima_resource);
152   if (!res)
153      return NULL;
154
155   res->base = *templat;
156   res->base.screen = pscreen;
157   pipe_reference_init(&res->base.reference, 1);
158
159   pres = &res->base;
160
161   uint32_t size = setup_miptree(res, width, height, should_align_dimensions);
162   size = align(size, LIMA_PAGE_SIZE);
163
164   res->bo = lima_bo_create(screen, size, 0);
165   if (!res->bo) {
166      FREE(res);
167      return NULL;
168   }
169
170   return pres;
171}
172
173static struct pipe_resource *
174_lima_resource_create_with_modifiers(struct pipe_screen *pscreen,
175                                     const struct pipe_resource *templat,
176                                     const uint64_t *modifiers,
177                                     int count)
178{
179   struct lima_screen *screen = lima_screen(pscreen);
180   bool should_tile = false;
181   unsigned width, height;
182   bool should_align_dimensions;
183
184   /* VBOs/PBOs are untiled (and 1 height). */
185   if (templat->target == PIPE_BUFFER)
186      should_tile = false;
187
188   if (templat->bind & (PIPE_BIND_LINEAR | PIPE_BIND_SCANOUT))
189      should_tile = false;
190
191   /* if linear buffer is not allowed, alloc fail */
192   if (!should_tile && !drm_find_modifier(DRM_FORMAT_MOD_LINEAR, modifiers, count))
193      return NULL;
194
195   if (should_tile || (templat->bind & PIPE_BIND_RENDER_TARGET) ||
196       (templat->bind & PIPE_BIND_DEPTH_STENCIL)) {
197      should_align_dimensions = true;
198      width = align(templat->width0, 16);
199      height = align(templat->height0, 16);
200   }
201   else {
202      should_align_dimensions = false;
203      width = templat->width0;
204      height = templat->height0;
205   }
206
207   struct pipe_resource *pres;
208   if (screen->ro && (templat->bind & PIPE_BIND_SCANOUT))
209      pres = lima_resource_create_scanout(pscreen, templat, width, height);
210   else
211      pres = lima_resource_create_bo(pscreen, templat, width, height,
212                                     should_align_dimensions);
213
214   if (pres) {
215      struct lima_resource *res = lima_resource(pres);
216      res->tiled = should_tile;
217
218      debug_printf("%s: pres=%p width=%u height=%u depth=%u target=%d "
219                   "bind=%x usage=%d tile=%d last_level=%d\n", __func__,
220                   pres, pres->width0, pres->height0, pres->depth0,
221                   pres->target, pres->bind, pres->usage, should_tile, templat->last_level);
222   }
223   return pres;
224}
225
226static struct pipe_resource *
227lima_resource_create(struct pipe_screen *pscreen,
228                     const struct pipe_resource *templat)
229{
230   static const uint64_t modifiers[] = {
231      DRM_FORMAT_MOD_LINEAR,
232   };
233   return _lima_resource_create_with_modifiers(pscreen, templat, modifiers, ARRAY_SIZE(modifiers));
234}
235
236static struct pipe_resource *
237lima_resource_create_with_modifiers(struct pipe_screen *pscreen,
238                                    const struct pipe_resource *templat,
239                                    const uint64_t *modifiers,
240                                    int count)
241{
242   struct pipe_resource tmpl = *templat;
243
244   /* gbm_bo_create_with_modifiers & gbm_surface_create_with_modifiers
245    * don't have usage parameter, but buffer created by these functions
246    * may be used for scanout. So we assume buffer created by this
247    * function always enable scanout if linear modifier is permitted.
248    */
249   if (drm_find_modifier(DRM_FORMAT_MOD_LINEAR, modifiers, count))
250      tmpl.bind |= PIPE_BIND_SCANOUT;
251
252   return _lima_resource_create_with_modifiers(pscreen, &tmpl, modifiers, count);
253}
254
255static void
256lima_resource_destroy(struct pipe_screen *pscreen, struct pipe_resource *pres)
257{
258   struct lima_screen *screen = lima_screen(pscreen);
259   struct lima_resource *res = lima_resource(pres);
260
261   if (res->bo)
262      lima_bo_free(res->bo);
263
264   if (res->scanout)
265      renderonly_scanout_destroy(res->scanout, screen->ro);
266
267   FREE(res);
268}
269
270static struct pipe_resource *
271lima_resource_from_handle(struct pipe_screen *pscreen,
272        const struct pipe_resource *templat,
273        struct winsys_handle *handle, unsigned usage)
274{
275   struct lima_resource *res;
276   struct lima_screen *screen = lima_screen(pscreen);
277
278   res = CALLOC_STRUCT(lima_resource);
279   if (!res)
280      return NULL;
281
282   struct pipe_resource *pres = &res->base;
283   *pres = *templat;
284   pres->screen = pscreen;
285   pipe_reference_init(&pres->reference, 1);
286   res->levels[0].offset = 0;
287   res->levels[0].stride = handle->stride;
288
289   res->bo = lima_bo_import(screen, handle);
290   if (!res->bo) {
291      FREE(res);
292      return NULL;
293   }
294
295   /* check alignment for the buffer */
296   if (pres->bind & PIPE_BIND_RENDER_TARGET) {
297      unsigned width, height, stride, size;
298
299      width = align(pres->width0, 16);
300      height = align(pres->height0, 16);
301      stride = util_format_get_stride(pres->format, width);
302      size = util_format_get_2d_size(pres->format, stride, height);
303
304      if (res->levels[0].stride != stride || res->bo->size < size) {
305         debug_error("import buffer not properly aligned\n");
306         goto err_out;
307      }
308
309      res->levels[0].width = width;
310   }
311   else
312      res->levels[0].width = pres->width0;
313
314   handle->modifier = DRM_FORMAT_MOD_LINEAR;
315   res->tiled = false;
316
317   return pres;
318
319err_out:
320   lima_resource_destroy(pscreen, pres);
321   return NULL;
322}
323
324static boolean
325lima_resource_get_handle(struct pipe_screen *pscreen,
326                         struct pipe_context *pctx,
327                         struct pipe_resource *pres,
328                         struct winsys_handle *handle, unsigned usage)
329{
330   struct lima_screen *screen = lima_screen(pscreen);
331   struct lima_resource *res = lima_resource(pres);
332
333   handle->modifier = DRM_FORMAT_MOD_LINEAR;
334
335   if (handle->type == WINSYS_HANDLE_TYPE_KMS && screen->ro &&
336       renderonly_get_handle(res->scanout, handle))
337      return TRUE;
338
339   if (!lima_bo_export(res->bo, handle))
340      return FALSE;
341
342   handle->stride = res->levels[0].stride;
343   return TRUE;
344}
345
346void
347lima_resource_screen_init(struct lima_screen *screen)
348{
349   screen->base.resource_create = lima_resource_create;
350   screen->base.resource_create_with_modifiers = lima_resource_create_with_modifiers;
351   screen->base.resource_from_handle = lima_resource_from_handle;
352   screen->base.resource_destroy = lima_resource_destroy;
353   screen->base.resource_get_handle = lima_resource_get_handle;
354}
355
356static struct pipe_surface *
357lima_surface_create(struct pipe_context *pctx,
358                    struct pipe_resource *pres,
359                    const struct pipe_surface *surf_tmpl)
360{
361   struct lima_surface *surf = CALLOC_STRUCT(lima_surface);
362
363   if (!surf)
364      return NULL;
365
366   assert(surf_tmpl->u.tex.first_layer == surf_tmpl->u.tex.last_layer);
367
368   struct pipe_surface *psurf = &surf->base;
369   unsigned level = surf_tmpl->u.tex.level;
370
371   pipe_reference_init(&psurf->reference, 1);
372   pipe_resource_reference(&psurf->texture, pres);
373
374   psurf->context = pctx;
375   psurf->format = surf_tmpl->format;
376   psurf->width = u_minify(pres->width0, level);
377   psurf->height = u_minify(pres->height0, level);
378   psurf->u.tex.level = level;
379   psurf->u.tex.first_layer = surf_tmpl->u.tex.first_layer;
380   psurf->u.tex.last_layer = surf_tmpl->u.tex.last_layer;
381
382   surf->tiled_w = align(psurf->width, 16) >> 4;
383   surf->tiled_h = align(psurf->height, 16) >> 4;
384
385   struct lima_context *ctx = lima_context(pctx);
386   if (ctx->plb_pp_stream) {
387      struct lima_ctx_plb_pp_stream_key key = {
388         .tiled_w = surf->tiled_w,
389         .tiled_h = surf->tiled_h,
390      };
391
392      for (int i = 0; i < lima_ctx_num_plb; i++) {
393         key.plb_index = i;
394
395         struct hash_entry *entry =
396            _mesa_hash_table_search(ctx->plb_pp_stream, &key);
397         if (entry) {
398            struct lima_ctx_plb_pp_stream *s = entry->data;
399            s->refcnt++;
400         }
401         else {
402            struct lima_ctx_plb_pp_stream *s =
403               ralloc(ctx->plb_pp_stream, struct lima_ctx_plb_pp_stream);
404            s->key.plb_index = i;
405            s->key.tiled_w = surf->tiled_w;
406            s->key.tiled_h = surf->tiled_h;
407            s->refcnt = 1;
408            s->bo = NULL;
409            _mesa_hash_table_insert(ctx->plb_pp_stream, &s->key, s);
410         }
411      }
412   }
413
414   return &surf->base;
415}
416
417static void
418lima_surface_destroy(struct pipe_context *pctx, struct pipe_surface *psurf)
419{
420   struct lima_surface *surf = lima_surface(psurf);
421   /* psurf->context may be not equal with pctx (i.e. glxinfo) */
422   struct lima_context *ctx = lima_context(psurf->context);
423
424   if (ctx->plb_pp_stream) {
425      struct lima_ctx_plb_pp_stream_key key = {
426         .tiled_w = surf->tiled_w,
427         .tiled_h = surf->tiled_h,
428      };
429
430      for (int i = 0; i < lima_ctx_num_plb; i++) {
431         key.plb_index = i;
432
433         struct hash_entry *entry =
434            _mesa_hash_table_search(ctx->plb_pp_stream, &key);
435         struct lima_ctx_plb_pp_stream *s = entry->data;
436         if (--s->refcnt == 0) {
437            if (s->bo)
438               lima_bo_free(s->bo);
439            _mesa_hash_table_remove(ctx->plb_pp_stream, entry);
440            ralloc_free(s);
441         }
442      }
443   }
444
445   pipe_resource_reference(&psurf->texture, NULL);
446   FREE(surf);
447}
448
449static void *
450lima_transfer_map(struct pipe_context *pctx,
451                  struct pipe_resource *pres,
452                  unsigned level,
453                  unsigned usage,
454                  const struct pipe_box *box,
455                  struct pipe_transfer **pptrans)
456{
457   struct lima_context *ctx = lima_context(pctx);
458   struct lima_resource *res = lima_resource(pres);
459   struct lima_bo *bo = res->bo;
460   struct lima_transfer *trans;
461   struct pipe_transfer *ptrans;
462
463   /* No direct mappings of tiled, since we need to manually
464    * tile/untile.
465    */
466   if (res->tiled && (usage & PIPE_TRANSFER_MAP_DIRECTLY))
467      return NULL;
468
469   /* use once buffers are made sure to not read/write overlapped
470    * range, so no need to sync */
471   if (pres->usage != PIPE_USAGE_STREAM) {
472      if (usage & PIPE_TRANSFER_READ_WRITE) {
473         if (lima_need_flush(ctx, bo, usage & PIPE_TRANSFER_WRITE))
474            lima_flush(ctx);
475
476         unsigned op = usage & PIPE_TRANSFER_WRITE ?
477            LIMA_GEM_WAIT_WRITE : LIMA_GEM_WAIT_READ;
478         lima_bo_wait(bo, op, PIPE_TIMEOUT_INFINITE);
479      }
480   }
481
482   if (!lima_bo_map(bo))
483      return NULL;
484
485   trans = slab_alloc(&ctx->transfer_pool);
486   if (!trans)
487      return NULL;
488
489   memset(trans, 0, sizeof(*trans));
490   ptrans = &trans->base;
491
492   pipe_resource_reference(&ptrans->resource, pres);
493   ptrans->level = level;
494   ptrans->usage = usage;
495   ptrans->box = *box;
496
497   *pptrans = ptrans;
498
499   if (res->tiled) {
500      ptrans->stride = util_format_get_stride(pres->format, ptrans->box.width);
501      ptrans->layer_stride = ptrans->stride * ptrans->box.height;
502
503      trans->staging = malloc(ptrans->stride * ptrans->box.height * ptrans->box.depth);
504
505      if (usage & PIPE_TRANSFER_READ)
506         lima_load_tiled_image(trans->staging, bo->map + res->levels[level].offset,
507                              &ptrans->box,
508                              ptrans->stride,
509                              res->levels[level].stride,
510                              util_format_get_blocksize(pres->format));
511
512      return trans->staging;
513   } else {
514      ptrans->stride = res->levels[level].stride;
515      ptrans->layer_stride = ptrans->stride * box->height;
516
517      return bo->map + res->levels[level].offset +
518         box->z * ptrans->layer_stride +
519         box->y / util_format_get_blockheight(pres->format) * ptrans->stride +
520         box->x / util_format_get_blockwidth(pres->format) *
521         util_format_get_blocksize(pres->format);
522   }
523}
524
525static void
526lima_transfer_flush_region(struct pipe_context *pctx,
527                           struct pipe_transfer *ptrans,
528                           const struct pipe_box *box)
529{
530
531}
532
533static void
534lima_transfer_unmap(struct pipe_context *pctx,
535                    struct pipe_transfer *ptrans)
536{
537   struct lima_context *ctx = lima_context(pctx);
538   struct lima_transfer *trans = lima_transfer(ptrans);
539   struct lima_resource *res = lima_resource(ptrans->resource);
540   struct lima_bo *bo = res->bo;
541   struct pipe_resource *pres;
542
543   if (trans->staging) {
544      pres = &res->base;
545      if (ptrans->usage & PIPE_TRANSFER_WRITE)
546         lima_store_tiled_image(bo->map + res->levels[ptrans->level].offset, trans->staging,
547                              &ptrans->box,
548                              res->levels[ptrans->level].stride,
549                              ptrans->stride,
550                              util_format_get_blocksize(pres->format));
551      free(trans->staging);
552   }
553
554   pipe_resource_reference(&ptrans->resource, NULL);
555   slab_free(&ctx->transfer_pool, trans);
556}
557
558static void
559lima_util_blitter_save_states(struct lima_context *ctx)
560{
561   util_blitter_save_blend(ctx->blitter, (void *)ctx->blend);
562   util_blitter_save_depth_stencil_alpha(ctx->blitter, (void *)ctx->zsa);
563   util_blitter_save_stencil_ref(ctx->blitter, &ctx->stencil_ref);
564   util_blitter_save_rasterizer(ctx->blitter, (void *)ctx->rasterizer);
565   util_blitter_save_fragment_shader(ctx->blitter, ctx->fs);
566   util_blitter_save_vertex_shader(ctx->blitter, ctx->vs);
567   util_blitter_save_viewport(ctx->blitter,
568                              &ctx->viewport.transform);
569   util_blitter_save_scissor(ctx->blitter, &ctx->scissor);
570   util_blitter_save_vertex_elements(ctx->blitter,
571                                     ctx->vertex_elements);
572   util_blitter_save_vertex_buffer_slot(ctx->blitter,
573                                        ctx->vertex_buffers.vb);
574
575   util_blitter_save_framebuffer(ctx->blitter, &ctx->framebuffer.base);
576
577   util_blitter_save_fragment_sampler_states(ctx->blitter,
578                                             ctx->tex_stateobj.num_samplers,
579                                             (void**)ctx->tex_stateobj.samplers);
580   util_blitter_save_fragment_sampler_views(ctx->blitter,
581                                            ctx->tex_stateobj.num_textures,
582                                            ctx->tex_stateobj.textures);
583}
584
585static void
586lima_blit(struct pipe_context *pctx, const struct pipe_blit_info *blit_info)
587{
588   struct lima_context *ctx = lima_context(pctx);
589   struct pipe_blit_info info = *blit_info;
590
591   if (util_try_blit_via_copy_region(pctx, &info)) {
592      return; /* done */
593   }
594
595   if (info.mask & PIPE_MASK_S) {
596      debug_printf("lima: cannot blit stencil, skipping\n");
597      info.mask &= ~PIPE_MASK_S;
598   }
599
600   if (!util_blitter_is_blit_supported(ctx->blitter, &info)) {
601      debug_printf("lima: blit unsupported %s -> %s\n",
602                   util_format_short_name(info.src.resource->format),
603                   util_format_short_name(info.dst.resource->format));
604      return;
605   }
606
607   lima_util_blitter_save_states(ctx);
608
609   util_blitter_blit(ctx->blitter, &info);
610}
611
612static void
613lima_flush_resource(struct pipe_context *pctx, struct pipe_resource *resource)
614{
615
616}
617
618void
619lima_resource_context_init(struct lima_context *ctx)
620{
621   ctx->base.create_surface = lima_surface_create;
622   ctx->base.surface_destroy = lima_surface_destroy;
623
624   /* TODO: optimize these functions to read/write data directly
625    * from/to target instead of creating a staging memory for tiled
626    * buffer indirectly
627    */
628   ctx->base.buffer_subdata = u_default_buffer_subdata;
629   ctx->base.texture_subdata = u_default_texture_subdata;
630   ctx->base.resource_copy_region = util_resource_copy_region;
631
632   ctx->base.blit = lima_blit;
633
634   ctx->base.transfer_map = lima_transfer_map;
635   ctx->base.transfer_flush_region = lima_transfer_flush_region;
636   ctx->base.transfer_unmap = lima_transfer_unmap;
637
638   ctx->base.flush_resource = lima_flush_resource;
639}
640