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