1/*
2 * Copyright 2014, 2015 Red Hat.
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 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * 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 AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include "util/u_format.h"
24#include "util/u_inlines.h"
25#include "util/u_memory.h"
26
27#include "virgl_context.h"
28#include "virgl_resource.h"
29#include "virgl_screen.h"
30
31static void virgl_copy_region_with_blit(struct pipe_context *pipe,
32                                        struct pipe_resource *dst,
33                                        unsigned dst_level,
34                                        const struct pipe_box *dst_box,
35                                        struct pipe_resource *src,
36                                        unsigned src_level,
37                                        const struct pipe_box *src_box)
38{
39   struct pipe_blit_info blit;
40
41   assert(src_box->width == dst_box->width);
42   assert(src_box->height == dst_box->height);
43   assert(src_box->depth == dst_box->depth);
44
45   memset(&blit, 0, sizeof(blit));
46   blit.src.resource = src;
47   blit.src.format = src->format;
48   blit.src.level = src_level;
49   blit.src.box = *src_box;
50   blit.dst.resource = dst;
51   blit.dst.format = dst->format;
52   blit.dst.level = dst_level;
53   blit.dst.box.x = dst_box->x;
54   blit.dst.box.y = dst_box->y;
55   blit.dst.box.z = dst_box->z;
56   blit.dst.box.width = src_box->width;
57   blit.dst.box.height = src_box->height;
58   blit.dst.box.depth = src_box->depth;
59   blit.mask = util_format_get_mask(src->format) &
60      util_format_get_mask(dst->format);
61   blit.filter = PIPE_TEX_FILTER_NEAREST;
62
63   if (blit.mask) {
64      pipe->blit(pipe, &blit);
65   }
66}
67
68static unsigned temp_bind(unsigned orig)
69{
70   unsigned warn = ~(PIPE_BIND_RENDER_TARGET | PIPE_BIND_DEPTH_STENCIL |
71                     PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_DISPLAY_TARGET);
72   if (orig & warn)
73      debug_printf("VIRGL: Warning, possibly unhandled bind: %x\n",
74                   orig & warn);
75
76   return orig & (PIPE_BIND_DEPTH_STENCIL | PIPE_BIND_RENDER_TARGET);
77}
78
79static void virgl_init_temp_resource_from_box(struct pipe_resource *res,
80                                              struct pipe_resource *orig,
81                                              const struct pipe_box *box,
82                                              unsigned level, unsigned flags,
83                                              enum pipe_format fmt)
84{
85   memset(res, 0, sizeof(*res));
86   res->bind = temp_bind(orig->bind);
87   res->format = fmt;
88   res->width0 = box->width;
89   res->height0 = box->height;
90   res->depth0 = 1;
91   res->array_size = 1;
92   res->usage = PIPE_USAGE_STAGING;
93   res->flags = flags;
94
95   /* We must set the correct texture target and dimensions for a 3D box. */
96   if (box->depth > 1 && util_max_layer(orig, level) > 0)
97      res->target = orig->target;
98   else
99      res->target = PIPE_TEXTURE_2D;
100
101   if (res->target != PIPE_BUFFER)
102      res->bind = PIPE_BIND_RENDER_TARGET;
103
104   switch (res->target) {
105   case PIPE_TEXTURE_1D_ARRAY:
106   case PIPE_TEXTURE_2D_ARRAY:
107   case PIPE_TEXTURE_CUBE_ARRAY:
108      res->array_size = box->depth;
109      break;
110   case PIPE_TEXTURE_3D:
111      res->depth0 = box->depth;
112      break;
113   default:
114      break;
115   }
116}
117
118static void *texture_transfer_map_plain(struct pipe_context *ctx,
119                                        struct pipe_resource *resource,
120                                        unsigned level,
121                                        unsigned usage,
122                                        const struct pipe_box *box,
123                                        struct pipe_transfer **transfer)
124{
125   struct virgl_context *vctx = virgl_context(ctx);
126   struct virgl_winsys *vws = virgl_screen(ctx->screen)->vws;
127   struct virgl_resource *vtex = virgl_resource(resource);
128   struct virgl_transfer *trans;
129   bool flush, readback;
130
131   trans = virgl_resource_create_transfer(&vctx->transfer_pool, resource,
132                                          &vtex->metadata, level, usage, box);
133   trans->resolve_transfer = NULL;
134
135   assert(resource->nr_samples <= 1);
136
137   flush = virgl_res_needs_flush(vctx, trans);
138   if (flush)
139      ctx->flush(ctx, NULL, 0);
140
141   readback = virgl_res_needs_readback(vctx, vtex, usage, level);
142   if (readback)
143      vws->transfer_get(vws, vtex->hw_res, box, trans->base.stride,
144                        trans->l_stride, trans->offset, level);
145
146   if (readback || flush)
147      vws->resource_wait(vws, vtex->hw_res);
148
149   trans->hw_res_map = vws->resource_map(vws, vtex->hw_res);
150   if (!trans->hw_res_map) {
151      virgl_resource_destroy_transfer(&vctx->transfer_pool, trans);
152      return NULL;
153   }
154
155   *transfer = &trans->base;
156   return trans->hw_res_map + trans->offset;
157}
158
159static void *texture_transfer_map_resolve(struct pipe_context *ctx,
160                                          struct pipe_resource *resource,
161                                          unsigned level,
162                                          unsigned usage,
163                                          const struct pipe_box *box,
164                                          struct pipe_transfer **transfer)
165{
166   struct virgl_context *vctx = virgl_context(ctx);
167   struct virgl_resource *vtex = virgl_resource(resource);
168   struct pipe_resource templ, *resolve_tmp;
169   struct virgl_transfer *trans;
170
171   trans = virgl_resource_create_transfer(&vctx->transfer_pool, resource,
172                                          &vtex->metadata, level, usage, box);
173   if (!trans)
174      return NULL;
175
176   enum pipe_format fmt = resource->format;
177   if (!virgl_has_readback_format(ctx->screen, fmt)) {
178      if (util_format_fits_8unorm(util_format_description(fmt)))
179         fmt = PIPE_FORMAT_R8G8B8A8_UNORM;
180      else if (util_format_is_pure_sint(fmt))
181         fmt = PIPE_FORMAT_R32G32B32A32_SINT;
182      else if (util_format_is_pure_uint(fmt))
183         fmt = PIPE_FORMAT_R32G32B32A32_UINT;
184      else
185         fmt = PIPE_FORMAT_R32G32B32A32_FLOAT;
186      assert(virgl_has_readback_format(ctx->screen, fmt));
187   }
188
189   virgl_init_temp_resource_from_box(&templ, resource, box, level, 0, fmt);
190
191   resolve_tmp = ctx->screen->resource_create(ctx->screen, &templ);
192   if (!resolve_tmp)
193      return NULL;
194
195   struct pipe_box dst_box = *box;
196   dst_box.x = dst_box.y = dst_box.z = 0;
197
198   if (usage & PIPE_TRANSFER_READ) {
199      virgl_copy_region_with_blit(ctx, resolve_tmp, 0, &dst_box, resource,
200                                  level, box);
201      ctx->flush(ctx, NULL, 0);
202   }
203
204   void *ptr = texture_transfer_map_plain(ctx, resolve_tmp, 0, usage, &dst_box,
205                                          &trans->resolve_transfer);
206   if (!ptr)
207      goto fail;
208
209   *transfer = &trans->base;
210   if (fmt == resource->format) {
211      trans->base.stride = trans->resolve_transfer->stride;
212      trans->base.layer_stride = trans->resolve_transfer->layer_stride;
213      return ptr;
214   } else {
215      if (usage & PIPE_TRANSFER_READ) {
216         struct virgl_winsys *vws = virgl_screen(ctx->screen)->vws;
217         void *src = ptr;
218         ptr = vws->resource_map(vws, vtex->hw_res);
219         if (!ptr)
220            goto fail;
221
222          if (!util_format_translate_3d(resource->format,
223                                       ptr,
224                                       trans->base.stride,
225                                       trans->base.layer_stride,
226                                       box->x, box->y, box->z,
227                                       fmt,
228                                       src,
229                                       trans->resolve_transfer->stride,
230                                       trans->resolve_transfer->layer_stride,
231                                       0, 0, 0,
232                                       box->width, box->height, box->depth)) {
233            debug_printf("failed to translate format %s to %s\n",
234                         util_format_short_name(fmt),
235                         util_format_short_name(resource->format));
236            goto fail;
237         }
238      }
239
240      if ((usage & PIPE_TRANSFER_WRITE) == 0)
241         pipe_resource_reference(&trans->resolve_transfer->resource, NULL);
242
243      return ptr + trans->offset;
244   }
245
246fail:
247   pipe_resource_reference(&resolve_tmp, NULL);
248   virgl_resource_destroy_transfer(&vctx->transfer_pool, trans);
249   return NULL;
250}
251
252static bool needs_resolve(struct pipe_screen *screen,
253                          struct pipe_resource *resource, unsigned usage)
254{
255   if (resource->nr_samples > 1)
256      return true;
257
258   if (usage & PIPE_TRANSFER_READ)
259      return !util_format_is_depth_or_stencil(resource->format) &&
260             !virgl_has_readback_format(screen, resource->format);
261
262   return false;
263}
264
265static void *virgl_texture_transfer_map(struct pipe_context *ctx,
266                                        struct pipe_resource *resource,
267                                        unsigned level,
268                                        unsigned usage,
269                                        const struct pipe_box *box,
270                                        struct pipe_transfer **transfer)
271{
272   if (needs_resolve(ctx->screen, resource, usage))
273      return texture_transfer_map_resolve(ctx, resource, level, usage, box,
274                                          transfer);
275
276   return texture_transfer_map_plain(ctx, resource, level, usage, box, transfer);
277}
278
279static void flush_data(struct pipe_context *ctx,
280                       struct virgl_transfer *trans,
281                       const struct pipe_box *box)
282{
283   struct virgl_winsys *vws = virgl_screen(ctx->screen)->vws;
284   vws->transfer_put(vws, virgl_resource(trans->base.resource)->hw_res, box,
285                     trans->base.stride, trans->l_stride, trans->offset,
286                     trans->base.level);
287}
288
289static void virgl_texture_transfer_unmap(struct pipe_context *ctx,
290                                         struct pipe_transfer *transfer)
291{
292   struct virgl_context *vctx = virgl_context(ctx);
293   struct virgl_transfer *trans = virgl_transfer(transfer);
294   bool queue_unmap = false;
295
296   if (transfer->usage & PIPE_TRANSFER_WRITE &&
297       (transfer->usage & PIPE_TRANSFER_FLUSH_EXPLICIT) == 0) {
298
299      if (trans->resolve_transfer && (trans->base.resource->format ==
300          trans->resolve_transfer->resource->format)) {
301         flush_data(ctx, virgl_transfer(trans->resolve_transfer),
302                    &trans->resolve_transfer->box);
303
304         /* FINISHME: In case the destination format isn't renderable here, the
305          * blit here will currently fail. This could for instance happen if the
306          * mapped resource is of a compressed format, and it's mapped with both
307          * read and write usage.
308          */
309
310         virgl_copy_region_with_blit(ctx,
311                                     trans->base.resource, trans->base.level,
312                                     &transfer->box,
313                                     trans->resolve_transfer->resource, 0,
314                                     &trans->resolve_transfer->box);
315         ctx->flush(ctx, NULL, 0);
316      } else
317         queue_unmap = true;
318   }
319
320   if (trans->resolve_transfer) {
321      pipe_resource_reference(&trans->resolve_transfer->resource, NULL);
322      virgl_resource_destroy_transfer(&vctx->transfer_pool,
323                                      virgl_transfer(trans->resolve_transfer));
324   }
325
326   if (queue_unmap)
327      virgl_transfer_queue_unmap(&vctx->queue, trans);
328   else
329      virgl_resource_destroy_transfer(&vctx->transfer_pool, trans);
330}
331
332static const struct u_resource_vtbl virgl_texture_vtbl =
333{
334   virgl_resource_get_handle,           /* get_handle */
335   virgl_resource_destroy,              /* resource_destroy */
336   virgl_texture_transfer_map,          /* transfer_map */
337   NULL,                                /* transfer_flush_region */
338   virgl_texture_transfer_unmap,        /* transfer_unmap */
339};
340
341void virgl_texture_init(struct virgl_resource *res)
342{
343   res->u.vtbl = &virgl_texture_vtbl;
344}
345