• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2012-2015 Etnaviv 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  * Authors:
24  *    Wladimir J. van der Laan <laanwj@gmail.com>
25  */
26 
27 #include "etnaviv_resource.h"
28 
29 #include "hw/common.xml.h"
30 
31 #include "etnaviv_context.h"
32 #include "etnaviv_debug.h"
33 #include "etnaviv_screen.h"
34 #include "etnaviv_translate.h"
35 
36 #include "util/u_inlines.h"
37 #include "util/u_memory.h"
38 
39 #include <drm_fourcc.h>
40 
41 #ifndef DRM_FORMAT_MOD_INVALID
42 #define DRM_FORMAT_MOD_INVALID ((1ULL<<56) - 1)
43 #endif
44 
modifier_to_layout(uint64_t modifier)45 static enum etna_surface_layout modifier_to_layout(uint64_t modifier)
46 {
47    switch (modifier) {
48    case DRM_FORMAT_MOD_VIVANTE_TILED:
49       return ETNA_LAYOUT_TILED;
50    case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
51       return ETNA_LAYOUT_SUPER_TILED;
52    case DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED:
53       return ETNA_LAYOUT_MULTI_TILED;
54    case DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED:
55       return ETNA_LAYOUT_MULTI_SUPERTILED;
56    case DRM_FORMAT_MOD_LINEAR:
57    default:
58       return ETNA_LAYOUT_LINEAR;
59    }
60 }
61 
layout_to_modifier(enum etna_surface_layout layout)62 static uint64_t layout_to_modifier(enum etna_surface_layout layout)
63 {
64    switch (layout) {
65    case ETNA_LAYOUT_TILED:
66       return DRM_FORMAT_MOD_VIVANTE_TILED;
67    case ETNA_LAYOUT_SUPER_TILED:
68       return DRM_FORMAT_MOD_VIVANTE_SUPER_TILED;
69    case ETNA_LAYOUT_MULTI_TILED:
70       return DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED;
71    case ETNA_LAYOUT_MULTI_SUPERTILED:
72       return DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED;
73    case ETNA_LAYOUT_LINEAR:
74       return DRM_FORMAT_MOD_LINEAR;
75    default:
76       return DRM_FORMAT_MOD_INVALID;
77    }
78 }
79 
80 /* A tile is 4x4 pixels, having 'screen->specs.bits_per_tile' of tile status.
81  * So, in a buffer of N pixels, there are N / (4 * 4) tiles.
82  * We need N * screen->specs.bits_per_tile / (4 * 4) bits of tile status, or
83  * N * screen->specs.bits_per_tile / (4 * 4 * 8) bytes.
84  */
85 bool
etna_screen_resource_alloc_ts(struct pipe_screen * pscreen,struct etna_resource * rsc)86 etna_screen_resource_alloc_ts(struct pipe_screen *pscreen,
87                               struct etna_resource *rsc)
88 {
89    struct etna_screen *screen = etna_screen(pscreen);
90    size_t rt_ts_size, ts_layer_stride, pixels;
91 
92    assert(!rsc->ts_bo);
93 
94    /* TS only for level 0 -- XXX is this formula correct? */
95    pixels = rsc->levels[0].layer_stride / util_format_get_blocksize(rsc->base.format);
96    ts_layer_stride = align(pixels * screen->specs.bits_per_tile / 0x80,
97                            0x100 * screen->specs.pixel_pipes);
98    rt_ts_size = ts_layer_stride * rsc->base.array_size;
99    if (rt_ts_size == 0)
100       return true;
101 
102    DBG_F(ETNA_DBG_RESOURCE_MSGS, "%p: Allocating tile status of size %zu",
103          rsc, rt_ts_size);
104 
105    struct etna_bo *rt_ts;
106    rt_ts = etna_bo_new(screen->dev, rt_ts_size, DRM_ETNA_GEM_CACHE_WC);
107 
108    if (unlikely(!rt_ts)) {
109       BUG("Problem allocating tile status for resource");
110       return false;
111    }
112 
113    rsc->ts_bo = rt_ts;
114    rsc->levels[0].ts_offset = 0;
115    rsc->levels[0].ts_layer_stride = ts_layer_stride;
116    rsc->levels[0].ts_size = rt_ts_size;
117 
118    /* It is important to initialize the TS, as random pattern
119     * can result in crashes. Do this on the CPU as this only happens once
120     * per surface anyway and it's a small area, so it may not be worth
121     * queuing this to the GPU. */
122    void *ts_map = etna_bo_map(rt_ts);
123    memset(ts_map, screen->specs.ts_clear_value, rt_ts_size);
124 
125    return true;
126 }
127 
128 static boolean
etna_screen_can_create_resource(struct pipe_screen * pscreen,const struct pipe_resource * templat)129 etna_screen_can_create_resource(struct pipe_screen *pscreen,
130                                 const struct pipe_resource *templat)
131 {
132    struct etna_screen *screen = etna_screen(pscreen);
133    if (!translate_samples_to_xyscale(templat->nr_samples, NULL, NULL, NULL))
134       return false;
135 
136    /* templat->bind is not set here, so we must use the minimum sizes */
137    uint max_size =
138       MIN2(screen->specs.max_rendertarget_size, screen->specs.max_texture_size);
139 
140    if (templat->width0 > max_size || templat->height0 > max_size)
141       return false;
142 
143    return true;
144 }
145 
146 static unsigned
setup_miptree(struct etna_resource * rsc,unsigned paddingX,unsigned paddingY,unsigned msaa_xscale,unsigned msaa_yscale)147 setup_miptree(struct etna_resource *rsc, unsigned paddingX, unsigned paddingY,
148               unsigned msaa_xscale, unsigned msaa_yscale)
149 {
150    struct pipe_resource *prsc = &rsc->base;
151    unsigned level, size = 0;
152    unsigned width = prsc->width0;
153    unsigned height = prsc->height0;
154    unsigned depth = prsc->depth0;
155 
156    for (level = 0; level <= prsc->last_level; level++) {
157       struct etna_resource_level *mip = &rsc->levels[level];
158 
159       mip->width = width;
160       mip->height = height;
161       mip->padded_width = align(width * msaa_xscale, paddingX);
162       mip->padded_height = align(height * msaa_yscale, paddingY);
163       mip->stride = util_format_get_stride(prsc->format, mip->padded_width);
164       mip->offset = size;
165       mip->layer_stride = mip->stride * util_format_get_nblocksy(prsc->format, mip->padded_height);
166       mip->size = prsc->array_size * mip->layer_stride;
167 
168       /* align levels to 64 bytes to be able to render to them */
169       size += align(mip->size, ETNA_PE_ALIGNMENT) * depth;
170 
171       width = u_minify(width, 1);
172       height = u_minify(height, 1);
173       depth = u_minify(depth, 1);
174    }
175 
176    return size;
177 }
178 
179 /* Create a new resource object, using the given template info */
180 struct pipe_resource *
etna_resource_alloc(struct pipe_screen * pscreen,unsigned layout,uint64_t modifier,const struct pipe_resource * templat)181 etna_resource_alloc(struct pipe_screen *pscreen, unsigned layout,
182                     uint64_t modifier, const struct pipe_resource *templat)
183 {
184    struct etna_screen *screen = etna_screen(pscreen);
185    struct etna_resource *rsc;
186    unsigned size;
187 
188    DBG_F(ETNA_DBG_RESOURCE_MSGS,
189          "target=%d, format=%s, %ux%ux%u, array_size=%u, "
190          "last_level=%u, nr_samples=%u, usage=%u, bind=%x, flags=%x",
191          templat->target, util_format_name(templat->format), templat->width0,
192          templat->height0, templat->depth0, templat->array_size,
193          templat->last_level, templat->nr_samples, templat->usage,
194          templat->bind, templat->flags);
195 
196    /* Determine scaling for antialiasing, allow override using debug flag */
197    int nr_samples = templat->nr_samples;
198    if ((templat->bind & (PIPE_BIND_RENDER_TARGET | PIPE_BIND_DEPTH_STENCIL)) &&
199        !(templat->bind & PIPE_BIND_SAMPLER_VIEW)) {
200       if (DBG_ENABLED(ETNA_DBG_MSAA_2X))
201          nr_samples = 2;
202       if (DBG_ENABLED(ETNA_DBG_MSAA_4X))
203          nr_samples = 4;
204    }
205 
206    int msaa_xscale = 1, msaa_yscale = 1;
207    if (!translate_samples_to_xyscale(nr_samples, &msaa_xscale, &msaa_yscale, NULL)) {
208       /* Number of samples not supported */
209       return NULL;
210    }
211 
212    /* Determine needed padding (alignment of height/width) */
213    unsigned paddingX = 0, paddingY = 0;
214    unsigned halign = TEXTURE_HALIGN_FOUR;
215    if (!util_format_is_compressed(templat->format)) {
216       /* If we have the TEXTURE_HALIGN feature, we can always align to the
217        * resolve engine's width.  If not, we must not align resources used
218        * only for textures. If this GPU uses the BLT engine, never do RS align.
219        */
220       bool rs_align = screen->specs.use_blt ? false : (
221                          VIV_FEATURE(screen, chipMinorFeatures1, TEXTURE_HALIGN) ||
222                          !etna_resource_sampler_only(templat));
223       etna_layout_multiple(layout, screen->specs.pixel_pipes, rs_align, &paddingX,
224                            &paddingY, &halign);
225       assert(paddingX && paddingY);
226    } else {
227       /* Compressed textures are padded to their block size, but we don't have
228        * to do anything special for that. */
229       paddingX = 1;
230       paddingY = 1;
231    }
232 
233    if (!screen->specs.use_blt && templat->target != PIPE_BUFFER)
234       etna_adjust_rs_align(screen->specs.pixel_pipes, NULL, &paddingY);
235 
236    if (templat->bind & PIPE_BIND_SCANOUT) {
237       struct pipe_resource scanout_templat = *templat;
238       struct renderonly_scanout *scanout;
239       struct winsys_handle handle;
240 
241       /* pad scanout buffer size to be compatible with the RS */
242       if (!screen->specs.use_blt && modifier == DRM_FORMAT_MOD_LINEAR)
243          etna_adjust_rs_align(screen->specs.pixel_pipes, &paddingX, &paddingY);
244 
245       scanout_templat.width0 = align(scanout_templat.width0, paddingX);
246       scanout_templat.height0 = align(scanout_templat.height0, paddingY);
247 
248       scanout = renderonly_scanout_for_resource(&scanout_templat,
249                                                 screen->ro, &handle);
250       if (!scanout)
251          return NULL;
252 
253       assert(handle.type == DRM_API_HANDLE_TYPE_FD);
254       handle.modifier = modifier;
255       rsc = etna_resource(pscreen->resource_from_handle(pscreen, templat,
256                                                         &handle,
257                                                         PIPE_HANDLE_USAGE_WRITE));
258       close(handle.handle);
259       if (!rsc)
260          return NULL;
261 
262       rsc->scanout = scanout;
263 
264       return &rsc->base;
265    }
266 
267    rsc = CALLOC_STRUCT(etna_resource);
268    if (!rsc)
269       return NULL;
270 
271    rsc->base = *templat;
272    rsc->base.screen = pscreen;
273    rsc->base.nr_samples = nr_samples;
274    rsc->layout = layout;
275    rsc->halign = halign;
276 
277    pipe_reference_init(&rsc->base.reference, 1);
278    list_inithead(&rsc->list);
279 
280    size = setup_miptree(rsc, paddingX, paddingY, msaa_xscale, msaa_yscale);
281 
282    uint32_t flags = DRM_ETNA_GEM_CACHE_WC;
283    if (templat->bind & PIPE_BIND_VERTEX_BUFFER)
284       flags |= DRM_ETNA_GEM_FORCE_MMU;
285    struct etna_bo *bo = etna_bo_new(screen->dev, size, flags);
286    if (unlikely(bo == NULL)) {
287       BUG("Problem allocating video memory for resource");
288       goto free_rsc;
289    }
290 
291    rsc->bo = bo;
292    rsc->ts_bo = 0; /* TS is only created when first bound to surface */
293 
294    if (DBG_ENABLED(ETNA_DBG_ZERO)) {
295       void *map = etna_bo_map(bo);
296       memset(map, 0, size);
297    }
298 
299    return &rsc->base;
300 
301 free_rsc:
302    FREE(rsc);
303    return NULL;
304 }
305 
306 static struct pipe_resource *
etna_resource_create(struct pipe_screen * pscreen,const struct pipe_resource * templat)307 etna_resource_create(struct pipe_screen *pscreen,
308                      const struct pipe_resource *templat)
309 {
310    struct etna_screen *screen = etna_screen(pscreen);
311 
312    /* Figure out what tiling to use -- for now, assume that texture cannot be linear.
313     * there is a capability LINEAR_TEXTURE_SUPPORT (supported on gc880 and
314     * gc2000 at least), but not sure how it works.
315     * Buffers always have LINEAR layout.
316     */
317    unsigned layout = ETNA_LAYOUT_LINEAR;
318    if (etna_resource_sampler_only(templat)) {
319       /* The buffer is only used for texturing, so create something
320        * directly compatible with the sampler.  Such a buffer can
321        * never be rendered to. */
322       layout = ETNA_LAYOUT_TILED;
323 
324       if (util_format_is_compressed(templat->format))
325          layout = ETNA_LAYOUT_LINEAR;
326    } else if (templat->target != PIPE_BUFFER) {
327       bool want_multitiled = false;
328       bool want_supertiled = screen->specs.can_supertile;
329 
330       /* When this GPU supports single-buffer rendering, don't ever enable
331        * multi-tiling. This replicates the blob behavior on GC3000.
332        */
333       if (!screen->specs.single_buffer)
334          want_multitiled = screen->specs.pixel_pipes > 1;
335 
336       /* Keep single byte blocksized resources as tiled, since we
337        * are unable to use the RS blit to de-tile them. However,
338        * if they're used as a render target or depth/stencil, they
339        * must be multi-tiled for GPUs with multiple pixel pipes.
340        * Ignore depth/stencil here, but it is an error for a render
341        * target.
342        */
343       if (util_format_get_blocksize(templat->format) == 1 &&
344           !(templat->bind & PIPE_BIND_DEPTH_STENCIL)) {
345          assert(!(templat->bind & PIPE_BIND_RENDER_TARGET && want_multitiled));
346          want_multitiled = want_supertiled = false;
347       }
348 
349       layout = ETNA_LAYOUT_BIT_TILE;
350       if (want_multitiled)
351          layout |= ETNA_LAYOUT_BIT_MULTI;
352       if (want_supertiled)
353          layout |= ETNA_LAYOUT_BIT_SUPER;
354    }
355 
356    if (templat->target == PIPE_TEXTURE_3D)
357       layout = ETNA_LAYOUT_LINEAR;
358 
359    /* modifier is only used for scanout surfaces, so safe to use LINEAR here */
360    return etna_resource_alloc(pscreen, layout, DRM_FORMAT_MOD_LINEAR, templat);
361 }
362 
363 enum modifier_priority {
364    MODIFIER_PRIORITY_INVALID = 0,
365    MODIFIER_PRIORITY_LINEAR,
366    MODIFIER_PRIORITY_SPLIT_TILED,
367    MODIFIER_PRIORITY_SPLIT_SUPER_TILED,
368    MODIFIER_PRIORITY_TILED,
369    MODIFIER_PRIORITY_SUPER_TILED,
370 };
371 
372 const uint64_t priority_to_modifier[] = {
373    [MODIFIER_PRIORITY_INVALID] = DRM_FORMAT_MOD_INVALID,
374    [MODIFIER_PRIORITY_LINEAR] = DRM_FORMAT_MOD_LINEAR,
375    [MODIFIER_PRIORITY_SPLIT_TILED] = DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED,
376    [MODIFIER_PRIORITY_SPLIT_SUPER_TILED] = DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED,
377    [MODIFIER_PRIORITY_TILED] = DRM_FORMAT_MOD_VIVANTE_TILED,
378    [MODIFIER_PRIORITY_SUPER_TILED] = DRM_FORMAT_MOD_VIVANTE_SUPER_TILED,
379 };
380 
381 static uint64_t
select_best_modifier(const struct etna_screen * screen,const uint64_t * modifiers,const unsigned count)382 select_best_modifier(const struct etna_screen * screen,
383                      const uint64_t *modifiers, const unsigned count)
384 {
385    enum modifier_priority prio = MODIFIER_PRIORITY_INVALID;
386 
387    for (int i = 0; i < count; i++) {
388       switch (modifiers[i]) {
389       case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
390          if ((screen->specs.pixel_pipes > 1 && !screen->specs.single_buffer) ||
391              !screen->specs.can_supertile)
392             break;
393          prio = MAX2(prio, MODIFIER_PRIORITY_SUPER_TILED);
394          break;
395       case DRM_FORMAT_MOD_VIVANTE_TILED:
396          if (screen->specs.pixel_pipes > 1 && !screen->specs.single_buffer)
397             break;
398          prio = MAX2(prio, MODIFIER_PRIORITY_TILED);
399          break;
400       case DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED:
401          if ((screen->specs.pixel_pipes < 2) || !screen->specs.can_supertile)
402             break;
403          prio = MAX2(prio, MODIFIER_PRIORITY_SPLIT_SUPER_TILED);
404          break;
405       case DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED:
406          if (screen->specs.pixel_pipes < 2)
407             break;
408          prio = MAX2(prio, MODIFIER_PRIORITY_SPLIT_TILED);
409          break;
410       case DRM_FORMAT_MOD_LINEAR:
411          prio = MAX2(prio, MODIFIER_PRIORITY_LINEAR);
412          break;
413       case DRM_FORMAT_MOD_INVALID:
414       default:
415          break;
416       }
417    }
418 
419    return priority_to_modifier[prio];
420 }
421 
422 static struct pipe_resource *
etna_resource_create_modifiers(struct pipe_screen * pscreen,const struct pipe_resource * templat,const uint64_t * modifiers,int count)423 etna_resource_create_modifiers(struct pipe_screen *pscreen,
424                                const struct pipe_resource *templat,
425                                const uint64_t *modifiers, int count)
426 {
427    struct etna_screen *screen = etna_screen(pscreen);
428    struct pipe_resource tmpl = *templat;
429    uint64_t modifier = select_best_modifier(screen, modifiers, count);
430 
431    if (modifier == DRM_FORMAT_MOD_INVALID)
432       return NULL;
433 
434    /*
435     * We currently assume that all buffers allocated through this interface
436     * should be scanout enabled.
437     */
438    tmpl.bind |= PIPE_BIND_SCANOUT;
439 
440    return etna_resource_alloc(pscreen, modifier_to_layout(modifier),
441                               modifier, &tmpl);
442 }
443 
444 static void
etna_resource_changed(struct pipe_screen * pscreen,struct pipe_resource * prsc)445 etna_resource_changed(struct pipe_screen *pscreen, struct pipe_resource *prsc)
446 {
447    struct etna_resource *res = etna_resource(prsc);
448 
449    if (res->external)
450       etna_resource(res->external)->seqno++;
451    else
452       res->seqno++;
453 }
454 
455 static void
etna_resource_destroy(struct pipe_screen * pscreen,struct pipe_resource * prsc)456 etna_resource_destroy(struct pipe_screen *pscreen, struct pipe_resource *prsc)
457 {
458    struct etna_resource *rsc = etna_resource(prsc);
459 
460    if (rsc->bo)
461       etna_bo_del(rsc->bo);
462 
463    if (rsc->ts_bo)
464       etna_bo_del(rsc->ts_bo);
465 
466    if (rsc->scanout)
467       renderonly_scanout_destroy(rsc->scanout, etna_screen(pscreen)->ro);
468 
469    list_delinit(&rsc->list);
470 
471    pipe_resource_reference(&rsc->texture, NULL);
472    pipe_resource_reference(&rsc->external, NULL);
473 
474    FREE(rsc);
475 }
476 
477 static struct pipe_resource *
etna_resource_from_handle(struct pipe_screen * pscreen,const struct pipe_resource * tmpl,struct winsys_handle * handle,unsigned usage)478 etna_resource_from_handle(struct pipe_screen *pscreen,
479                           const struct pipe_resource *tmpl,
480                           struct winsys_handle *handle, unsigned usage)
481 {
482    struct etna_screen *screen = etna_screen(pscreen);
483    struct etna_resource *rsc;
484    struct etna_resource_level *level;
485    struct pipe_resource *prsc;
486    struct pipe_resource *ptiled = NULL;
487 
488    DBG("target=%d, format=%s, %ux%ux%u, array_size=%u, last_level=%u, "
489        "nr_samples=%u, usage=%u, bind=%x, flags=%x",
490        tmpl->target, util_format_name(tmpl->format), tmpl->width0,
491        tmpl->height0, tmpl->depth0, tmpl->array_size, tmpl->last_level,
492        tmpl->nr_samples, tmpl->usage, tmpl->bind, tmpl->flags);
493 
494    rsc = CALLOC_STRUCT(etna_resource);
495    if (!rsc)
496       return NULL;
497 
498    level = &rsc->levels[0];
499    prsc = &rsc->base;
500 
501    *prsc = *tmpl;
502 
503    pipe_reference_init(&prsc->reference, 1);
504    list_inithead(&rsc->list);
505    prsc->screen = pscreen;
506 
507    rsc->bo = etna_screen_bo_from_handle(pscreen, handle, &level->stride);
508    if (!rsc->bo)
509       goto fail;
510 
511    rsc->seqno = 1;
512    rsc->layout = modifier_to_layout(handle->modifier);
513    rsc->halign = TEXTURE_HALIGN_FOUR;
514 
515 
516    level->width = tmpl->width0;
517    level->height = tmpl->height0;
518 
519    /* Determine padding of the imported resource. */
520    unsigned paddingX = 0, paddingY = 0;
521    etna_layout_multiple(rsc->layout, screen->specs.pixel_pipes,
522                         VIV_FEATURE(screen, chipMinorFeatures1, TEXTURE_HALIGN),
523                         &paddingX, &paddingY, &rsc->halign);
524 
525    if (!screen->specs.use_blt)
526       etna_adjust_rs_align(screen->specs.pixel_pipes, NULL, &paddingY);
527    level->padded_width = align(level->width, paddingX);
528    level->padded_height = align(level->height, paddingY);
529 
530    level->layer_stride = level->stride * util_format_get_nblocksy(prsc->format,
531                                                                   level->padded_height);
532    level->size = level->layer_stride;
533 
534    /* The DDX must give us a BO which conforms to our padding size.
535     * The stride of the BO must be greater or equal to our padded
536     * stride. The size of the BO must accomodate the padded height. */
537    if (level->stride < util_format_get_stride(tmpl->format, level->padded_width)) {
538       BUG("BO stride is too small for RS engine width padding");
539       goto fail;
540    }
541    if (etna_bo_size(rsc->bo) < level->stride * level->padded_height) {
542       BUG("BO size is too small for RS engine height padding");
543       goto fail;
544    }
545 
546    if (rsc->layout == ETNA_LAYOUT_LINEAR) {
547       /*
548        * Both sampler and pixel pipes can't handle linear, create a compatible
549        * base resource, where we can attach the imported buffer as an external
550        * resource.
551        */
552       struct pipe_resource tiled_templat = *tmpl;
553 
554       /*
555        * Remove BIND_SCANOUT to avoid recursion, as etna_resource_create uses
556        * this function to import the scanout buffer and get a tiled resource.
557        */
558       tiled_templat.bind &= ~PIPE_BIND_SCANOUT;
559 
560       ptiled = etna_resource_create(pscreen, &tiled_templat);
561       if (!ptiled)
562          goto fail;
563 
564       etna_resource(ptiled)->external = prsc;
565 
566       return ptiled;
567    }
568 
569    return prsc;
570 
571 fail:
572    etna_resource_destroy(pscreen, prsc);
573    if (ptiled)
574       etna_resource_destroy(pscreen, ptiled);
575 
576    return NULL;
577 }
578 
579 static boolean
etna_resource_get_handle(struct pipe_screen * pscreen,struct pipe_context * pctx,struct pipe_resource * prsc,struct winsys_handle * handle,unsigned usage)580 etna_resource_get_handle(struct pipe_screen *pscreen,
581                          struct pipe_context *pctx,
582                          struct pipe_resource *prsc,
583                          struct winsys_handle *handle, unsigned usage)
584 {
585    struct etna_resource *rsc = etna_resource(prsc);
586    /* Scanout is always attached to the base resource */
587    struct renderonly_scanout *scanout = rsc->scanout;
588 
589    /*
590     * External resources are preferred, so a import->export chain of
591     * render/sampler incompatible buffers yield the same handle.
592     */
593    if (rsc->external)
594       rsc = etna_resource(rsc->external);
595 
596    handle->stride = rsc->levels[0].stride;
597    handle->modifier = layout_to_modifier(rsc->layout);
598 
599    if (handle->type == DRM_API_HANDLE_TYPE_SHARED) {
600       return etna_bo_get_name(rsc->bo, &handle->handle) == 0;
601    } else if (handle->type == DRM_API_HANDLE_TYPE_KMS) {
602       if (renderonly_get_handle(scanout, handle)) {
603          return TRUE;
604       } else {
605          handle->handle = etna_bo_handle(rsc->bo);
606          return TRUE;
607       }
608    } else if (handle->type == DRM_API_HANDLE_TYPE_FD) {
609       handle->handle = etna_bo_dmabuf(rsc->bo);
610       return TRUE;
611    } else {
612       return FALSE;
613    }
614 }
615 
616 void
etna_resource_used(struct etna_context * ctx,struct pipe_resource * prsc,enum etna_resource_status status)617 etna_resource_used(struct etna_context *ctx, struct pipe_resource *prsc,
618                    enum etna_resource_status status)
619 {
620    struct etna_resource *rsc;
621 
622    if (!prsc)
623       return;
624 
625    rsc = etna_resource(prsc);
626    rsc->status |= status;
627 
628    /* TODO resources can actually be shared across contexts,
629     * so I'm not sure a single list-head will do the trick? */
630    debug_assert((rsc->pending_ctx == ctx) || !rsc->pending_ctx);
631    list_delinit(&rsc->list);
632    list_addtail(&rsc->list, &ctx->used_resources);
633    rsc->pending_ctx = ctx;
634 }
635 
636 bool
etna_resource_has_valid_ts(struct etna_resource * rsc)637 etna_resource_has_valid_ts(struct etna_resource *rsc)
638 {
639    if (!rsc->ts_bo)
640       return false;
641 
642    for (int level = 0; level <= rsc->base.last_level; level++)
643       if (rsc->levels[level].ts_valid)
644          return true;
645 
646    return false;
647 }
648 
649 void
etna_resource_screen_init(struct pipe_screen * pscreen)650 etna_resource_screen_init(struct pipe_screen *pscreen)
651 {
652    pscreen->can_create_resource = etna_screen_can_create_resource;
653    pscreen->resource_create = etna_resource_create;
654    pscreen->resource_create_with_modifiers = etna_resource_create_modifiers;
655    pscreen->resource_from_handle = etna_resource_from_handle;
656    pscreen->resource_get_handle = etna_resource_get_handle;
657    pscreen->resource_changed = etna_resource_changed;
658    pscreen->resource_destroy = etna_resource_destroy;
659 }
660