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