xref: /aosp_15_r20/external/mesa3d/src/gallium/drivers/crocus/crocus_resource.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2017 Intel Corporation
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, sublicense,
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 shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 /**
24  * @file crocus_resource.c
25  *
26  * Resources are images, buffers, and other objects used by the GPU.
27  *
28  * XXX: explain resources
29  */
30 
31 #include <stdio.h>
32 #include <errno.h>
33 #include "pipe/p_defines.h"
34 #include "pipe/p_state.h"
35 #include "pipe/p_context.h"
36 #include "pipe/p_screen.h"
37 #include "util/os_memory.h"
38 #include "util/u_cpu_detect.h"
39 #include "util/u_inlines.h"
40 #include "util/format/u_format.h"
41 #include "util/u_resource.h"
42 #include "util/u_threaded_context.h"
43 #include "util/u_transfer.h"
44 #include "util/u_transfer_helper.h"
45 #include "util/u_upload_mgr.h"
46 #include "util/ralloc.h"
47 #include "util/u_memory.h"
48 #include "crocus_batch.h"
49 #include "crocus_context.h"
50 #include "crocus_resource.h"
51 #include "crocus_screen.h"
52 #include "intel/dev/intel_debug.h"
53 #include "isl/isl.h"
54 #include "drm-uapi/drm_fourcc.h"
55 #include "drm-uapi/i915_drm.h"
56 
57 enum modifier_priority {
58    MODIFIER_PRIORITY_INVALID = 0,
59    MODIFIER_PRIORITY_LINEAR,
60    MODIFIER_PRIORITY_X,
61    MODIFIER_PRIORITY_Y,
62    MODIFIER_PRIORITY_Y_CCS,
63 };
64 
65 static const uint64_t priority_to_modifier[] = {
66    [MODIFIER_PRIORITY_INVALID] = DRM_FORMAT_MOD_INVALID,
67    [MODIFIER_PRIORITY_LINEAR] = DRM_FORMAT_MOD_LINEAR,
68    [MODIFIER_PRIORITY_X] = I915_FORMAT_MOD_X_TILED,
69    [MODIFIER_PRIORITY_Y] = I915_FORMAT_MOD_Y_TILED,
70 };
71 
72 static bool
modifier_is_supported(const struct intel_device_info * devinfo,enum pipe_format pfmt,unsigned bind,uint64_t modifier)73 modifier_is_supported(const struct intel_device_info *devinfo,
74                       enum pipe_format pfmt, unsigned bind,
75                       uint64_t modifier)
76 {
77    /* XXX: do something real */
78    switch (modifier) {
79    case I915_FORMAT_MOD_Y_TILED:
80       if (bind & PIPE_BIND_SCANOUT)
81          return false;
82       return devinfo->ver >= 6;
83    case I915_FORMAT_MOD_X_TILED:
84    case DRM_FORMAT_MOD_LINEAR:
85       return true;
86    case DRM_FORMAT_MOD_INVALID:
87    default:
88       return false;
89    }
90 }
91 
92 static uint64_t
select_best_modifier(struct intel_device_info * devinfo,const struct pipe_resource * templ,const uint64_t * modifiers,int count)93 select_best_modifier(struct intel_device_info *devinfo,
94                      const struct pipe_resource *templ,
95                      const uint64_t *modifiers,
96                      int count)
97 {
98    enum modifier_priority prio = MODIFIER_PRIORITY_INVALID;
99 
100    for (int i = 0; i < count; i++) {
101       if (!modifier_is_supported(devinfo, templ->format, templ->bind,
102                                  modifiers[i]))
103          continue;
104 
105       switch (modifiers[i]) {
106       case I915_FORMAT_MOD_Y_TILED:
107          prio = MAX2(prio, MODIFIER_PRIORITY_Y);
108          break;
109       case I915_FORMAT_MOD_X_TILED:
110          prio = MAX2(prio, MODIFIER_PRIORITY_X);
111          break;
112       case DRM_FORMAT_MOD_LINEAR:
113          prio = MAX2(prio, MODIFIER_PRIORITY_LINEAR);
114          break;
115       case DRM_FORMAT_MOD_INVALID:
116       default:
117          break;
118       }
119    }
120 
121    return priority_to_modifier[prio];
122 }
123 
124 static enum isl_surf_dim
crocus_target_to_isl_surf_dim(enum pipe_texture_target target)125 crocus_target_to_isl_surf_dim(enum pipe_texture_target target)
126 {
127    switch (target) {
128    case PIPE_BUFFER:
129    case PIPE_TEXTURE_1D:
130    case PIPE_TEXTURE_1D_ARRAY:
131       return ISL_SURF_DIM_1D;
132    case PIPE_TEXTURE_2D:
133    case PIPE_TEXTURE_CUBE:
134    case PIPE_TEXTURE_RECT:
135    case PIPE_TEXTURE_2D_ARRAY:
136    case PIPE_TEXTURE_CUBE_ARRAY:
137       return ISL_SURF_DIM_2D;
138    case PIPE_TEXTURE_3D:
139       return ISL_SURF_DIM_3D;
140    case PIPE_MAX_TEXTURE_TYPES:
141       break;
142    }
143    unreachable("invalid texture type");
144 }
145 
146 static isl_surf_usage_flags_t
pipe_bind_to_isl_usage(unsigned bindings)147 pipe_bind_to_isl_usage(unsigned bindings)
148 {
149    isl_surf_usage_flags_t usage = 0;
150 
151    if (bindings & PIPE_BIND_RENDER_TARGET)
152       usage |= ISL_SURF_USAGE_RENDER_TARGET_BIT;
153 
154    if (bindings & PIPE_BIND_SAMPLER_VIEW)
155       usage |= ISL_SURF_USAGE_TEXTURE_BIT;
156 
157    if (bindings & (PIPE_BIND_SHADER_IMAGE | PIPE_BIND_SHADER_BUFFER))
158       usage |= ISL_SURF_USAGE_STORAGE_BIT;
159 
160    if (bindings & PIPE_BIND_SCANOUT)
161       usage |= ISL_SURF_USAGE_DISPLAY_BIT;
162    return usage;
163 }
164 
165 static bool
crocus_resource_configure_main(const struct crocus_screen * screen,struct crocus_resource * res,const struct pipe_resource * templ,uint64_t modifier,uint32_t row_pitch_B)166 crocus_resource_configure_main(const struct crocus_screen *screen,
167                                struct crocus_resource *res,
168                                const struct pipe_resource *templ,
169                                uint64_t modifier, uint32_t row_pitch_B)
170 {
171    const struct intel_device_info *devinfo = &screen->devinfo;
172    const struct util_format_description *format_desc =
173       util_format_description(templ->format);
174    const bool has_depth = util_format_has_depth(format_desc);
175    isl_surf_usage_flags_t usage = pipe_bind_to_isl_usage(templ->bind);
176    isl_tiling_flags_t tiling_flags = ISL_TILING_ANY_MASK;
177 
178    /* TODO: This used to be because there wasn't BLORP to handle Y-tiling. */
179    if (devinfo->ver < 6 && !util_format_is_depth_or_stencil(templ->format))
180       tiling_flags &= ~ISL_TILING_Y0_BIT;
181 
182    if (modifier != DRM_FORMAT_MOD_INVALID) {
183       res->mod_info = isl_drm_modifier_get_info(modifier);
184 
185       tiling_flags = 1 << res->mod_info->tiling;
186    } else {
187       if (templ->bind & PIPE_BIND_RENDER_TARGET && devinfo->ver < 6)
188          tiling_flags &= ISL_TILING_LINEAR_BIT | ISL_TILING_X_BIT;
189       /* Use linear for staging buffers */
190       if (templ->usage == PIPE_USAGE_STAGING ||
191           templ->bind & (PIPE_BIND_LINEAR | PIPE_BIND_CURSOR) )
192          tiling_flags = ISL_TILING_LINEAR_BIT;
193       else if (templ->bind & PIPE_BIND_SCANOUT)
194          tiling_flags = screen->devinfo.has_tiling_uapi ?
195             ISL_TILING_X_BIT : ISL_TILING_LINEAR_BIT;
196    }
197 
198    if (templ->target == PIPE_TEXTURE_CUBE ||
199        templ->target == PIPE_TEXTURE_CUBE_ARRAY)
200       usage |= ISL_SURF_USAGE_CUBE_BIT;
201 
202    if (templ->usage != PIPE_USAGE_STAGING) {
203       if (templ->format == PIPE_FORMAT_S8_UINT)
204          usage |= ISL_SURF_USAGE_STENCIL_BIT;
205       else if (has_depth) {
206          /* combined DS only on gen4/5 */
207          if (devinfo->ver < 6) {
208             if (templ->format == PIPE_FORMAT_Z24X8_UNORM ||
209                 templ->format == PIPE_FORMAT_Z24_UNORM_S8_UINT ||
210                 templ->format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT)
211                usage |= ISL_SURF_USAGE_STENCIL_BIT;
212          }
213          usage |= ISL_SURF_USAGE_DEPTH_BIT;
214       }
215 
216       if (templ->format == PIPE_FORMAT_S8_UINT)
217          tiling_flags = ISL_TILING_W_BIT;
218    }
219 
220    /* Disable aux for external memory objects. */
221    if (!res->mod_info && res->external_format != PIPE_FORMAT_NONE)
222       usage |= ISL_SURF_USAGE_DISABLE_AUX_BIT;
223 
224    const enum isl_format format =
225       crocus_format_for_usage(&screen->devinfo, templ->format, usage).fmt;
226 
227    if (row_pitch_B == 0 && templ->usage == PIPE_USAGE_STAGING &&
228        templ->target == PIPE_TEXTURE_2D &&
229        devinfo->ver < 6) {
230       /* align row pitch to 4 so we can keep using BLT engine */
231       row_pitch_B = util_format_get_stride(templ->format, templ->width0);
232       row_pitch_B = ALIGN(row_pitch_B, 4);
233    }
234 
235    const struct isl_surf_init_info init_info = {
236       .dim = crocus_target_to_isl_surf_dim(templ->target),
237       .format = format,
238       .width = templ->width0,
239       .height = templ->height0,
240       .depth = templ->depth0,
241       .levels = templ->last_level + 1,
242       .array_len = templ->array_size,
243       .samples = MAX2(templ->nr_samples, 1),
244       .min_alignment_B = 0,
245       .row_pitch_B = row_pitch_B,
246       .usage = usage,
247       .tiling_flags = tiling_flags
248    };
249 
250    if (!isl_surf_init_s(&screen->isl_dev, &res->surf, &init_info))
251       return false;
252 
253    /*
254     * Don't create staging surfaces that will use > half the aperture
255     * since staging implies you are sending to another resource,
256     * which there is no way to fit both into aperture.
257     */
258    if (templ->usage == PIPE_USAGE_STAGING)
259       if (res->surf.size_B > screen->aperture_threshold / 2)
260          return false;
261 
262    res->internal_format = templ->format;
263 
264    return true;
265 }
266 
267 static void
crocus_query_dmabuf_modifiers(struct pipe_screen * pscreen,enum pipe_format pfmt,int max,uint64_t * modifiers,unsigned int * external_only,int * count)268 crocus_query_dmabuf_modifiers(struct pipe_screen *pscreen,
269                               enum pipe_format pfmt,
270                               int max,
271                               uint64_t *modifiers,
272                               unsigned int *external_only,
273                               int *count)
274 {
275    struct crocus_screen *screen = (void *) pscreen;
276    const struct intel_device_info *devinfo = &screen->devinfo;
277 
278    uint64_t all_modifiers[] = {
279       DRM_FORMAT_MOD_LINEAR,
280       I915_FORMAT_MOD_X_TILED,
281       I915_FORMAT_MOD_Y_TILED,
282    };
283 
284    int supported_mods = 0;
285 
286    for (int i = 0; i < ARRAY_SIZE(all_modifiers); i++) {
287       if (!modifier_is_supported(devinfo, pfmt, 0, all_modifiers[i]))
288          continue;
289 
290       if (supported_mods < max) {
291          if (modifiers)
292             modifiers[supported_mods] = all_modifiers[i];
293 
294          if (external_only)
295             external_only[supported_mods] = util_format_is_yuv(pfmt);
296       }
297 
298       supported_mods++;
299    }
300 
301    *count = supported_mods;
302 }
303 
304 static struct pipe_resource *
crocus_resource_get_separate_stencil(struct pipe_resource * p_res)305 crocus_resource_get_separate_stencil(struct pipe_resource *p_res)
306 {
307    return _crocus_resource_get_separate_stencil(p_res);
308 }
309 
310 static void
crocus_resource_set_separate_stencil(struct pipe_resource * p_res,struct pipe_resource * stencil)311 crocus_resource_set_separate_stencil(struct pipe_resource *p_res,
312                                      struct pipe_resource *stencil)
313 {
314    assert(util_format_has_depth(util_format_description(p_res->format)));
315    pipe_resource_reference(&p_res->next, stencil);
316 }
317 
318 void
crocus_resource_disable_aux(struct crocus_resource * res)319 crocus_resource_disable_aux(struct crocus_resource *res)
320 {
321    crocus_bo_unreference(res->aux.bo);
322    free(res->aux.state);
323 
324    res->aux.usage = ISL_AUX_USAGE_NONE;
325    res->aux.has_hiz = 0;
326    res->aux.surf.size_B = 0;
327    res->aux.surf.levels = 0;
328    res->aux.bo = NULL;
329    res->aux.state = NULL;
330 }
331 
332 static void
crocus_resource_destroy(struct pipe_screen * screen,struct pipe_resource * resource)333 crocus_resource_destroy(struct pipe_screen *screen,
334                         struct pipe_resource *resource)
335 {
336    struct crocus_resource *res = (struct crocus_resource *)resource;
337 
338    if (resource->target == PIPE_BUFFER)
339       util_range_destroy(&res->valid_buffer_range);
340 
341    if (res->shadow)
342       pipe_resource_reference((struct pipe_resource **)&res->shadow, NULL);
343    crocus_resource_disable_aux(res);
344 
345    threaded_resource_deinit(resource);
346    crocus_bo_unreference(res->bo);
347    crocus_pscreen_unref(res->orig_screen);
348    free(res);
349 }
350 
351 static struct crocus_resource *
crocus_alloc_resource(struct pipe_screen * pscreen,const struct pipe_resource * templ)352 crocus_alloc_resource(struct pipe_screen *pscreen,
353                       const struct pipe_resource *templ)
354 {
355    struct crocus_resource *res = calloc(1, sizeof(struct crocus_resource));
356    if (!res)
357       return NULL;
358 
359    res->base.b = *templ;
360    res->base.b.screen = pscreen;
361    res->orig_screen = crocus_pscreen_ref(pscreen);
362    pipe_reference_init(&res->base.b.reference, 1);
363    threaded_resource_init(&res->base.b, false);
364 
365    if (templ->target == PIPE_BUFFER)
366       util_range_init(&res->valid_buffer_range);
367 
368    return res;
369 }
370 
371 unsigned
crocus_get_num_logical_layers(const struct crocus_resource * res,unsigned level)372 crocus_get_num_logical_layers(const struct crocus_resource *res, unsigned level)
373 {
374    if (res->surf.dim == ISL_SURF_DIM_3D)
375       return u_minify(res->surf.logical_level0_px.depth, level);
376    else
377       return res->surf.logical_level0_px.array_len;
378 }
379 
380 static enum isl_aux_state **
create_aux_state_map(struct crocus_resource * res,enum isl_aux_state initial)381 create_aux_state_map(struct crocus_resource *res, enum isl_aux_state initial)
382 {
383    assert(res->aux.state == NULL);
384 
385    uint32_t total_slices = 0;
386    for (uint32_t level = 0; level < res->surf.levels; level++)
387       total_slices += crocus_get_num_logical_layers(res, level);
388 
389    const size_t per_level_array_size =
390       res->surf.levels * sizeof(enum isl_aux_state *);
391 
392    /* We're going to allocate a single chunk of data for both the per-level
393     * reference array and the arrays of aux_state.  This makes cleanup
394     * significantly easier.
395     */
396    const size_t total_size =
397       per_level_array_size + total_slices * sizeof(enum isl_aux_state);
398 
399    void *data = malloc(total_size);
400    if (!data)
401       return NULL;
402 
403    enum isl_aux_state **per_level_arr = data;
404    enum isl_aux_state *s = data + per_level_array_size;
405    for (uint32_t level = 0; level < res->surf.levels; level++) {
406       per_level_arr[level] = s;
407       const unsigned level_layers = crocus_get_num_logical_layers(res, level);
408       for (uint32_t a = 0; a < level_layers; a++)
409          *(s++) = initial;
410    }
411    assert((void *)s == data + total_size);
412 
413    return per_level_arr;
414 }
415 
416 /**
417  * Configure aux for the resource, but don't allocate it. For images which
418  * might be shared with modifiers, we must allocate the image and aux data in
419  * a single bo.
420  *
421  * Returns false on unexpected error (e.g. allocation failed, or invalid
422  * configuration result).
423  */
424 static bool
crocus_resource_configure_aux(struct crocus_screen * screen,struct crocus_resource * res,uint64_t * aux_size_B,uint32_t * alloc_flags)425 crocus_resource_configure_aux(struct crocus_screen *screen,
426                               struct crocus_resource *res,
427                               uint64_t *aux_size_B,
428                               uint32_t *alloc_flags)
429 {
430    const struct intel_device_info *devinfo = &screen->devinfo;
431 
432    /* Modifiers with compression are not supported. */
433    assert(!res->mod_info ||
434           !isl_drm_modifier_has_aux(res->mod_info->modifier));
435 
436    const bool has_mcs = devinfo->ver >= 7 && !res->mod_info &&
437       isl_surf_get_mcs_surf(&screen->isl_dev, &res->surf, &res->aux.surf);
438 
439    const bool has_hiz = devinfo->ver >= 6 && !res->mod_info &&
440       isl_surf_get_hiz_surf(&screen->isl_dev, &res->surf, &res->aux.surf);
441 
442    const bool has_ccs =
443       devinfo->ver >= 7 && !res->mod_info &&
444       isl_surf_get_ccs_surf(&screen->isl_dev, &res->surf, &res->aux.surf, 0);
445 
446    /* Having more than one type of compression is impossible */
447    assert(has_ccs + has_mcs + has_hiz <= 1);
448 
449    if (has_mcs) {
450       res->aux.usage = ISL_AUX_USAGE_MCS;
451    } else if (has_hiz) {
452       res->aux.usage = ISL_AUX_USAGE_HIZ;
453    } else if (has_ccs) {
454       if (isl_format_supports_ccs_d(devinfo, res->surf.format))
455          res->aux.usage = ISL_AUX_USAGE_CCS_D;
456    }
457 
458    enum isl_aux_state initial_state = ISL_AUX_STATE_AUX_INVALID;
459    *aux_size_B = 0;
460    *alloc_flags = 0;
461    assert(!res->aux.bo);
462 
463    switch (res->aux.usage) {
464    case ISL_AUX_USAGE_NONE:
465       res->aux.surf.levels = 0;
466       return true;
467    case ISL_AUX_USAGE_HIZ:
468       initial_state = ISL_AUX_STATE_AUX_INVALID;
469       break;
470    case ISL_AUX_USAGE_MCS:
471       /* The Ivybridge PRM, Vol 2 Part 1 p326 says:
472        *
473        *    "When MCS buffer is enabled and bound to MSRT, it is required
474        *     that it is cleared prior to any rendering."
475        *
476        * Since we only use the MCS buffer for rendering, we just clear it
477        * immediately on allocation.  The clear value for MCS buffers is all
478        * 1's, so we simply memset it to 0xff.
479        */
480       initial_state = ISL_AUX_STATE_CLEAR;
481       break;
482    case ISL_AUX_USAGE_CCS_D:
483       /* When CCS_E is used, we need to ensure that the CCS starts off in
484        * a valid state.  From the Sky Lake PRM, "MCS Buffer for Render
485        * Target(s)":
486        *
487        *    "If Software wants to enable Color Compression without Fast
488        *     clear, Software needs to initialize MCS with zeros."
489        *
490        * A CCS value of 0 indicates that the corresponding block is in the
491        * pass-through state which is what we want.
492        *
493        * For CCS_D, do the same thing.  On Gen9+, this avoids having any
494        * undefined bits in the aux buffer.
495        */
496       initial_state = ISL_AUX_STATE_PASS_THROUGH;
497       *alloc_flags |= BO_ALLOC_ZEROED;
498       break;
499    default:
500       unreachable("non-crocus aux");
501    }
502 
503    /* Create the aux_state for the auxiliary buffer. */
504    res->aux.state = create_aux_state_map(res, initial_state);
505    if (!res->aux.state)
506       return false;
507 
508    /* Increase the aux offset if the main and aux surfaces will share a BO. */
509    res->aux.offset = (uint32_t)align64(res->surf.size_B, res->aux.surf.alignment_B);
510    uint64_t size = res->aux.surf.size_B;
511 
512    /* Allocate space in the buffer for storing the clear color. On modern
513     * platforms (gen > 9), we can read it directly from such buffer.
514     *
515     * On gen <= 9, we are going to store the clear color on the buffer
516     * anyways, and copy it back to the surface state during state emission.
517     *
518     * Also add some padding to make sure the fast clear color state buffer
519     * starts at a 4K alignment. We believe that 256B might be enough, but due
520     * to lack of testing we will leave this as 4K for now.
521     */
522    size = align64(size, 4096);
523    *aux_size_B = size;
524 
525    if (isl_aux_usage_has_hiz(res->aux.usage)) {
526       for (unsigned level = 0; level < res->surf.levels; ++level) {
527          uint32_t width = u_minify(res->surf.phys_level0_sa.width, level);
528          uint32_t height = u_minify(res->surf.phys_level0_sa.height, level);
529 
530          /* Disable HiZ for LOD > 0 unless the width/height are 8x4 aligned.
531           * For LOD == 0, we can grow the dimensions to make it work.
532           */
533          if (devinfo->verx10 < 75 ||
534              (level == 0 || ((width & 7) == 0 && (height & 3) == 0)))
535             res->aux.has_hiz |= 1 << level;
536       }
537    }
538 
539    return true;
540 }
541 
542 /**
543  * Initialize the aux buffer contents.
544  *
545  * Returns false on unexpected error (e.g. mapping a BO failed).
546  */
547 static bool
crocus_resource_init_aux_buf(struct crocus_resource * res,uint32_t alloc_flags)548 crocus_resource_init_aux_buf(struct crocus_resource *res, uint32_t alloc_flags)
549 {
550    if (!(alloc_flags & BO_ALLOC_ZEROED)) {
551       void *map = crocus_bo_map(NULL, res->aux.bo, MAP_WRITE | MAP_RAW);
552 
553       if (!map)
554          return false;
555 
556       if (crocus_resource_get_aux_state(res, 0, 0) != ISL_AUX_STATE_AUX_INVALID) {
557          uint8_t memset_value = isl_aux_usage_has_mcs(res->aux.usage) ? 0xFF : 0;
558          memset((char*)map + res->aux.offset, memset_value,
559                 res->aux.surf.size_B);
560       }
561 
562       crocus_bo_unmap(res->aux.bo);
563    }
564 
565    return true;
566 }
567 
568 /**
569  * Allocate the initial aux surface for a resource based on aux.usage
570  *
571  * Returns false on unexpected error (e.g. allocation failed, or invalid
572  * configuration result).
573  */
574 static bool
crocus_resource_alloc_separate_aux(struct crocus_screen * screen,struct crocus_resource * res)575 crocus_resource_alloc_separate_aux(struct crocus_screen *screen,
576                                    struct crocus_resource *res)
577 {
578    uint32_t alloc_flags;
579    uint64_t size;
580    if (!crocus_resource_configure_aux(screen, res, &size, &alloc_flags))
581       return false;
582 
583    if (size == 0)
584       return true;
585 
586    /* Allocate the auxiliary buffer.  ISL has stricter set of alignment rules
587     * the drm allocator.  Therefore, one can pass the ISL dimensions in terms
588     * of bytes instead of trying to recalculate based on different format
589     * block sizes.
590     */
591    res->aux.bo = crocus_bo_alloc_tiled(screen->bufmgr, "aux buffer", size, 4096,
592                                        isl_tiling_to_i915_tiling(res->aux.surf.tiling),
593                                        res->aux.surf.row_pitch_B, alloc_flags);
594    if (!res->aux.bo) {
595       return false;
596    }
597 
598    if (!crocus_resource_init_aux_buf(res, alloc_flags))
599       return false;
600 
601    return true;
602 }
603 
604 static struct pipe_resource *
crocus_resource_create_for_buffer(struct pipe_screen * pscreen,const struct pipe_resource * templ)605 crocus_resource_create_for_buffer(struct pipe_screen *pscreen,
606                                   const struct pipe_resource *templ)
607 {
608    struct crocus_screen *screen = (struct crocus_screen *)pscreen;
609    struct crocus_resource *res = crocus_alloc_resource(pscreen, templ);
610 
611    assert(templ->target == PIPE_BUFFER);
612    assert(templ->height0 <= 1);
613    assert(templ->depth0 <= 1);
614    assert(templ->format == PIPE_FORMAT_NONE ||
615           util_format_get_blocksize(templ->format) == 1);
616 
617    res->internal_format = templ->format;
618    res->surf.tiling = ISL_TILING_LINEAR;
619 
620    const char *name = templ->target == PIPE_BUFFER ? "buffer" : "miptree";
621 
622    res->bo = crocus_bo_alloc(screen->bufmgr, name, templ->width0);
623    if (!res->bo) {
624       crocus_resource_destroy(pscreen, &res->base.b);
625       return NULL;
626    }
627 
628    return &res->base.b;
629 }
630 
631 static struct pipe_resource *
crocus_resource_create_with_modifiers(struct pipe_screen * pscreen,const struct pipe_resource * templ,const uint64_t * modifiers,int modifiers_count)632 crocus_resource_create_with_modifiers(struct pipe_screen *pscreen,
633                                       const struct pipe_resource *templ,
634                                       const uint64_t *modifiers,
635                                       int modifiers_count)
636 {
637    struct crocus_screen *screen = (struct crocus_screen *)pscreen;
638    struct intel_device_info *devinfo = &screen->devinfo;
639    struct crocus_resource *res = crocus_alloc_resource(pscreen, templ);
640 
641    if (!res)
642       return NULL;
643 
644    uint64_t modifier =
645       select_best_modifier(devinfo, templ, modifiers, modifiers_count);
646 
647    if (modifier == DRM_FORMAT_MOD_INVALID && modifiers_count > 0) {
648       fprintf(stderr, "Unsupported modifier, resource creation failed.\n");
649       goto fail;
650    }
651 
652    if (templ->usage == PIPE_USAGE_STAGING &&
653        templ->bind == PIPE_BIND_DEPTH_STENCIL &&
654        devinfo->ver < 6)
655       goto fail;
656 
657    const bool isl_surf_created_successfully =
658       crocus_resource_configure_main(screen, res, templ, modifier, 0);
659    if (!isl_surf_created_successfully)
660       goto fail;
661 
662    const char *name = "miptree";
663 
664    unsigned int flags = 0;
665    if (templ->usage == PIPE_USAGE_STAGING)
666       flags |= BO_ALLOC_COHERENT;
667 
668    /* Scanout buffers need to be WC. */
669    if (templ->bind & PIPE_BIND_SCANOUT)
670       flags |= BO_ALLOC_SCANOUT;
671 
672    uint64_t aux_size = 0;
673    uint32_t aux_preferred_alloc_flags;
674 
675    if (!crocus_resource_configure_aux(screen, res, &aux_size,
676                                       &aux_preferred_alloc_flags)) {
677       goto fail;
678    }
679 
680    /* Modifiers require the aux data to be in the same buffer as the main
681     * surface, but we combine them even when a modifiers is not being used.
682     */
683    const uint64_t bo_size =
684       MAX2(res->surf.size_B, res->aux.offset + aux_size);
685    uint32_t alignment = MAX2(4096, res->surf.alignment_B);
686    res->bo = crocus_bo_alloc_tiled(screen->bufmgr, name, bo_size, alignment,
687                                    isl_tiling_to_i915_tiling(res->surf.tiling),
688                                    res->surf.row_pitch_B, flags);
689 
690    if (!res->bo)
691       goto fail;
692 
693    if (aux_size > 0) {
694       res->aux.bo = res->bo;
695       crocus_bo_reference(res->aux.bo);
696       if (!crocus_resource_init_aux_buf(res, flags))
697          goto fail;
698    }
699 
700    if (templ->format == PIPE_FORMAT_S8_UINT && !(templ->usage == PIPE_USAGE_STAGING) &&
701        devinfo->ver == 7 && (templ->bind & PIPE_BIND_SAMPLER_VIEW)) {
702       struct pipe_resource templ_shadow = (struct pipe_resource) {
703          .usage = 0,
704          .bind = PIPE_BIND_SAMPLER_VIEW,
705          .width0 = res->base.b.width0,
706          .height0 = res->base.b.height0,
707          .depth0 = res->base.b.depth0,
708          .last_level = res->base.b.last_level,
709          .nr_samples = res->base.b.nr_samples,
710          .nr_storage_samples = res->base.b.nr_storage_samples,
711          .array_size = res->base.b.array_size,
712          .format = PIPE_FORMAT_R8_UINT,
713          .target = res->base.b.target,
714       };
715       res->shadow = (struct crocus_resource *)screen->base.resource_create(&screen->base, &templ_shadow);
716       assert(res->shadow);
717    }
718 
719    return &res->base.b;
720 
721 fail:
722    crocus_resource_destroy(pscreen, &res->base.b);
723    return NULL;
724 
725 }
726 
727 static struct pipe_resource *
crocus_resource_create(struct pipe_screen * pscreen,const struct pipe_resource * templ)728 crocus_resource_create(struct pipe_screen *pscreen,
729                        const struct pipe_resource *templ)
730 {
731    if (templ->target == PIPE_BUFFER)
732       return crocus_resource_create_for_buffer(pscreen, templ);
733    else
734       return crocus_resource_create_with_modifiers(pscreen, templ, NULL, 0);
735 }
736 
737 static uint64_t
tiling_to_modifier(uint32_t tiling)738 tiling_to_modifier(uint32_t tiling)
739 {
740    static const uint64_t map[] = {
741       [I915_TILING_NONE]   = DRM_FORMAT_MOD_LINEAR,
742       [I915_TILING_X]      = I915_FORMAT_MOD_X_TILED,
743       [I915_TILING_Y]      = I915_FORMAT_MOD_Y_TILED,
744    };
745 
746    assert(tiling < ARRAY_SIZE(map));
747 
748    return map[tiling];
749 }
750 
751 static struct pipe_resource *
crocus_resource_from_user_memory(struct pipe_screen * pscreen,const struct pipe_resource * templ,void * user_memory)752 crocus_resource_from_user_memory(struct pipe_screen *pscreen,
753                                  const struct pipe_resource *templ,
754                                  void *user_memory)
755 {
756    struct crocus_screen *screen = (struct crocus_screen *)pscreen;
757    struct crocus_bufmgr *bufmgr = screen->bufmgr;
758    struct crocus_resource *res = crocus_alloc_resource(pscreen, templ);
759    if (!res)
760       return NULL;
761 
762    assert(templ->target == PIPE_BUFFER);
763 
764    res->internal_format = templ->format;
765    res->bo = crocus_bo_create_userptr(bufmgr, "user",
766                                       user_memory, templ->width0);
767    if (!res->bo) {
768       free(res);
769       return NULL;
770    }
771 
772    util_range_add(&res->base.b, &res->valid_buffer_range, 0, templ->width0);
773 
774    return &res->base.b;
775 }
776 
777 static struct pipe_resource *
crocus_resource_from_handle(struct pipe_screen * pscreen,const struct pipe_resource * templ,struct winsys_handle * whandle,unsigned usage)778 crocus_resource_from_handle(struct pipe_screen *pscreen,
779                             const struct pipe_resource *templ,
780                             struct winsys_handle *whandle,
781                             unsigned usage)
782 {
783    assert(templ->target != PIPE_BUFFER);
784 
785    struct crocus_screen *screen = (struct crocus_screen *)pscreen;
786    struct crocus_bufmgr *bufmgr = screen->bufmgr;
787    struct crocus_resource *res = crocus_alloc_resource(pscreen, templ);
788 
789    if (!res)
790       return NULL;
791 
792    switch (whandle->type) {
793    case WINSYS_HANDLE_TYPE_FD:
794       res->bo = crocus_bo_import_dmabuf(bufmgr, whandle->handle,
795                                         whandle->modifier);
796       break;
797    case WINSYS_HANDLE_TYPE_SHARED:
798       res->bo = crocus_bo_gem_create_from_name(bufmgr, "winsys image",
799                                                whandle->handle);
800       break;
801    default:
802       unreachable("invalid winsys handle type");
803    }
804    if (!res->bo)
805       goto fail;
806 
807    res->offset = whandle->offset;
808    res->external_format = whandle->format;
809 
810    assert(whandle->plane < util_format_get_num_planes(whandle->format));
811    const uint64_t modifier =
812       whandle->modifier != DRM_FORMAT_MOD_INVALID ?
813       whandle->modifier : tiling_to_modifier(res->bo->tiling_mode);
814 
815    UNUSED const bool isl_surf_created_successfully =
816       crocus_resource_configure_main(screen, res, templ, modifier,
817                                      whandle->stride);
818    assert(isl_surf_created_successfully);
819    assert(res->bo->tiling_mode ==
820           isl_tiling_to_i915_tiling(res->surf.tiling));
821 
822    // XXX: create_ccs_buf_for_image?
823    if (whandle->modifier == DRM_FORMAT_MOD_INVALID) {
824       if (!crocus_resource_alloc_separate_aux(screen, res))
825          goto fail;
826    } else {
827       assert(!isl_drm_modifier_has_aux(whandle->modifier));
828    }
829 
830    return &res->base.b;
831 
832 fail:
833    crocus_resource_destroy(pscreen, &res->base.b);
834    return NULL;
835 }
836 
837 static struct pipe_resource *
crocus_resource_from_memobj(struct pipe_screen * pscreen,const struct pipe_resource * templ,struct pipe_memory_object * pmemobj,uint64_t offset)838 crocus_resource_from_memobj(struct pipe_screen *pscreen,
839                             const struct pipe_resource *templ,
840                             struct pipe_memory_object *pmemobj,
841                             uint64_t offset)
842 {
843    /* Disable Depth, and combined Depth+Stencil for now. */
844    if (util_format_has_depth(util_format_description(templ->format)))
845       return NULL;
846 
847    struct crocus_screen *screen = (struct crocus_screen *)pscreen;
848    struct crocus_memory_object *memobj = (struct crocus_memory_object *)pmemobj;
849    struct crocus_resource *res = crocus_alloc_resource(pscreen, templ);
850 
851    if (!res)
852       return NULL;
853 
854    if (templ->flags & PIPE_RESOURCE_FLAG_TEXTURING_MORE_LIKELY) {
855       UNUSED const bool isl_surf_created_successfully =
856          crocus_resource_configure_main(screen, res, templ, DRM_FORMAT_MOD_INVALID, 0);
857       assert(isl_surf_created_successfully);
858    }
859 
860    res->bo = memobj->bo;
861    res->offset = offset;
862    res->external_format = memobj->format;
863 
864    crocus_bo_reference(memobj->bo);
865 
866    return &res->base.b;
867 }
868 
869 static void
crocus_flush_resource(struct pipe_context * ctx,struct pipe_resource * resource)870 crocus_flush_resource(struct pipe_context *ctx, struct pipe_resource *resource)
871 {
872    struct crocus_context *ice = (struct crocus_context *)ctx;
873    struct crocus_resource *res = (void *) resource;
874 
875    /* Modifiers with compression are not supported. */
876    assert(!res->mod_info ||
877           !isl_drm_modifier_has_aux(res->mod_info->modifier));
878 
879    crocus_resource_prepare_access(ice, res,
880                                   0, INTEL_REMAINING_LEVELS,
881                                   0, INTEL_REMAINING_LAYERS,
882                                   ISL_AUX_USAGE_NONE, false);
883 }
884 
885 static void
crocus_resource_disable_aux_on_first_query(struct pipe_resource * resource,unsigned usage)886 crocus_resource_disable_aux_on_first_query(struct pipe_resource *resource,
887                                            unsigned usage)
888 {
889    struct crocus_resource *res = (struct crocus_resource *)resource;
890 
891    /* Modifiers with compression are not supported. */
892    assert(!res->mod_info ||
893           !isl_drm_modifier_has_aux(res->mod_info->modifier));
894 
895    /* Disable aux usage if explicit flush not set and this is the first time
896     * we are dealing with this resource.
897     */
898    if ((!(usage & PIPE_HANDLE_USAGE_EXPLICIT_FLUSH) && res->aux.usage != 0) &&
899        p_atomic_read(&resource->reference.count) == 1) {
900       crocus_resource_disable_aux(res);
901    }
902 }
903 
904 static bool
crocus_resource_get_param(struct pipe_screen * pscreen,struct pipe_context * context,struct pipe_resource * resource,unsigned plane,unsigned layer,unsigned level,enum pipe_resource_param param,unsigned handle_usage,uint64_t * value)905 crocus_resource_get_param(struct pipe_screen *pscreen,
906                           struct pipe_context *context,
907                           struct pipe_resource *resource,
908                           unsigned plane,
909                           unsigned layer,
910                           unsigned level,
911                           enum pipe_resource_param param,
912                           unsigned handle_usage,
913                           uint64_t *value)
914 {
915    struct crocus_screen *screen = (struct crocus_screen *)pscreen;
916    struct crocus_resource *res =
917       (struct crocus_resource *)util_resource_at_index(resource, plane);
918 
919    /* Modifiers with compression are not supported. */
920    assert(!res->mod_info ||
921           !isl_drm_modifier_has_aux(res->mod_info->modifier));
922 
923    bool result;
924    unsigned handle;
925 
926    struct crocus_bo *bo = res->bo;
927 
928    crocus_resource_disable_aux_on_first_query(resource, handle_usage);
929 
930    switch (param) {
931    case PIPE_RESOURCE_PARAM_NPLANES:
932       *value = util_resource_num(resource);
933       return true;
934    case PIPE_RESOURCE_PARAM_STRIDE:
935       *value = res->surf.row_pitch_B;
936       return true;
937    case PIPE_RESOURCE_PARAM_OFFSET:
938       *value = res->offset;
939       return true;
940    case PIPE_RESOURCE_PARAM_MODIFIER:
941       *value = res->mod_info ? res->mod_info->modifier :
942                tiling_to_modifier(isl_tiling_to_i915_tiling(res->surf.tiling));
943       return true;
944    case PIPE_RESOURCE_PARAM_HANDLE_TYPE_SHARED:
945       result = crocus_bo_flink(bo, &handle) == 0;
946       if (result)
947          *value = handle;
948       return result;
949    case PIPE_RESOURCE_PARAM_HANDLE_TYPE_KMS: {
950       /* Because we share the same drm file across multiple crocus_screen, when
951        * we export a GEM handle we must make sure it is valid in the DRM file
952        * descriptor the caller is using (this is the FD given at screen
953        * creation).
954        */
955       uint32_t handle;
956       if (crocus_bo_export_gem_handle_for_device(bo, screen->winsys_fd, &handle))
957          return false;
958       *value = handle;
959       return true;
960    }
961    case PIPE_RESOURCE_PARAM_HANDLE_TYPE_FD:
962       result = crocus_bo_export_dmabuf(bo, (int *) &handle) == 0;
963       if (result)
964          *value = handle;
965       return result;
966    default:
967       return false;
968    }
969 }
970 
971 static bool
crocus_resource_get_handle(struct pipe_screen * pscreen,struct pipe_context * ctx,struct pipe_resource * resource,struct winsys_handle * whandle,unsigned usage)972 crocus_resource_get_handle(struct pipe_screen *pscreen,
973                            struct pipe_context *ctx,
974                            struct pipe_resource *resource,
975                            struct winsys_handle *whandle,
976                            unsigned usage)
977 {
978    struct crocus_screen *screen = (struct crocus_screen *) pscreen;
979    struct crocus_resource *res = (struct crocus_resource *)resource;
980 
981    /* Modifiers with compression are not supported. */
982    assert(!res->mod_info ||
983           !isl_drm_modifier_has_aux(res->mod_info->modifier));
984 
985    crocus_resource_disable_aux_on_first_query(resource, usage);
986 
987    struct crocus_bo *bo;
988    /* If this is a buffer, stride should be 0 - no need to special case */
989    whandle->stride = res->surf.row_pitch_B;
990    bo = res->bo;
991    whandle->format = res->external_format;
992    whandle->modifier =
993       res->mod_info ? res->mod_info->modifier
994                     : tiling_to_modifier(res->bo->tiling_mode);
995 
996 #ifndef NDEBUG
997    enum isl_aux_usage allowed_usage = ISL_AUX_USAGE_NONE;
998 
999    if (res->aux.usage != allowed_usage) {
1000       enum isl_aux_state aux_state = crocus_resource_get_aux_state(res, 0, 0);
1001       assert(aux_state == ISL_AUX_STATE_RESOLVED ||
1002              aux_state == ISL_AUX_STATE_PASS_THROUGH);
1003    }
1004 #endif
1005 
1006    switch (whandle->type) {
1007    case WINSYS_HANDLE_TYPE_SHARED:
1008       return crocus_bo_flink(bo, &whandle->handle) == 0;
1009    case WINSYS_HANDLE_TYPE_KMS: {
1010       /* Because we share the same drm file across multiple crocus_screen, when
1011        * we export a GEM handle we must make sure it is valid in the DRM file
1012        * descriptor the caller is using (this is the FD given at screen
1013        * creation).
1014        */
1015       uint32_t handle;
1016       if (crocus_bo_export_gem_handle_for_device(bo, screen->winsys_fd, &handle))
1017          return false;
1018       whandle->handle = handle;
1019       return true;
1020    }
1021    case WINSYS_HANDLE_TYPE_FD:
1022       return crocus_bo_export_dmabuf(bo, (int *) &whandle->handle) == 0;
1023    }
1024 
1025    return false;
1026 }
1027 
1028 static bool
resource_is_busy(struct crocus_context * ice,struct crocus_resource * res)1029 resource_is_busy(struct crocus_context *ice,
1030                  struct crocus_resource *res)
1031 {
1032    bool busy = crocus_bo_busy(res->bo);
1033 
1034    for (int i = 0; i < ice->batch_count; i++)
1035       busy |= crocus_batch_references(&ice->batches[i], res->bo);
1036 
1037    return busy;
1038 }
1039 
1040 void
crocus_replace_buffer_storage(struct pipe_context * ctx,struct pipe_resource * p_dst,struct pipe_resource * p_src,unsigned num_rebinds,uint32_t rebind_mask,uint32_t delete_buffer_id)1041 crocus_replace_buffer_storage(struct pipe_context *ctx,
1042                               struct pipe_resource *p_dst,
1043                               struct pipe_resource *p_src,
1044                               unsigned num_rebinds,
1045                               uint32_t rebind_mask,
1046                               uint32_t delete_buffer_id)
1047 {
1048    struct crocus_screen *screen = (void *) ctx->screen;
1049    struct crocus_context *ice = (void *) ctx;
1050    struct crocus_resource *dst = (void *) p_dst;
1051    struct crocus_resource *src = (void *) p_src;
1052 
1053    assert(memcmp(&dst->surf, &src->surf, sizeof(dst->surf)) == 0);
1054 
1055    struct crocus_bo *old_bo = dst->bo;
1056 
1057    /* Swap out the backing storage */
1058    crocus_bo_reference(src->bo);
1059    dst->bo = src->bo;
1060 
1061    /* Rebind the buffer, replacing any state referring to the old BO's
1062     * address, and marking state dirty so it's reemitted.
1063     */
1064    screen->vtbl.rebind_buffer(ice, dst);
1065 
1066    crocus_bo_unreference(old_bo);
1067 }
1068 
1069 static void
crocus_invalidate_resource(struct pipe_context * ctx,struct pipe_resource * resource)1070 crocus_invalidate_resource(struct pipe_context *ctx,
1071                            struct pipe_resource *resource)
1072 {
1073    struct crocus_screen *screen = (void *) ctx->screen;
1074    struct crocus_context *ice = (void *) ctx;
1075    struct crocus_resource *res = (void *) resource;
1076 
1077    if (resource->target != PIPE_BUFFER)
1078       return;
1079 
1080    /* If it's already invalidated, don't bother doing anything. */
1081    if (res->valid_buffer_range.start > res->valid_buffer_range.end)
1082       return;
1083 
1084    if (!resource_is_busy(ice, res)) {
1085       /* The resource is idle, so just mark that it contains no data and
1086        * keep using the same underlying buffer object.
1087        */
1088       util_range_set_empty(&res->valid_buffer_range);
1089       return;
1090    }
1091 
1092    /* Otherwise, try and replace the backing storage with a new BO. */
1093 
1094    /* We can't reallocate memory we didn't allocate in the first place. */
1095    if (res->bo->userptr)
1096       return;
1097 
1098    struct crocus_bo *old_bo = res->bo;
1099    struct crocus_bo *new_bo =
1100       crocus_bo_alloc(screen->bufmgr, res->bo->name, resource->width0);
1101 
1102    if (!new_bo)
1103       return;
1104 
1105    /* Swap out the backing storage */
1106    res->bo = new_bo;
1107 
1108    /* Rebind the buffer, replacing any state referring to the old BO's
1109     * address, and marking state dirty so it's reemitted.
1110     */
1111    screen->vtbl.rebind_buffer(ice, res);
1112 
1113    util_range_set_empty(&res->valid_buffer_range);
1114 
1115    crocus_bo_unreference(old_bo);
1116 }
1117 
1118 static void
crocus_flush_staging_region(struct pipe_transfer * xfer,const struct pipe_box * flush_box)1119 crocus_flush_staging_region(struct pipe_transfer *xfer,
1120                             const struct pipe_box *flush_box)
1121 {
1122    if (!(xfer->usage & PIPE_MAP_WRITE))
1123       return;
1124 
1125    struct crocus_transfer *map = (void *) xfer;
1126 
1127    struct pipe_box src_box = *flush_box;
1128 
1129    /* Account for extra alignment padding in staging buffer */
1130    if (xfer->resource->target == PIPE_BUFFER)
1131       src_box.x += xfer->box.x % CROCUS_MAP_BUFFER_ALIGNMENT;
1132 
1133    struct pipe_box dst_box = (struct pipe_box) {
1134       .x = xfer->box.x + flush_box->x,
1135       .y = xfer->box.y + flush_box->y,
1136       .z = xfer->box.z + flush_box->z,
1137       .width = flush_box->width,
1138       .height = flush_box->height,
1139       .depth = flush_box->depth,
1140    };
1141 
1142    crocus_copy_region(map->blorp, map->batch, xfer->resource, xfer->level,
1143                       dst_box.x, dst_box.y, dst_box.z, map->staging, 0,
1144                       &src_box);
1145 }
1146 
1147 static void
crocus_unmap_copy_region(struct crocus_transfer * map)1148 crocus_unmap_copy_region(struct crocus_transfer *map)
1149 {
1150    crocus_resource_destroy(map->staging->screen, map->staging);
1151 
1152    map->ptr = NULL;
1153 }
1154 
1155 static void
crocus_map_copy_region(struct crocus_transfer * map)1156 crocus_map_copy_region(struct crocus_transfer *map)
1157 {
1158    struct pipe_screen *pscreen = &map->batch->screen->base;
1159    struct pipe_transfer *xfer = &map->base.b;
1160    struct pipe_box *box = &xfer->box;
1161    struct crocus_resource *res = (void *) xfer->resource;
1162 
1163    unsigned extra = xfer->resource->target == PIPE_BUFFER ?
1164                     box->x % CROCUS_MAP_BUFFER_ALIGNMENT : 0;
1165 
1166    struct pipe_resource templ = (struct pipe_resource) {
1167       .usage = PIPE_USAGE_STAGING,
1168       .width0 = box->width + extra,
1169       .height0 = box->height,
1170       .depth0 = 1,
1171       .nr_samples = xfer->resource->nr_samples,
1172       .nr_storage_samples = xfer->resource->nr_storage_samples,
1173       .array_size = box->depth,
1174       .format = res->internal_format,
1175    };
1176 
1177    if (xfer->resource->target == PIPE_BUFFER)
1178       templ.target = PIPE_BUFFER;
1179    else if (templ.array_size > 1)
1180       templ.target = PIPE_TEXTURE_2D_ARRAY;
1181    else
1182       templ.target = PIPE_TEXTURE_2D;
1183 
1184    map->staging = crocus_resource_create(pscreen, &templ);
1185 
1186    /* If we fail to create a staging resource, the caller will fallback
1187     * to mapping directly on the CPU.
1188     */
1189    if (!map->staging)
1190       return;
1191 
1192    if (templ.target != PIPE_BUFFER) {
1193       struct isl_surf *surf = &((struct crocus_resource *) map->staging)->surf;
1194       xfer->stride = isl_surf_get_row_pitch_B(surf);
1195       xfer->layer_stride = isl_surf_get_array_pitch(surf);
1196    }
1197 
1198    if (!(xfer->usage & PIPE_MAP_DISCARD_RANGE)) {
1199       crocus_copy_region(map->blorp, map->batch, map->staging, 0, extra, 0, 0,
1200                          xfer->resource, xfer->level, box);
1201       /* Ensure writes to the staging BO land before we map it below. */
1202       crocus_emit_pipe_control_flush(map->batch,
1203                                      "transfer read: flush before mapping",
1204                                      PIPE_CONTROL_RENDER_TARGET_FLUSH |
1205                                      PIPE_CONTROL_CS_STALL);
1206    }
1207 
1208    struct crocus_bo *staging_bo = crocus_resource_bo(map->staging);
1209 
1210    if (crocus_batch_references(map->batch, staging_bo))
1211       crocus_batch_flush(map->batch);
1212 
1213    map->ptr =
1214       crocus_bo_map(map->dbg, staging_bo, xfer->usage & MAP_FLAGS) + extra;
1215 
1216    map->unmap = crocus_unmap_copy_region;
1217 }
1218 
1219 static void
get_image_offset_el(const struct isl_surf * surf,unsigned level,unsigned z,unsigned * out_x0_el,unsigned * out_y0_el)1220 get_image_offset_el(const struct isl_surf *surf, unsigned level, unsigned z,
1221                     unsigned *out_x0_el, unsigned *out_y0_el)
1222 {
1223    ASSERTED uint32_t z0_el, a0_el;
1224    if (surf->dim == ISL_SURF_DIM_3D) {
1225       isl_surf_get_image_offset_el(surf, level, 0, z,
1226                                    out_x0_el, out_y0_el, &z0_el, &a0_el);
1227    } else {
1228       isl_surf_get_image_offset_el(surf, level, z, 0,
1229                                    out_x0_el, out_y0_el, &z0_el, &a0_el);
1230    }
1231    assert(z0_el == 0 && a0_el == 0);
1232 }
1233 
1234 void
crocus_resource_get_image_offset(struct crocus_resource * res,uint32_t level,uint32_t z,uint32_t * x,uint32_t * y)1235 crocus_resource_get_image_offset(struct crocus_resource *res,
1236                                  uint32_t level, uint32_t z,
1237                                  uint32_t *x, uint32_t *y)
1238 {
1239    get_image_offset_el(&res->surf, level, z, x, y);
1240 }
1241 
1242 /**
1243  * Get pointer offset into stencil buffer.
1244  *
1245  * The stencil buffer is W tiled. Since the GTT is incapable of W fencing, we
1246  * must decode the tile's layout in software.
1247  *
1248  * See
1249  *   - PRM, 2011 Sandy Bridge, Volume 1, Part 2, Section 4.5.2.1 W-Major Tile
1250  *     Format.
1251  *   - PRM, 2011 Sandy Bridge, Volume 1, Part 2, Section 4.5.3 Tiling Algorithm
1252  *
1253  * Even though the returned offset is always positive, the return type is
1254  * signed due to
1255  *    commit e8b1c6d6f55f5be3bef25084fdd8b6127517e137
1256  *    mesa: Fix return type of  _mesa_get_format_bytes() (#37351)
1257  */
1258 static intptr_t
s8_offset(uint32_t stride,uint32_t x,uint32_t y,bool swizzled)1259 s8_offset(uint32_t stride, uint32_t x, uint32_t y, bool swizzled)
1260 {
1261    uint32_t tile_size = 4096;
1262    uint32_t tile_width = 64;
1263    uint32_t tile_height = 64;
1264    uint32_t row_size = 64 * stride / 2; /* Two rows are interleaved. */
1265 
1266    uint32_t tile_x = x / tile_width;
1267    uint32_t tile_y = y / tile_height;
1268 
1269    /* The byte's address relative to the tile's base addres. */
1270    uint32_t byte_x = x % tile_width;
1271    uint32_t byte_y = y % tile_height;
1272 
1273    uintptr_t u = tile_y * row_size
1274                + tile_x * tile_size
1275                + 512 * (byte_x / 8)
1276                +  64 * (byte_y / 8)
1277                +  32 * ((byte_y / 4) % 2)
1278                +  16 * ((byte_x / 4) % 2)
1279                +   8 * ((byte_y / 2) % 2)
1280                +   4 * ((byte_x / 2) % 2)
1281                +   2 * (byte_y % 2)
1282                +   1 * (byte_x % 2);
1283 
1284    if (swizzled) {
1285       /* adjust for bit6 swizzling */
1286       if (((byte_x / 8) % 2) == 1) {
1287          if (((byte_y / 8) % 2) == 0) {
1288             u += 64;
1289          } else {
1290             u -= 64;
1291          }
1292       }
1293    }
1294 
1295    return u;
1296 }
1297 
1298 static void
crocus_unmap_s8(struct crocus_transfer * map)1299 crocus_unmap_s8(struct crocus_transfer *map)
1300 {
1301    struct pipe_transfer *xfer = &map->base.b;
1302    const struct pipe_box *box = &xfer->box;
1303    struct crocus_resource *res = (struct crocus_resource *) xfer->resource;
1304    struct isl_surf *surf = &res->surf;
1305 
1306    if (xfer->usage & PIPE_MAP_WRITE) {
1307       uint8_t *untiled_s8_map = map->ptr;
1308       uint8_t *tiled_s8_map =
1309          crocus_bo_map(map->dbg, res->bo, (xfer->usage | MAP_RAW) & MAP_FLAGS);
1310 
1311       for (int s = 0; s < box->depth; s++) {
1312          unsigned x0_el, y0_el;
1313          get_image_offset_el(surf, xfer->level, box->z + s, &x0_el, &y0_el);
1314 
1315          for (uint32_t y = 0; y < box->height; y++) {
1316             for (uint32_t x = 0; x < box->width; x++) {
1317                ptrdiff_t offset = s8_offset(surf->row_pitch_B,
1318                                             x0_el + box->x + x,
1319                                             y0_el + box->y + y,
1320                                             map->has_swizzling);
1321                tiled_s8_map[offset] =
1322                   untiled_s8_map[s * xfer->layer_stride + y * xfer->stride + x];
1323             }
1324          }
1325       }
1326    }
1327 
1328    free(map->buffer);
1329 }
1330 
1331 static void
crocus_map_s8(struct crocus_transfer * map)1332 crocus_map_s8(struct crocus_transfer *map)
1333 {
1334    struct pipe_transfer *xfer = &map->base.b;
1335    const struct pipe_box *box = &xfer->box;
1336    struct crocus_resource *res = (struct crocus_resource *) xfer->resource;
1337    struct isl_surf *surf = &res->surf;
1338 
1339    xfer->stride = surf->row_pitch_B;
1340    xfer->layer_stride = xfer->stride * box->height;
1341 
1342    /* The tiling and detiling functions require that the linear buffer has
1343     * a 16-byte alignment (that is, its `x0` is 16-byte aligned).  Here we
1344     * over-allocate the linear buffer to get the proper alignment.
1345     */
1346    map->buffer = map->ptr = malloc(xfer->layer_stride * box->depth);
1347    assert(map->buffer);
1348 
1349    /* One of either READ_BIT or WRITE_BIT or both is set.  READ_BIT implies no
1350     * INVALIDATE_RANGE_BIT.  WRITE_BIT needs the original values read in unless
1351     * invalidate is set, since we'll be writing the whole rectangle from our
1352     * temporary buffer back out.
1353     */
1354    if (!(xfer->usage & PIPE_MAP_DISCARD_RANGE)) {
1355       uint8_t *untiled_s8_map = map->ptr;
1356       uint8_t *tiled_s8_map =
1357          crocus_bo_map(map->dbg, res->bo, (xfer->usage | MAP_RAW) & MAP_FLAGS);
1358 
1359       for (int s = 0; s < box->depth; s++) {
1360          unsigned x0_el, y0_el;
1361          get_image_offset_el(surf, xfer->level, box->z + s, &x0_el, &y0_el);
1362 
1363          for (uint32_t y = 0; y < box->height; y++) {
1364             for (uint32_t x = 0; x < box->width; x++) {
1365                ptrdiff_t offset = s8_offset(surf->row_pitch_B,
1366                                             x0_el + box->x + x,
1367                                             y0_el + box->y + y,
1368                                             map->has_swizzling);
1369                untiled_s8_map[s * xfer->layer_stride + y * xfer->stride + x] =
1370                   tiled_s8_map[offset];
1371             }
1372          }
1373       }
1374    }
1375 
1376    map->unmap = crocus_unmap_s8;
1377 }
1378 
1379 /* Compute extent parameters for use with tiled_memcpy functions.
1380  * xs are in units of bytes and ys are in units of strides.
1381  */
1382 static inline void
tile_extents(const struct isl_surf * surf,const struct pipe_box * box,unsigned level,int z,unsigned * x1_B,unsigned * x2_B,unsigned * y1_el,unsigned * y2_el)1383 tile_extents(const struct isl_surf *surf,
1384              const struct pipe_box *box,
1385              unsigned level, int z,
1386              unsigned *x1_B, unsigned *x2_B,
1387              unsigned *y1_el, unsigned *y2_el)
1388 {
1389    const struct isl_format_layout *fmtl = isl_format_get_layout(surf->format);
1390    const unsigned cpp = fmtl->bpb / 8;
1391 
1392    assert(box->x % fmtl->bw == 0);
1393    assert(box->y % fmtl->bh == 0);
1394 
1395    unsigned x0_el, y0_el;
1396    get_image_offset_el(surf, level, box->z + z, &x0_el, &y0_el);
1397 
1398    *x1_B = (box->x / fmtl->bw + x0_el) * cpp;
1399    *y1_el = box->y / fmtl->bh + y0_el;
1400    *x2_B = (DIV_ROUND_UP(box->x + box->width, fmtl->bw) + x0_el) * cpp;
1401    *y2_el = DIV_ROUND_UP(box->y + box->height, fmtl->bh) + y0_el;
1402 }
1403 
1404 static void
crocus_unmap_tiled_memcpy(struct crocus_transfer * map)1405 crocus_unmap_tiled_memcpy(struct crocus_transfer *map)
1406 {
1407    struct pipe_transfer *xfer = &map->base.b;
1408    const struct pipe_box *box = &xfer->box;
1409    struct crocus_resource *res = (struct crocus_resource *) xfer->resource;
1410    struct isl_surf *surf = &res->surf;
1411 
1412    if (xfer->usage & PIPE_MAP_WRITE) {
1413       char *dst =
1414          crocus_bo_map(map->dbg, res->bo, (xfer->usage | MAP_RAW) & MAP_FLAGS);
1415 
1416       for (int s = 0; s < box->depth; s++) {
1417          unsigned x1, x2, y1, y2;
1418          tile_extents(surf, box, xfer->level, s, &x1, &x2, &y1, &y2);
1419 
1420          void *ptr = map->ptr + s * xfer->layer_stride;
1421 
1422          isl_memcpy_linear_to_tiled(x1, x2, y1, y2, dst, ptr,
1423                                     surf->row_pitch_B, xfer->stride,
1424                                     map->has_swizzling,
1425                                     surf->tiling, ISL_MEMCPY);
1426       }
1427    }
1428    os_free_aligned(map->buffer);
1429    map->buffer = map->ptr = NULL;
1430 }
1431 
1432 static void
crocus_map_tiled_memcpy(struct crocus_transfer * map)1433 crocus_map_tiled_memcpy(struct crocus_transfer *map)
1434 {
1435    struct pipe_transfer *xfer = &map->base.b;
1436    const struct pipe_box *box = &xfer->box;
1437    struct crocus_resource *res = (struct crocus_resource *) xfer->resource;
1438    struct isl_surf *surf = &res->surf;
1439 
1440    xfer->stride = ALIGN(surf->row_pitch_B, 16);
1441    xfer->layer_stride = xfer->stride * box->height;
1442 
1443    unsigned x1, x2, y1, y2;
1444    tile_extents(surf, box, xfer->level, 0, &x1, &x2, &y1, &y2);
1445 
1446    /* The tiling and detiling functions require that the linear buffer has
1447     * a 16-byte alignment (that is, its `x0` is 16-byte aligned).  Here we
1448     * over-allocate the linear buffer to get the proper alignment.
1449     */
1450    map->buffer =
1451       os_malloc_aligned(xfer->layer_stride * box->depth, 16);
1452    assert(map->buffer);
1453    map->ptr = (char *)map->buffer + (x1 & 0xf);
1454 
1455    if (!(xfer->usage & PIPE_MAP_DISCARD_RANGE)) {
1456       char *src =
1457          crocus_bo_map(map->dbg, res->bo, (xfer->usage | MAP_RAW) & MAP_FLAGS);
1458 
1459       for (int s = 0; s < box->depth; s++) {
1460          unsigned x1, x2, y1, y2;
1461          tile_extents(surf, box, xfer->level, s, &x1, &x2, &y1, &y2);
1462 
1463          /* Use 's' rather than 'box->z' to rebase the first slice to 0. */
1464          void *ptr = map->ptr + s * xfer->layer_stride;
1465 
1466          isl_memcpy_tiled_to_linear(x1, x2, y1, y2, ptr, src, xfer->stride,
1467                                     surf->row_pitch_B,
1468                                     map->has_swizzling,
1469                                     surf->tiling,
1470 #if defined(USE_SSE41)
1471                                     util_get_cpu_caps()->has_sse4_1 ? ISL_MEMCPY_STREAMING_LOAD :
1472 #endif
1473                                     ISL_MEMCPY);
1474       }
1475    }
1476 
1477    map->unmap = crocus_unmap_tiled_memcpy;
1478 }
1479 
1480 static void
crocus_map_direct(struct crocus_transfer * map)1481 crocus_map_direct(struct crocus_transfer *map)
1482 {
1483    struct pipe_transfer *xfer = &map->base.b;
1484    struct pipe_box *box = &xfer->box;
1485    struct crocus_resource *res = (struct crocus_resource *) xfer->resource;
1486 
1487    void *ptr = crocus_bo_map(map->dbg, res->bo, xfer->usage & MAP_FLAGS);
1488 
1489    if (res->base.b.target == PIPE_BUFFER) {
1490       xfer->stride = 0;
1491       xfer->layer_stride = 0;
1492 
1493       map->ptr = ptr + box->x;
1494    } else {
1495       struct isl_surf *surf = &res->surf;
1496       const struct isl_format_layout *fmtl =
1497          isl_format_get_layout(surf->format);
1498       const unsigned cpp = fmtl->bpb / 8;
1499       unsigned x0_el, y0_el;
1500 
1501       assert(box->x % fmtl->bw == 0);
1502       assert(box->y % fmtl->bh == 0);
1503       get_image_offset_el(surf, xfer->level, box->z, &x0_el, &y0_el);
1504 
1505       x0_el += box->x / fmtl->bw;
1506       y0_el += box->y / fmtl->bh;
1507 
1508       xfer->stride = isl_surf_get_row_pitch_B(surf);
1509       xfer->layer_stride = isl_surf_get_array_pitch(surf);
1510 
1511       map->ptr = ptr + y0_el * xfer->stride + x0_el * cpp;
1512    }
1513 }
1514 
1515 static bool
can_promote_to_async(const struct crocus_resource * res,const struct pipe_box * box,unsigned usage)1516 can_promote_to_async(const struct crocus_resource *res,
1517                      const struct pipe_box *box,
1518                      unsigned usage)
1519 {
1520    /* If we're writing to a section of the buffer that hasn't even been
1521     * initialized with useful data, then we can safely promote this write
1522     * to be unsynchronized.  This helps the common pattern of appending data.
1523     */
1524    return res->base.b.target == PIPE_BUFFER && (usage & PIPE_MAP_WRITE) &&
1525           !(usage & TC_TRANSFER_MAP_NO_INFER_UNSYNCHRONIZED) &&
1526           !util_ranges_intersect(&res->valid_buffer_range, box->x,
1527                                  box->x + box->width);
1528 }
1529 
1530 static void *
crocus_transfer_map(struct pipe_context * ctx,struct pipe_resource * resource,unsigned level,unsigned usage,const struct pipe_box * box,struct pipe_transfer ** ptransfer)1531 crocus_transfer_map(struct pipe_context *ctx,
1532                     struct pipe_resource *resource,
1533                     unsigned level,
1534                     unsigned usage,
1535                     const struct pipe_box *box,
1536                     struct pipe_transfer **ptransfer)
1537 {
1538    struct crocus_context *ice = (struct crocus_context *)ctx;
1539    struct crocus_resource *res = (struct crocus_resource *)resource;
1540    struct isl_surf *surf = &res->surf;
1541    struct crocus_screen *screen = (struct crocus_screen *)ctx->screen;
1542 
1543    if (usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE) {
1544       /* Replace the backing storage with a fresh buffer for non-async maps */
1545       if (!(usage & (PIPE_MAP_UNSYNCHRONIZED |
1546                      TC_TRANSFER_MAP_NO_INVALIDATE)))
1547          crocus_invalidate_resource(ctx, resource);
1548 
1549       /* If we can discard the whole resource, we can discard the range. */
1550       usage |= PIPE_MAP_DISCARD_RANGE;
1551    }
1552 
1553    if (!(usage & PIPE_MAP_UNSYNCHRONIZED) &&
1554        can_promote_to_async(res, box, usage)) {
1555       usage |= PIPE_MAP_UNSYNCHRONIZED;
1556    }
1557 
1558    bool map_would_stall = false;
1559 
1560    if (!(usage & PIPE_MAP_UNSYNCHRONIZED)) {
1561       map_would_stall = resource_is_busy(ice, res) ||
1562          crocus_has_invalid_primary(res, level, 1, box->z, box->depth);
1563 
1564 
1565       if (map_would_stall && (usage & PIPE_MAP_DONTBLOCK) &&
1566                              (usage & PIPE_MAP_DIRECTLY))
1567          return NULL;
1568    }
1569 
1570    if (surf->tiling != ISL_TILING_LINEAR &&
1571        (usage & PIPE_MAP_DIRECTLY))
1572       return NULL;
1573 
1574    struct crocus_transfer *map;
1575    if (usage & TC_TRANSFER_MAP_THREADED_UNSYNC)
1576       map = slab_zalloc(&ice->transfer_pool_unsync);
1577    else
1578       map = slab_zalloc(&ice->transfer_pool);
1579 
1580    if (!map)
1581       return NULL;
1582 
1583    struct pipe_transfer *xfer = &map->base.b;
1584 
1585    map->dbg = &ice->dbg;
1586 
1587    map->has_swizzling = screen->devinfo.has_bit6_swizzle;
1588    pipe_resource_reference(&xfer->resource, resource);
1589    xfer->level = level;
1590    xfer->usage = usage;
1591    xfer->box = *box;
1592    *ptransfer = xfer;
1593 
1594    map->dest_had_defined_contents =
1595       util_ranges_intersect(&res->valid_buffer_range, box->x,
1596                             box->x + box->width);
1597 
1598    if (usage & PIPE_MAP_WRITE)
1599       util_range_add(&res->base.b, &res->valid_buffer_range, box->x, box->x + box->width);
1600 
1601    /* Avoid using GPU copies for persistent/coherent buffers, as the idea
1602     * there is to access them simultaneously on the CPU & GPU.  This also
1603     * avoids trying to use GPU copies for our u_upload_mgr buffers which
1604     * contain state we're constructing for a GPU draw call, which would
1605     * kill us with infinite stack recursion.
1606     */
1607    bool no_gpu = usage & (PIPE_MAP_PERSISTENT |
1608                           PIPE_MAP_COHERENT |
1609                           PIPE_MAP_DIRECTLY);
1610 
1611    /* GPU copies are not useful for buffer reads.  Instead of stalling to
1612     * read from the original buffer, we'd simply copy it to a temporary...
1613     * then stall (a bit longer) to read from that buffer.
1614     *
1615     * Images are less clear-cut.  Color resolves are destructive, removing
1616     * the underlying compression, so we'd rather blit the data to a linear
1617     * temporary and map that, to avoid the resolve.  (It might be better to
1618     * a tiled temporary and use the tiled_memcpy paths...)
1619     */
1620    if (!(usage & PIPE_MAP_DISCARD_RANGE) &&
1621        !crocus_has_invalid_primary(res, level, 1, box->z, box->depth))
1622       no_gpu = true;
1623 
1624    const struct isl_format_layout *fmtl = isl_format_get_layout(surf->format);
1625    if (fmtl->txc == ISL_TXC_ASTC)
1626       no_gpu = true;
1627 
1628    if (map_would_stall && !no_gpu) {
1629       /* If we need a synchronous mapping and the resource is busy, or needs
1630        * resolving, we copy to/from a linear temporary buffer using the GPU.
1631        */
1632       map->batch = &ice->batches[CROCUS_BATCH_RENDER];
1633       map->blorp = &ice->blorp;
1634       crocus_map_copy_region(map);
1635    }
1636 
1637    /* If we've requested a direct mapping, or crocus_map_copy_region failed
1638     * to create a staging resource, then map it directly on the CPU.
1639     */
1640    if (!map->ptr) {
1641       if (resource->target != PIPE_BUFFER) {
1642          crocus_resource_access_raw(ice, res,
1643                                     level, box->z, box->depth,
1644                                     usage & PIPE_MAP_WRITE);
1645       }
1646 
1647       if (!(usage & PIPE_MAP_UNSYNCHRONIZED)) {
1648          for (int i = 0; i < ice->batch_count; i++) {
1649             if (crocus_batch_references(&ice->batches[i], res->bo))
1650                crocus_batch_flush(&ice->batches[i]);
1651          }
1652       }
1653 
1654       if (surf->tiling == ISL_TILING_W) {
1655          /* TODO: Teach crocus_map_tiled_memcpy about W-tiling... */
1656          crocus_map_s8(map);
1657       } else if (surf->tiling != ISL_TILING_LINEAR && screen->devinfo.ver > 4) {
1658          crocus_map_tiled_memcpy(map);
1659       } else {
1660          crocus_map_direct(map);
1661       }
1662    }
1663 
1664    return map->ptr;
1665 }
1666 
1667 static void
crocus_transfer_flush_region(struct pipe_context * ctx,struct pipe_transfer * xfer,const struct pipe_box * box)1668 crocus_transfer_flush_region(struct pipe_context *ctx,
1669                              struct pipe_transfer *xfer,
1670                              const struct pipe_box *box)
1671 {
1672    struct crocus_context *ice = (struct crocus_context *)ctx;
1673    struct crocus_resource *res = (struct crocus_resource *) xfer->resource;
1674    struct crocus_transfer *map = (void *) xfer;
1675 
1676    if (map->staging)
1677       crocus_flush_staging_region(xfer, box);
1678 
1679    uint32_t history_flush = 0;
1680 
1681    if (res->base.b.target == PIPE_BUFFER) {
1682       if (map->staging)
1683          history_flush |= PIPE_CONTROL_RENDER_TARGET_FLUSH;
1684 
1685       if (map->dest_had_defined_contents)
1686          history_flush |= crocus_flush_bits_for_history(res);
1687 
1688       util_range_add(&res->base.b, &res->valid_buffer_range, box->x, box->x + box->width);
1689    }
1690 
1691    if (history_flush & ~PIPE_CONTROL_CS_STALL) {
1692       for (int i = 0; i < ice->batch_count; i++) {
1693          struct crocus_batch *batch = &ice->batches[i];
1694 
1695          if (!batch->command.bo)
1696             continue;
1697          if (batch->contains_draw || batch->cache.render->entries) {
1698             crocus_batch_maybe_flush(batch, 24);
1699             crocus_emit_pipe_control_flush(batch,
1700                                            "cache history: transfer flush",
1701                                            history_flush);
1702          }
1703       }
1704    }
1705 
1706    /* Make sure we flag constants dirty even if there's no need to emit
1707     * any PIPE_CONTROLs to a batch.
1708     */
1709    crocus_dirty_for_history(ice, res);
1710 }
1711 
1712 static void
crocus_transfer_unmap(struct pipe_context * ctx,struct pipe_transfer * xfer)1713 crocus_transfer_unmap(struct pipe_context *ctx, struct pipe_transfer *xfer)
1714 {
1715    struct crocus_context *ice = (struct crocus_context *)ctx;
1716    struct crocus_transfer *map = (void *) xfer;
1717 
1718    if (!(xfer->usage & (PIPE_MAP_FLUSH_EXPLICIT |
1719                         PIPE_MAP_COHERENT))) {
1720       struct pipe_box flush_box = {
1721          .x = 0, .y = 0, .z = 0,
1722          .width  = xfer->box.width,
1723          .height = xfer->box.height,
1724          .depth  = xfer->box.depth,
1725       };
1726       crocus_transfer_flush_region(ctx, xfer, &flush_box);
1727    }
1728 
1729    if (map->unmap)
1730       map->unmap(map);
1731 
1732    pipe_resource_reference(&xfer->resource, NULL);
1733    /* transfer_unmap is always called from the driver thread, so we have to
1734     * use transfer_pool, not transfer_pool_unsync.  Freeing an object into a
1735     * different pool is allowed, however.
1736     */
1737    slab_free(&ice->transfer_pool, map);
1738 }
1739 
1740 /**
1741  * Mark state dirty that needs to be re-emitted when a resource is written.
1742  */
1743 void
crocus_dirty_for_history(struct crocus_context * ice,struct crocus_resource * res)1744 crocus_dirty_for_history(struct crocus_context *ice,
1745                          struct crocus_resource *res)
1746 {
1747    uint64_t stage_dirty = 0ull;
1748 
1749    if (res->bind_history & PIPE_BIND_CONSTANT_BUFFER) {
1750       stage_dirty |= ((uint64_t)res->bind_stages) << CROCUS_SHIFT_FOR_STAGE_DIRTY_CONSTANTS;
1751    }
1752 
1753    ice->state.stage_dirty |= stage_dirty;
1754 }
1755 
1756 /**
1757  * Produce a set of PIPE_CONTROL bits which ensure data written to a
1758  * resource becomes visible, and any stale read cache data is invalidated.
1759  */
1760 uint32_t
crocus_flush_bits_for_history(struct crocus_resource * res)1761 crocus_flush_bits_for_history(struct crocus_resource *res)
1762 {
1763    uint32_t flush = PIPE_CONTROL_CS_STALL;
1764 
1765    if (res->bind_history & PIPE_BIND_CONSTANT_BUFFER) {
1766       flush |= PIPE_CONTROL_CONST_CACHE_INVALIDATE |
1767                PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
1768    }
1769 
1770    if (res->bind_history & PIPE_BIND_SAMPLER_VIEW)
1771       flush |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
1772 
1773    if (res->bind_history & (PIPE_BIND_VERTEX_BUFFER | PIPE_BIND_INDEX_BUFFER))
1774       flush |= PIPE_CONTROL_VF_CACHE_INVALIDATE;
1775 
1776    if (res->bind_history & (PIPE_BIND_SHADER_BUFFER | PIPE_BIND_SHADER_IMAGE))
1777       flush |= PIPE_CONTROL_DATA_CACHE_FLUSH;
1778 
1779    return flush;
1780 }
1781 
1782 void
crocus_flush_and_dirty_for_history(struct crocus_context * ice,struct crocus_batch * batch,struct crocus_resource * res,uint32_t extra_flags,const char * reason)1783 crocus_flush_and_dirty_for_history(struct crocus_context *ice,
1784                                    struct crocus_batch *batch,
1785                                    struct crocus_resource *res,
1786                                    uint32_t extra_flags,
1787                                    const char *reason)
1788 {
1789    if (res->base.b.target != PIPE_BUFFER)
1790       return;
1791 
1792    uint32_t flush = crocus_flush_bits_for_history(res) | extra_flags;
1793 
1794    crocus_emit_pipe_control_flush(batch, reason, flush);
1795 
1796    crocus_dirty_for_history(ice, res);
1797 }
1798 
1799 bool
crocus_resource_set_clear_color(struct crocus_context * ice,struct crocus_resource * res,union isl_color_value color)1800 crocus_resource_set_clear_color(struct crocus_context *ice,
1801                                 struct crocus_resource *res,
1802                                 union isl_color_value color)
1803 {
1804    if (memcmp(&res->aux.clear_color, &color, sizeof(color)) != 0) {
1805       res->aux.clear_color = color;
1806       return true;
1807    }
1808 
1809    return false;
1810 }
1811 
1812 union isl_color_value
crocus_resource_get_clear_color(const struct crocus_resource * res)1813 crocus_resource_get_clear_color(const struct crocus_resource *res)
1814 {
1815    assert(res->aux.bo);
1816 
1817    return res->aux.clear_color;
1818 }
1819 
1820 static enum pipe_format
crocus_resource_get_internal_format(struct pipe_resource * p_res)1821 crocus_resource_get_internal_format(struct pipe_resource *p_res)
1822 {
1823    struct crocus_resource *res = (void *) p_res;
1824    return res->internal_format;
1825 }
1826 
1827 static const struct u_transfer_vtbl transfer_vtbl = {
1828    .resource_create       = crocus_resource_create,
1829    .resource_destroy      = crocus_resource_destroy,
1830    .transfer_map          = crocus_transfer_map,
1831    .transfer_unmap        = crocus_transfer_unmap,
1832    .transfer_flush_region = crocus_transfer_flush_region,
1833    .get_internal_format   = crocus_resource_get_internal_format,
1834    .set_stencil           = crocus_resource_set_separate_stencil,
1835    .get_stencil           = crocus_resource_get_separate_stencil,
1836 };
1837 
1838 static bool
crocus_is_dmabuf_modifier_supported(struct pipe_screen * pscreen,uint64_t modifier,enum pipe_format pfmt,bool * external_only)1839 crocus_is_dmabuf_modifier_supported(struct pipe_screen *pscreen,
1840                                     uint64_t modifier, enum pipe_format pfmt,
1841                                     bool *external_only)
1842 {
1843    struct crocus_screen *screen = (void *) pscreen;
1844    const struct intel_device_info *devinfo = &screen->devinfo;
1845 
1846    if (modifier_is_supported(devinfo, pfmt, 0, modifier)) {
1847       if (external_only)
1848          *external_only = false;
1849 
1850       return true;
1851    }
1852 
1853    return false;
1854 }
1855 
1856 static unsigned int
crocus_get_dmabuf_modifier_planes(struct pipe_screen * pscreen,uint64_t modifier,enum pipe_format format)1857 crocus_get_dmabuf_modifier_planes(struct pipe_screen *pscreen, uint64_t modifier,
1858                                   enum pipe_format format)
1859 {
1860    return util_format_get_num_planes(format);
1861 }
1862 
1863 static struct pipe_memory_object *
crocus_memobj_create_from_handle(struct pipe_screen * pscreen,struct winsys_handle * whandle,bool dedicated)1864 crocus_memobj_create_from_handle(struct pipe_screen *pscreen,
1865                                  struct winsys_handle *whandle,
1866                                  bool dedicated)
1867 {
1868    struct crocus_screen *screen = (struct crocus_screen *)pscreen;
1869    struct crocus_memory_object *memobj = CALLOC_STRUCT(crocus_memory_object);
1870    struct crocus_bo *bo;
1871    const struct isl_drm_modifier_info *mod_inf;
1872 
1873    if (!memobj)
1874       return NULL;
1875 
1876    switch (whandle->type) {
1877    case WINSYS_HANDLE_TYPE_SHARED:
1878       bo = crocus_bo_gem_create_from_name(screen->bufmgr, "winsys image",
1879                                         whandle->handle);
1880       break;
1881    case WINSYS_HANDLE_TYPE_FD:
1882       mod_inf = isl_drm_modifier_get_info(whandle->modifier);
1883       if (mod_inf) {
1884          bo = crocus_bo_import_dmabuf(screen->bufmgr, whandle->handle,
1885                                     whandle->modifier);
1886       } else {
1887          /* If we can't get information about the tiling from the
1888           * kernel we ignore it. We are going to set it when we
1889           * create the resource.
1890           */
1891          bo = crocus_bo_import_dmabuf_no_mods(screen->bufmgr,
1892                                             whandle->handle);
1893       }
1894 
1895       break;
1896    default:
1897       unreachable("invalid winsys handle type");
1898    }
1899 
1900    if (!bo) {
1901       free(memobj);
1902       return NULL;
1903    }
1904 
1905    memobj->b.dedicated = dedicated;
1906    memobj->bo = bo;
1907    memobj->format = whandle->format;
1908    memobj->stride = whandle->stride;
1909 
1910    return &memobj->b;
1911 }
1912 
1913 static void
crocus_memobj_destroy(struct pipe_screen * pscreen,struct pipe_memory_object * pmemobj)1914 crocus_memobj_destroy(struct pipe_screen *pscreen,
1915                       struct pipe_memory_object *pmemobj)
1916 {
1917    struct crocus_memory_object *memobj = (struct crocus_memory_object *)pmemobj;
1918 
1919    crocus_bo_unreference(memobj->bo);
1920    free(memobj);
1921 }
1922 
1923 void
crocus_init_screen_resource_functions(struct pipe_screen * pscreen)1924 crocus_init_screen_resource_functions(struct pipe_screen *pscreen)
1925 {
1926    struct crocus_screen *screen = (void *) pscreen;
1927    pscreen->query_dmabuf_modifiers = crocus_query_dmabuf_modifiers;
1928    pscreen->is_dmabuf_modifier_supported = crocus_is_dmabuf_modifier_supported;
1929    pscreen->get_dmabuf_modifier_planes = crocus_get_dmabuf_modifier_planes;
1930    pscreen->resource_create_with_modifiers =
1931       crocus_resource_create_with_modifiers;
1932    pscreen->resource_create = u_transfer_helper_resource_create;
1933    pscreen->resource_from_user_memory = crocus_resource_from_user_memory;
1934    pscreen->resource_from_handle = crocus_resource_from_handle;
1935    pscreen->resource_from_memobj = crocus_resource_from_memobj;
1936    pscreen->resource_get_handle = crocus_resource_get_handle;
1937    pscreen->resource_get_param = crocus_resource_get_param;
1938    pscreen->resource_destroy = u_transfer_helper_resource_destroy;
1939    pscreen->memobj_create_from_handle = crocus_memobj_create_from_handle;
1940    pscreen->memobj_destroy = crocus_memobj_destroy;
1941 
1942    enum u_transfer_helper_flags transfer_flags = U_TRANSFER_HELPER_MSAA_MAP;
1943    if (screen->devinfo.ver >= 6) {
1944       transfer_flags |= U_TRANSFER_HELPER_SEPARATE_Z32S8 |
1945                U_TRANSFER_HELPER_SEPARATE_STENCIL;
1946    }
1947 
1948    pscreen->transfer_helper =
1949       u_transfer_helper_create(&transfer_vtbl, transfer_flags);
1950 }
1951 
1952 void
crocus_init_resource_functions(struct pipe_context * ctx)1953 crocus_init_resource_functions(struct pipe_context *ctx)
1954 {
1955    ctx->flush_resource = crocus_flush_resource;
1956    ctx->clear_buffer = u_default_clear_buffer;
1957    ctx->invalidate_resource = crocus_invalidate_resource;
1958    ctx->buffer_map = u_transfer_helper_transfer_map;
1959    ctx->texture_map = u_transfer_helper_transfer_map;
1960    ctx->transfer_flush_region = u_transfer_helper_transfer_flush_region;
1961    ctx->buffer_unmap = u_transfer_helper_transfer_unmap;
1962    ctx->texture_unmap = u_transfer_helper_transfer_unmap;
1963    ctx->buffer_subdata = u_default_buffer_subdata;
1964    ctx->texture_subdata = u_default_texture_subdata;
1965 }
1966