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
32#include "pipe/p_defines.h"
33#include "pipe/p_format.h"
34#include "pipe/p_screen.h"
35#include "pipe/p_state.h"
36#include "util/u_format.h"
37#include "util/u_inlines.h"
38#include "util/u_memory.h"
39#include "util/u_surface.h"
40#include "util/u_transfer.h"
41
42/* Compute offset into a 1D/2D/3D buffer of a certain box.
43 * This box must be aligned to the block width and height of the
44 * underlying format. */
45static inline size_t
46etna_compute_offset(enum pipe_format format, const struct pipe_box *box,
47                    size_t stride, size_t layer_stride)
48{
49   return box->z * layer_stride +
50          box->y / util_format_get_blockheight(format) * stride +
51          box->x / util_format_get_blockwidth(format) *
52             util_format_get_blocksize(format);
53}
54
55static void
56etna_transfer_unmap(struct pipe_context *pctx, struct pipe_transfer *ptrans)
57{
58   struct etna_context *ctx = etna_context(pctx);
59   struct etna_transfer *trans = etna_transfer(ptrans);
60   struct etna_resource *rsc = etna_resource(ptrans->resource);
61
62   /* XXX
63    * When writing to a resource that is already in use, replace the resource
64    * with a completely new buffer
65    * and free the old one using a fenced free.
66    * The most tricky case to implement will be: tiled or supertiled surface,
67    * partial write, target not aligned to 4/64. */
68   assert(ptrans->level <= rsc->base.last_level);
69
70   if (rsc->texture && !etna_resource_newer(rsc, etna_resource(rsc->texture)))
71      rsc = etna_resource(rsc->texture); /* switch to using the texture resource */
72
73   if (ptrans->usage & PIPE_TRANSFER_WRITE) {
74      if (trans->rsc) {
75         /* We have a temporary resource due to either tile status or
76          * tiling format. Write back the updated buffer contents.
77          * FIXME: we need to invalidate the tile status. */
78         etna_copy_resource(pctx, ptrans->resource, trans->rsc, ptrans->level,
79                            trans->rsc->last_level);
80      } else if (trans->staging) {
81         /* map buffer object */
82         struct etna_resource_level *res_level = &rsc->levels[ptrans->level];
83         void *mapped = etna_bo_map(rsc->bo) + res_level->offset;
84
85         if (rsc->layout == ETNA_LAYOUT_LINEAR || rsc->layout == ETNA_LAYOUT_TILED) {
86            if (rsc->layout == ETNA_LAYOUT_TILED && !util_format_is_compressed(rsc->base.format)) {
87               etna_texture_tile(
88                  mapped + ptrans->box.z * res_level->layer_stride,
89                  trans->staging, ptrans->box.x, ptrans->box.y,
90                  res_level->stride, ptrans->box.width, ptrans->box.height,
91                  ptrans->stride, util_format_get_blocksize(rsc->base.format));
92            } else { /* non-tiled or compressed format */
93               util_copy_box(mapped, rsc->base.format, res_level->stride,
94                             res_level->layer_stride, ptrans->box.x,
95                             ptrans->box.y, ptrans->box.z, ptrans->box.width,
96                             ptrans->box.height, ptrans->box.depth,
97                             trans->staging, ptrans->stride,
98                             ptrans->layer_stride, 0, 0, 0 /* src x,y,z */);
99            }
100         } else {
101            BUG("unsupported tiling %i", rsc->layout);
102         }
103
104         FREE(trans->staging);
105      }
106
107      rsc->seqno++;
108      etna_bo_cpu_fini(rsc->bo);
109
110      if (rsc->base.bind & PIPE_BIND_SAMPLER_VIEW) {
111         /* XXX do we need to flush the CPU cache too or start a write barrier
112          * to make sure the GPU sees it? */
113         ctx->dirty |= ETNA_DIRTY_TEXTURE_CACHES;
114      }
115   }
116
117   pipe_resource_reference(&trans->rsc, NULL);
118   pipe_resource_reference(&ptrans->resource, NULL);
119   slab_free(&ctx->transfer_pool, trans);
120}
121
122static void *
123etna_transfer_map(struct pipe_context *pctx, struct pipe_resource *prsc,
124                  unsigned level,
125                  unsigned usage,
126                  const struct pipe_box *box,
127                  struct pipe_transfer **out_transfer)
128{
129   struct etna_context *ctx = etna_context(pctx);
130   struct etna_resource *rsc = etna_resource(prsc);
131   struct etna_transfer *trans;
132   struct pipe_transfer *ptrans;
133   enum pipe_format format = prsc->format;
134
135   trans = slab_alloc(&ctx->transfer_pool);
136   if (!trans)
137      return NULL;
138
139   /* slab_alloc() doesn't zero */
140   memset(trans, 0, sizeof(*trans));
141
142   ptrans = &trans->base;
143   pipe_resource_reference(&ptrans->resource, prsc);
144   ptrans->level = level;
145   ptrans->usage = usage;
146   ptrans->box = *box;
147
148   assert(level <= prsc->last_level);
149
150   if (rsc->texture && !etna_resource_newer(rsc, etna_resource(rsc->texture))) {
151      /* We have a texture resource which is the same age or newer than the
152       * render resource. Use the texture resource, which avoids bouncing
153       * pixels between the two resources, and we can de-tile it in s/w. */
154      rsc = etna_resource(rsc->texture);
155   } else if (rsc->ts_bo ||
156              (rsc->layout != ETNA_LAYOUT_LINEAR &&
157               util_format_get_blocksize(format) > 1 &&
158               /* HALIGN 4 resources are incompatible with the resolve engine,
159                * so fall back to using software to detile this resource. */
160               rsc->halign != TEXTURE_HALIGN_FOUR)) {
161      /* If the surface has tile status, we need to resolve it first.
162       * The strategy we implement here is to use the RS to copy the
163       * depth buffer, filling in the "holes" where the tile status
164       * indicates that it's clear. We also do this for tiled
165       * resources, but only if the RS can blit them. */
166      if (usage & PIPE_TRANSFER_MAP_DIRECTLY) {
167         slab_free(&ctx->transfer_pool, trans);
168         BUG("unsupported transfer flags %#x with tile status/tiled layout", usage);
169         return NULL;
170      }
171
172      if (prsc->depth0 > 1) {
173         slab_free(&ctx->transfer_pool, trans);
174         BUG("resource has depth >1 with tile status");
175         return NULL;
176      }
177
178      struct pipe_resource templ = *prsc;
179      templ.nr_samples = 0;
180      templ.bind = PIPE_BIND_RENDER_TARGET;
181
182      trans->rsc = etna_resource_alloc(pctx->screen, ETNA_LAYOUT_LINEAR, &templ);
183      if (!trans->rsc) {
184         slab_free(&ctx->transfer_pool, trans);
185         return NULL;
186      }
187
188      etna_copy_resource(pctx, trans->rsc, prsc, level, trans->rsc->last_level);
189
190      /* Switch to using the temporary resource instead */
191      rsc = etna_resource(trans->rsc);
192   }
193
194   struct etna_resource_level *res_level = &rsc->levels[level];
195
196   /* Always sync if we have the temporary resource.  The PIPE_TRANSFER_READ
197    * case could be optimised if we knew whether the resource has outstanding
198    * rendering. */
199   if (usage & PIPE_TRANSFER_READ || trans->rsc)
200      etna_resource_wait(pctx, rsc);
201
202   /* XXX we don't handle PIPE_TRANSFER_FLUSH_EXPLICIT; this flag can be ignored
203    * when mapping in-place,
204    * but when not in place we need to fire off the copy operation in
205    * transfer_flush_region (currently
206    * a no-op) instead of unmap. Need to handle this to support
207    * ARB_map_buffer_range extension at least.
208    */
209   /* XXX we don't take care of current operations on the resource; which can
210      be, at some point in the pipeline
211      which is not yet executed:
212
213      - bound as surface
214      - bound through vertex buffer
215      - bound through index buffer
216      - bound in sampler view
217      - used in clear_render_target / clear_depth_stencil operation
218      - used in blit
219      - used in resource_copy_region
220
221      How do other drivers record this information over course of the rendering
222      pipeline?
223      Is it necessary at all? Only in case we want to provide a fast path and
224      map the resource directly
225      (and for PIPE_TRANSFER_MAP_DIRECTLY) and we don't want to force a sync.
226      We also need to know whether the resource is in use to determine if a sync
227      is needed (or just do it
228      always, but that comes at the expense of performance).
229
230      A conservative approximation without too much overhead would be to mark
231      all resources that have
232      been bound at some point as busy. A drawback would be that accessing
233      resources that have
234      been bound but are no longer in use for a while still carry a performance
235      penalty. On the other hand,
236      the program could be using PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE or
237      PIPE_TRANSFER_UNSYNCHRONIZED to
238      avoid this in the first place...
239
240      A) We use an in-pipe copy engine, and queue the copy operation after unmap
241      so that the copy
242         will be performed when all current commands have been executed.
243         Using the RS is possible, not sure if always efficient. This can also
244      do any kind of tiling for us.
245         Only possible when PIPE_TRANSFER_DISCARD_RANGE is set.
246      B) We discard the entire resource (or at least, the mipmap level) and
247      allocate new memory for it.
248         Only possible when mapping the entire resource or
249      PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE is set.
250    */
251
252   /* No need to allocate a buffer for copying if the resource is not in use,
253    * and no tiling is needed, can just return a direct pointer.
254    */
255   bool in_place = rsc->layout == ETNA_LAYOUT_LINEAR ||
256                   (rsc->layout == ETNA_LAYOUT_TILED &&
257                    util_format_is_compressed(prsc->format));
258
259   /* Ignore PIPE_TRANSFER_UNSYNCHRONIZED and PIPE_TRANSFER_DONTBLOCK here.
260    * It appears that Gallium operates the index/vertex buffers in a
261    * circular fashion, and the CPU can catch up with the GPU and starts
262    * overwriting yet-to-be-processed entries, causing rendering corruption. */
263   uint32_t prep_flags = 0;
264
265   if (usage & PIPE_TRANSFER_READ)
266      prep_flags |= DRM_ETNA_PREP_READ;
267   if (usage & PIPE_TRANSFER_WRITE)
268      prep_flags |= DRM_ETNA_PREP_WRITE;
269
270   if (etna_bo_cpu_prep(rsc->bo, prep_flags))
271      goto fail_prep;
272
273   /* map buffer object */
274   void *mapped = etna_bo_map(rsc->bo);
275   if (!mapped)
276      goto fail;
277
278   *out_transfer = ptrans;
279
280   if (in_place) {
281      ptrans->stride = res_level->stride;
282      ptrans->layer_stride = res_level->layer_stride;
283
284      return mapped + res_level->offset +
285             etna_compute_offset(prsc->format, box, res_level->stride,
286                                 res_level->layer_stride);
287   } else {
288      unsigned divSizeX = util_format_get_blockwidth(format);
289      unsigned divSizeY = util_format_get_blockheight(format);
290
291      /* No direct mappings of tiled, since we need to manually
292       * tile/untile.
293       */
294      if (usage & PIPE_TRANSFER_MAP_DIRECTLY)
295         goto fail;
296
297      mapped += res_level->offset;
298      ptrans->stride = align(box->width, divSizeX) * util_format_get_blocksize(format); /* row stride in bytes */
299      ptrans->layer_stride = align(box->height, divSizeY) * ptrans->stride;
300      size_t size = ptrans->layer_stride * box->depth;
301
302      trans->staging = MALLOC(size);
303      if (!trans->staging)
304         goto fail;
305
306      if (usage & PIPE_TRANSFER_READ) {
307         /* untile or copy resource for reading */
308         if (rsc->layout == ETNA_LAYOUT_LINEAR || rsc->layout == ETNA_LAYOUT_TILED) {
309            if (rsc->layout == ETNA_LAYOUT_TILED && !util_format_is_compressed(rsc->base.format)) {
310               etna_texture_untile(trans->staging,
311                                   mapped + ptrans->box.z * res_level->layer_stride,
312                                   ptrans->box.x, ptrans->box.y, res_level->stride,
313                                   ptrans->box.width, ptrans->box.height, ptrans->stride,
314                                   util_format_get_blocksize(rsc->base.format));
315            } else { /* non-tiled or compressed format */
316               util_copy_box(trans->staging, rsc->base.format, ptrans->stride,
317                             ptrans->layer_stride, 0, 0, 0, /* dst x,y,z */
318                             ptrans->box.width, ptrans->box.height,
319                             ptrans->box.depth, mapped, res_level->stride,
320                             res_level->layer_stride, ptrans->box.x,
321                             ptrans->box.y, ptrans->box.z);
322            }
323         } else /* TODO supertiling */
324         {
325            BUG("unsupported tiling %i for reading", rsc->layout);
326         }
327      }
328
329      return trans->staging;
330   }
331
332fail:
333   etna_bo_cpu_fini(rsc->bo);
334fail_prep:
335   etna_transfer_unmap(pctx, ptrans);
336   return NULL;
337}
338
339static void
340etna_transfer_flush_region(struct pipe_context *pctx,
341                           struct pipe_transfer *transfer,
342                           const struct pipe_box *box)
343{
344   /* NOOP for now */
345}
346
347void
348etna_transfer_init(struct pipe_context *pctx)
349{
350   pctx->transfer_map = etna_transfer_map;
351   pctx->transfer_flush_region = etna_transfer_flush_region;
352   pctx->transfer_unmap = etna_transfer_unmap;
353   pctx->buffer_subdata = u_default_buffer_subdata;
354   pctx->texture_subdata = u_default_texture_subdata;
355}
356