1 /*
2 * Copyright (c) 2012-2015 Etnaviv Project
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sub license,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the
12 * next paragraph) shall be included in all copies or substantial portions
13 * of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 * Wladimir J. van der Laan <laanwj@gmail.com>
25 */
26
27 #include "etnaviv_transfer.h"
28 #include "etnaviv_clear_blit.h"
29 #include "etnaviv_context.h"
30 #include "etnaviv_debug.h"
31 #include "etnaviv_etc2.h"
32 #include "etnaviv_screen.h"
33
34 #include "pipe/p_defines.h"
35 #include "util/format/u_formats.h"
36 #include "pipe/p_screen.h"
37 #include "pipe/p_state.h"
38 #include "util/format/u_format.h"
39 #include "util/u_inlines.h"
40 #include "util/u_memory.h"
41 #include "util/u_surface.h"
42 #include "util/u_transfer.h"
43
44 #include "hw/common_3d.xml.h"
45
46 #include "drm-uapi/drm_fourcc.h"
47
48 #define ETNA_PIPE_MAP_DISCARD_LEVEL (PIPE_MAP_DRV_PRV << 0)
49
50 /* Compute offset into a 1D/2D/3D buffer of a certain box.
51 * This box must be aligned to the block width and height of the
52 * underlying format. */
53 static inline size_t
etna_compute_offset(enum pipe_format format,const struct pipe_box * box,size_t stride,size_t layer_stride)54 etna_compute_offset(enum pipe_format format, const struct pipe_box *box,
55 size_t stride, size_t layer_stride)
56 {
57 return box->z * layer_stride +
58 box->y / util_format_get_blockheight(format) * stride +
59 box->x / util_format_get_blockwidth(format) *
60 util_format_get_blocksize(format);
61 }
62
etna_patch_data(void * buffer,const struct pipe_transfer * ptrans)63 static void etna_patch_data(void *buffer, const struct pipe_transfer *ptrans)
64 {
65 struct pipe_resource *prsc = ptrans->resource;
66 struct etna_resource *rsc = etna_resource(prsc);
67 struct etna_resource_level *level = &rsc->levels[ptrans->level];
68
69 if (likely(!etna_etc2_needs_patching(prsc)))
70 return;
71
72 if (level->patched)
73 return;
74
75 /* do have the offsets of blocks to patch? */
76 if (!level->patch_offsets) {
77 level->patch_offsets = CALLOC_STRUCT(util_dynarray);
78
79 etna_etc2_calculate_blocks(buffer, ptrans->stride,
80 ptrans->box.width, ptrans->box.height,
81 prsc->format, level->patch_offsets);
82 }
83
84 etna_etc2_patch(buffer, level->patch_offsets);
85
86 level->patched = true;
87 }
88
etna_unpatch_data(void * buffer,const struct pipe_transfer * ptrans)89 static void etna_unpatch_data(void *buffer, const struct pipe_transfer *ptrans)
90 {
91 struct pipe_resource *prsc = ptrans->resource;
92 struct etna_resource *rsc = etna_resource(prsc);
93 struct etna_resource_level *level = &rsc->levels[ptrans->level];
94
95 if (!level->patched)
96 return;
97
98 etna_etc2_patch(buffer, level->patch_offsets);
99
100 level->patched = false;
101 }
102
103 static void
etna_transfer_unmap(struct pipe_context * pctx,struct pipe_transfer * ptrans)104 etna_transfer_unmap(struct pipe_context *pctx, struct pipe_transfer *ptrans)
105 {
106 struct etna_context *ctx = etna_context(pctx);
107 struct etna_transfer *trans = etna_transfer(ptrans);
108 struct etna_resource *rsc = etna_resource(ptrans->resource);
109 struct etna_resource_level *res_level = &rsc->levels[ptrans->level];
110
111 if (rsc->texture && !etna_resource_newer(rsc, etna_resource(rsc->texture)))
112 rsc = etna_resource(rsc->texture); /* switch to using the texture resource */
113
114 /*
115 * Temporary resources are always pulled into the CPU domain, must push them
116 * back into GPU domain before the RS execs the blit to the base resource.
117 */
118 if (trans->rsc)
119 etna_bo_cpu_fini(etna_resource(trans->rsc)->bo);
120
121 if (ptrans->usage & PIPE_MAP_WRITE) {
122 if (etna_resource_level_needs_flush(res_level)) {
123 if (ptrans->usage & ETNA_PIPE_MAP_DISCARD_LEVEL)
124 etna_resource_level_mark_flushed(res_level);
125 else
126 etna_copy_resource(pctx, &rsc->base, &rsc->base, ptrans->level, ptrans->level);
127 }
128
129 if (trans->rsc) {
130 /* We have a temporary resource due to either tile status or
131 * tiling format. Write back the updated buffer contents.
132 */
133 etna_copy_resource_box(pctx, ptrans->resource, trans->rsc,
134 ptrans->level, 0, &ptrans->box);
135 } else if (trans->staging) {
136 /* map buffer object */
137 if (rsc->layout == ETNA_LAYOUT_TILED) {
138 for (unsigned z = 0; z < ptrans->box.depth; z++) {
139 etna_texture_tile(
140 trans->mapped + (ptrans->box.z + z) * res_level->layer_stride,
141 trans->staging + z * ptrans->layer_stride,
142 ptrans->box.x, ptrans->box.y,
143 res_level->stride, ptrans->box.width, ptrans->box.height,
144 ptrans->stride, util_format_get_blocksize(rsc->base.format));
145 }
146 } else if (rsc->layout == ETNA_LAYOUT_LINEAR) {
147 util_copy_box(trans->mapped, rsc->base.format, res_level->stride,
148 res_level->layer_stride, ptrans->box.x,
149 ptrans->box.y, ptrans->box.z, ptrans->box.width,
150 ptrans->box.height, ptrans->box.depth,
151 trans->staging, ptrans->stride,
152 ptrans->layer_stride, 0, 0, 0 /* src x,y,z */);
153 } else {
154 BUG("unsupported tiling %i", rsc->layout);
155 }
156 }
157
158 if (ptrans->resource->target == PIPE_BUFFER)
159 util_range_add(&rsc->base, &rsc->valid_buffer_range,
160 ptrans->box.x, ptrans->box.x + ptrans->box.width);
161
162 etna_resource_level_ts_mark_invalid(res_level);
163 etna_resource_level_mark_changed(res_level);
164
165 if (rsc->base.bind & PIPE_BIND_SAMPLER_VIEW)
166 ctx->dirty |= ETNA_DIRTY_TEXTURE_CACHES;
167 if (rsc->base.bind & PIPE_BIND_CONSTANT_BUFFER)
168 ctx->dirty |= ETNA_DIRTY_SHADER_CACHES;
169 }
170
171 /* We need to have the patched data ready for the GPU. */
172 etna_patch_data(trans->mapped, ptrans);
173
174 /*
175 * Transfers without a temporary are only pulled into the CPU domain if they
176 * are not mapped unsynchronized. If they are, must push them back into GPU
177 * domain after CPU access is finished.
178 */
179 if (!trans->rsc && !(ptrans->usage & PIPE_MAP_UNSYNCHRONIZED))
180 etna_bo_cpu_fini(rsc->bo);
181
182 FREE(trans->staging);
183 pipe_resource_reference(&trans->rsc, NULL);
184 pipe_resource_reference(&ptrans->resource, NULL);
185 slab_free(&ctx->transfer_pool, trans);
186 }
187
188 static void *
etna_transfer_map(struct pipe_context * pctx,struct pipe_resource * prsc,unsigned level,unsigned usage,const struct pipe_box * box,struct pipe_transfer ** out_transfer)189 etna_transfer_map(struct pipe_context *pctx, struct pipe_resource *prsc,
190 unsigned level,
191 unsigned usage,
192 const struct pipe_box *box,
193 struct pipe_transfer **out_transfer)
194 {
195 struct etna_context *ctx = etna_context(pctx);
196 struct etna_screen *screen = ctx->screen;
197 struct etna_resource *rsc = etna_resource(prsc);
198 struct etna_resource_level *res_level = &rsc->levels[level];
199 struct etna_transfer *trans;
200 struct pipe_transfer *ptrans;
201 enum pipe_format format = prsc->format;
202
203 trans = slab_zalloc(&ctx->transfer_pool);
204 if (!trans)
205 return NULL;
206
207 assert(level <= prsc->last_level);
208
209 /*
210 * Upgrade to UNSYNCHRONIZED if target is PIPE_BUFFER and range is uninitialized.
211 */
212 if ((usage & PIPE_MAP_WRITE) &&
213 (prsc->target == PIPE_BUFFER) &&
214 !util_ranges_intersect(&rsc->valid_buffer_range,
215 box->x,
216 box->x + box->width)) {
217 usage |= PIPE_MAP_UNSYNCHRONIZED;
218 }
219
220 /* Upgrade DISCARD_RANGE to WHOLE_RESOURCE if the whole resource is
221 * being mapped. If we add buffer reallocation to avoid CPU/GPU sync this
222 * check needs to be extended to coherent mappings and shared resources.
223 */
224 if ((usage & PIPE_MAP_DISCARD_RANGE) &&
225 !(usage & PIPE_MAP_UNSYNCHRONIZED) &&
226 !(prsc->flags & PIPE_RESOURCE_FLAG_MAP_PERSISTENT) &&
227 prsc->last_level == 0 &&
228 prsc->width0 == box->width &&
229 prsc->height0 == box->height &&
230 prsc->depth0 == box->depth &&
231 prsc->array_size == 1) {
232 usage |= PIPE_MAP_DISCARD_WHOLE_RESOURCE;
233 }
234
235 if ((usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE) ||
236 ((usage & PIPE_MAP_DISCARD_RANGE) &&
237 util_texrange_covers_whole_level(prsc, level, box->x, box->y, box->z,
238 box->width, box->height, box->depth)))
239 usage |= ETNA_PIPE_MAP_DISCARD_LEVEL;
240
241
242 ptrans = &trans->base;
243 pipe_resource_reference(&ptrans->resource, prsc);
244 ptrans->level = level;
245 ptrans->usage = usage;
246 ptrans->box = *box;
247
248 /* This one is a little tricky: if we have a separate render resource, which
249 * is newer than the base resource we want the transfer to target this one,
250 * to get the most up-to-date content, but only if we don't have a texture
251 * target of the same age, as transfering in/out of the texture target is
252 * generally preferred for the reasons listed below */
253 if (rsc->render && etna_resource_newer(etna_resource(rsc->render), rsc) &&
254 (!rsc->texture || etna_resource_newer(etna_resource(rsc->render),
255 etna_resource(rsc->texture)))) {
256 rsc = etna_resource(rsc->render);
257 }
258
259 if (rsc->texture && !etna_resource_newer(rsc, etna_resource(rsc->texture))) {
260 /* We have a texture resource which is the same age or newer than the
261 * render resource. Use the texture resource, which avoids bouncing
262 * pixels between the two resources, and we can de-tile it in s/w. */
263 rsc = etna_resource(rsc->texture);
264 } else if (etna_resource_level_ts_valid(res_level) ||
265 (rsc->layout != ETNA_LAYOUT_LINEAR &&
266 etna_resource_hw_tileable(screen->specs.use_blt, prsc) &&
267 /* HALIGN 4 resources are incompatible with the resolve engine,
268 * so fall back to using software to detile this resource. */
269 rsc->halign != TEXTURE_HALIGN_FOUR)) {
270
271 /* If the resource level has valid tile status, we copy the transfer
272 * region to a staging resource using the BLT or RS engine, which will
273 * resolve the TS information. We also do this for tiled resources without
274 * TS, but only if the dedicated blit engines can handle them. */
275
276 if (usage & PIPE_MAP_DIRECTLY) {
277 slab_free(&ctx->transfer_pool, trans);
278 BUG("unsupported map flags %#x with tile status/tiled layout", usage);
279 return NULL;
280 }
281
282 struct pipe_resource templ = *prsc;
283 templ.last_level = 0;
284 templ.width0 = res_level->width;
285 templ.height0 = res_level->height;
286 templ.nr_samples = 0;
287 templ.bind = PIPE_BIND_RENDER_TARGET;
288
289 trans->rsc = etna_resource_alloc(pctx->screen, ETNA_LAYOUT_LINEAR,
290 DRM_FORMAT_MOD_LINEAR, &templ);
291 if (!trans->rsc) {
292 slab_free(&ctx->transfer_pool, trans);
293 return NULL;
294 }
295
296 if (!screen->specs.use_blt) {
297 /* Need to align the transfer region to satisfy RS restrictions, as we
298 * really want to hit the RS blit path here.
299 */
300 unsigned w_align, h_align;
301
302 if (rsc->layout & ETNA_LAYOUT_BIT_SUPER) {
303 w_align = 64;
304 h_align = 64 * ctx->screen->specs.pixel_pipes;
305 } else {
306 w_align = ETNA_RS_WIDTH_MASK + 1;
307 h_align = ETNA_RS_HEIGHT_MASK + 1;
308 }
309
310 ptrans->box.width += ptrans->box.x & (w_align - 1);
311 ptrans->box.x = ptrans->box.x & ~(w_align - 1);
312 ptrans->box.width = align(ptrans->box.width, (ETNA_RS_WIDTH_MASK + 1));
313 ptrans->box.height += ptrans->box.y & (h_align - 1);
314 ptrans->box.y = ptrans->box.y & ~(h_align - 1);
315 ptrans->box.height = align(ptrans->box.height, ETNA_RS_HEIGHT_MASK + 1);
316 }
317
318 if ((usage & PIPE_MAP_READ) || !(usage & ETNA_PIPE_MAP_DISCARD_LEVEL))
319 etna_copy_resource_box(pctx, trans->rsc, &rsc->base, 0, level, &ptrans->box);
320
321 /* Switch to using the temporary resource instead */
322 rsc = etna_resource(trans->rsc);
323 res_level = &rsc->levels[0];
324 }
325
326 /* XXX we don't handle PIPE_MAP_FLUSH_EXPLICIT; this flag can be ignored
327 * when mapping in-place,
328 * but when not in place we need to fire off the copy operation in
329 * transfer_flush_region (currently
330 * a no-op) instead of unmap. Need to handle this to support
331 * ARB_map_buffer_range extension at least.
332 */
333
334 /*
335 * Pull resources into the CPU domain. Only skipped for unsynchronized
336 * transfers without a temporary resource.
337 */
338 if (trans->rsc || !(usage & PIPE_MAP_UNSYNCHRONIZED)) {
339 enum etna_resource_status status = etna_resource_status(ctx, rsc);
340 uint32_t prep_flags = 0;
341
342 /*
343 * Always flush if we have the temporary resource and have a copy to this
344 * outstanding. Otherwise infer flush requirement from resource access and
345 * current GPU usage (reads must wait for GPU writes, writes must have
346 * exclusive access to the buffer).
347 */
348 if ((trans->rsc && (status & ETNA_PENDING_WRITE)) ||
349 (!trans->rsc &&
350 (((usage & PIPE_MAP_READ) && (status & ETNA_PENDING_WRITE)) ||
351 ((usage & PIPE_MAP_WRITE) && status)))) {
352 etna_flush(pctx, NULL, 0, true);
353 }
354
355 if (usage & PIPE_MAP_READ)
356 prep_flags |= DRM_ETNA_PREP_READ;
357 if (usage & PIPE_MAP_WRITE)
358 prep_flags |= DRM_ETNA_PREP_WRITE;
359
360 /*
361 * The ETC2 patching operates in-place on the resource, so the resource will
362 * get written even on read-only transfers. This blocks the GPU to sample
363 * from this resource.
364 */
365 if ((usage & PIPE_MAP_READ) && etna_etc2_needs_patching(prsc))
366 prep_flags |= DRM_ETNA_PREP_WRITE;
367
368 if (etna_bo_cpu_prep(rsc->bo, prep_flags))
369 goto fail_prep;
370 }
371
372 /* map buffer object */
373 trans->mapped = etna_bo_map(rsc->bo);
374 if (!trans->mapped)
375 goto fail;
376
377 *out_transfer = ptrans;
378
379 if (rsc->layout == ETNA_LAYOUT_LINEAR) {
380 ptrans->stride = res_level->stride;
381 ptrans->layer_stride = res_level->layer_stride;
382
383 trans->mapped += res_level->offset +
384 etna_compute_offset(prsc->format, box, res_level->stride,
385 res_level->layer_stride);
386
387 /* We need to have the unpatched data ready for the gfx stack. */
388 if (usage & PIPE_MAP_READ)
389 etna_unpatch_data(trans->mapped, ptrans);
390
391 return trans->mapped;
392 } else {
393 unsigned divSizeX = util_format_get_blockwidth(format);
394 unsigned divSizeY = util_format_get_blockheight(format);
395
396 /* No direct mappings of tiled, since we need to manually
397 * tile/untile.
398 */
399 if (usage & PIPE_MAP_DIRECTLY)
400 goto fail;
401
402 trans->mapped += res_level->offset;
403 ptrans->stride = align(box->width, divSizeX) * util_format_get_blocksize(format); /* row stride in bytes */
404 ptrans->layer_stride = align(box->height, divSizeY) * ptrans->stride;
405 size_t size = ptrans->layer_stride * box->depth;
406
407 trans->staging = MALLOC(size);
408 if (!trans->staging)
409 goto fail;
410
411 if (usage & PIPE_MAP_READ) {
412 if (rsc->layout == ETNA_LAYOUT_TILED) {
413 for (unsigned z = 0; z < ptrans->box.depth; z++) {
414 etna_texture_untile(trans->staging + z * ptrans->layer_stride,
415 trans->mapped + (ptrans->box.z + z) * res_level->layer_stride,
416 ptrans->box.x, ptrans->box.y, res_level->stride,
417 ptrans->box.width, ptrans->box.height, ptrans->stride,
418 util_format_get_blocksize(rsc->base.format));
419 }
420 } else if (rsc->layout == ETNA_LAYOUT_LINEAR) {
421 util_copy_box(trans->staging, rsc->base.format, ptrans->stride,
422 ptrans->layer_stride, 0, 0, 0, /* dst x,y,z */
423 ptrans->box.width, ptrans->box.height,
424 ptrans->box.depth, trans->mapped, res_level->stride,
425 res_level->layer_stride, ptrans->box.x,
426 ptrans->box.y, ptrans->box.z);
427 } else {
428 /* TODO supertiling */
429 BUG("unsupported tiling %i for reading", rsc->layout);
430 }
431 }
432
433 return trans->staging;
434 }
435
436 fail:
437 etna_bo_cpu_fini(rsc->bo);
438 fail_prep:
439 etna_transfer_unmap(pctx, ptrans);
440 return NULL;
441 }
442
443 static void
etna_transfer_flush_region(struct pipe_context * pctx,struct pipe_transfer * ptrans,const struct pipe_box * box)444 etna_transfer_flush_region(struct pipe_context *pctx,
445 struct pipe_transfer *ptrans,
446 const struct pipe_box *box)
447 {
448 struct etna_resource *rsc = etna_resource(ptrans->resource);
449
450 if (ptrans->resource->target == PIPE_BUFFER)
451 util_range_add(&rsc->base,
452 &rsc->valid_buffer_range,
453 ptrans->box.x + box->x,
454 ptrans->box.x + box->x + box->width);
455 }
456
457 void
etna_transfer_init(struct pipe_context * pctx)458 etna_transfer_init(struct pipe_context *pctx)
459 {
460 pctx->buffer_map = etna_transfer_map;
461 pctx->texture_map = etna_transfer_map;
462 pctx->transfer_flush_region = etna_transfer_flush_region;
463 pctx->buffer_unmap = etna_transfer_unmap;
464 pctx->texture_unmap = etna_transfer_unmap;
465 pctx->buffer_subdata = u_default_buffer_subdata;
466 pctx->texture_subdata = u_default_texture_subdata;
467 }
468