1/**************************************************************************
2 *
3 * Copyright 2006 VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27 /*
28  * Authors:
29  *   Keith Whitwell <keithw@vmware.com>
30  *   Michel Dänzer <daenzer@vmware.com>
31  */
32
33#include "pipe/p_defines.h"
34#include "util/u_inlines.h"
35
36#include "util/format/u_format.h"
37#include "util/u_math.h"
38#include "util/u_memory.h"
39#include "util/u_transfer.h"
40#include "util/u_surface.h"
41
42#include "sp_context.h"
43#include "sp_flush.h"
44#include "sp_texture.h"
45#include "sp_screen.h"
46
47#include "frontend/sw_winsys.h"
48
49
50/**
51 * Conventional allocation path for non-display textures:
52 * Use a simple, maximally packed layout.
53 */
54static boolean
55softpipe_resource_layout(struct pipe_screen *screen,
56                         struct softpipe_resource *spr,
57                         boolean allocate)
58{
59   struct pipe_resource *pt = &spr->base;
60   unsigned level;
61   unsigned width = pt->width0;
62   unsigned height = pt->height0;
63   unsigned depth = pt->depth0;
64   uint64_t buffer_size = 0;
65
66   for (level = 0; level <= pt->last_level; level++) {
67      unsigned slices, nblocksy;
68
69      nblocksy = util_format_get_nblocksy(pt->format, height);
70
71      if (pt->target == PIPE_TEXTURE_CUBE)
72         assert(pt->array_size == 6);
73
74      if (pt->target == PIPE_TEXTURE_3D)
75         slices = depth;
76      else
77         slices = pt->array_size;
78
79      spr->stride[level] = util_format_get_stride(pt->format, width);
80
81      spr->level_offset[level] = buffer_size;
82
83      /* if row_stride * height > SP_MAX_TEXTURE_SIZE */
84      if ((uint64_t)spr->stride[level] * nblocksy > SP_MAX_TEXTURE_SIZE) {
85         /* image too large */
86         return FALSE;
87      }
88
89      spr->img_stride[level] = spr->stride[level] * nblocksy;
90
91      buffer_size += (uint64_t) spr->img_stride[level] * slices;
92
93      width  = u_minify(width, 1);
94      height = u_minify(height, 1);
95      depth = u_minify(depth, 1);
96   }
97
98   if (buffer_size > SP_MAX_TEXTURE_SIZE)
99      return FALSE;
100
101   if (allocate) {
102      spr->data = align_malloc(buffer_size, 64);
103      return spr->data != NULL;
104   }
105   else {
106      return TRUE;
107   }
108}
109
110
111/**
112 * Check the size of the texture specified by 'res'.
113 * \return TRUE if OK, FALSE if too large.
114 */
115static bool
116softpipe_can_create_resource(struct pipe_screen *screen,
117                             const struct pipe_resource *res)
118{
119   struct softpipe_resource spr;
120   memset(&spr, 0, sizeof(spr));
121   spr.base = *res;
122   return softpipe_resource_layout(screen, &spr, FALSE);
123}
124
125
126/**
127 * Texture layout for simple color buffers.
128 */
129static boolean
130softpipe_displaytarget_layout(struct pipe_screen *screen,
131                              struct softpipe_resource *spr,
132                              const void *map_front_private)
133{
134   struct sw_winsys *winsys = softpipe_screen(screen)->winsys;
135
136   /* Round up the surface size to a multiple of the tile size?
137    */
138   spr->dt = winsys->displaytarget_create(winsys,
139                                          spr->base.bind,
140                                          spr->base.format,
141                                          spr->base.width0,
142                                          spr->base.height0,
143                                          64,
144                                          map_front_private,
145                                          &spr->stride[0] );
146
147   return spr->dt != NULL;
148}
149
150
151/**
152 * Create new pipe_resource given the template information.
153 */
154static struct pipe_resource *
155softpipe_resource_create_front(struct pipe_screen *screen,
156                               const struct pipe_resource *templat,
157                               const void *map_front_private)
158{
159   struct softpipe_resource *spr = CALLOC_STRUCT(softpipe_resource);
160   if (!spr)
161      return NULL;
162
163   assert(templat->format != PIPE_FORMAT_NONE);
164
165   spr->base = *templat;
166   pipe_reference_init(&spr->base.reference, 1);
167   spr->base.screen = screen;
168
169   spr->pot = (util_is_power_of_two_or_zero(templat->width0) &&
170               util_is_power_of_two_or_zero(templat->height0) &&
171               util_is_power_of_two_or_zero(templat->depth0));
172
173   if (spr->base.bind & (PIPE_BIND_DISPLAY_TARGET |
174			 PIPE_BIND_SCANOUT |
175			 PIPE_BIND_SHARED)) {
176      if (!softpipe_displaytarget_layout(screen, spr, map_front_private))
177         goto fail;
178   }
179   else {
180      if (!softpipe_resource_layout(screen, spr, TRUE))
181         goto fail;
182   }
183
184   return &spr->base;
185
186 fail:
187   FREE(spr);
188   return NULL;
189}
190
191static struct pipe_resource *
192softpipe_resource_create(struct pipe_screen *screen,
193                         const struct pipe_resource *templat)
194{
195   return softpipe_resource_create_front(screen, templat, NULL);
196}
197
198static void
199softpipe_resource_destroy(struct pipe_screen *pscreen,
200			  struct pipe_resource *pt)
201{
202   struct softpipe_screen *screen = softpipe_screen(pscreen);
203   struct softpipe_resource *spr = softpipe_resource(pt);
204
205   if (spr->dt) {
206      /* display target */
207      struct sw_winsys *winsys = screen->winsys;
208      winsys->displaytarget_destroy(winsys, spr->dt);
209   }
210   else if (!spr->userBuffer) {
211      /* regular texture */
212      align_free(spr->data);
213   }
214
215   FREE(spr);
216}
217
218
219static struct pipe_resource *
220softpipe_resource_from_handle(struct pipe_screen *screen,
221                              const struct pipe_resource *templat,
222                              struct winsys_handle *whandle,
223                              unsigned usage)
224{
225   struct sw_winsys *winsys = softpipe_screen(screen)->winsys;
226   struct softpipe_resource *spr = CALLOC_STRUCT(softpipe_resource);
227   if (!spr)
228      return NULL;
229
230   spr->base = *templat;
231   pipe_reference_init(&spr->base.reference, 1);
232   spr->base.screen = screen;
233
234   spr->pot = (util_is_power_of_two_or_zero(templat->width0) &&
235               util_is_power_of_two_or_zero(templat->height0) &&
236               util_is_power_of_two_or_zero(templat->depth0));
237
238   spr->dt = winsys->displaytarget_from_handle(winsys,
239                                               templat,
240                                               whandle,
241                                               &spr->stride[0]);
242   if (!spr->dt)
243      goto fail;
244
245   return &spr->base;
246
247 fail:
248   FREE(spr);
249   return NULL;
250}
251
252
253static bool
254softpipe_resource_get_handle(struct pipe_screen *screen,
255                             struct pipe_context *ctx,
256                             struct pipe_resource *pt,
257                             struct winsys_handle *whandle,
258                             unsigned usage)
259{
260   struct sw_winsys *winsys = softpipe_screen(screen)->winsys;
261   struct softpipe_resource *spr = softpipe_resource(pt);
262
263   assert(spr->dt);
264   if (!spr->dt)
265      return false;
266
267   return winsys->displaytarget_get_handle(winsys, spr->dt, whandle);
268}
269
270
271/**
272 * Helper function to compute offset (in bytes) for a particular
273 * texture level/face/slice from the start of the buffer.
274 */
275unsigned
276softpipe_get_tex_image_offset(const struct softpipe_resource *spr,
277                              unsigned level, unsigned layer)
278{
279   unsigned offset = spr->level_offset[level];
280
281   offset += layer * spr->img_stride[level];
282
283   return offset;
284}
285
286
287/**
288 * Get a pipe_surface "view" into a texture resource.
289 */
290static struct pipe_surface *
291softpipe_create_surface(struct pipe_context *pipe,
292                        struct pipe_resource *pt,
293                        const struct pipe_surface *surf_tmpl)
294{
295   struct pipe_surface *ps;
296
297   ps = CALLOC_STRUCT(pipe_surface);
298   if (ps) {
299      pipe_reference_init(&ps->reference, 1);
300      pipe_resource_reference(&ps->texture, pt);
301      ps->context = pipe;
302      ps->format = surf_tmpl->format;
303      if (pt->target != PIPE_BUFFER) {
304         assert(surf_tmpl->u.tex.level <= pt->last_level);
305         ps->width = u_minify(pt->width0, surf_tmpl->u.tex.level);
306         ps->height = u_minify(pt->height0, surf_tmpl->u.tex.level);
307         ps->u.tex.level = surf_tmpl->u.tex.level;
308         ps->u.tex.first_layer = surf_tmpl->u.tex.first_layer;
309         ps->u.tex.last_layer = surf_tmpl->u.tex.last_layer;
310         if (ps->u.tex.first_layer != ps->u.tex.last_layer) {
311            debug_printf("creating surface with multiple layers, rendering to first layer only\n");
312         }
313      }
314      else {
315         /* setting width as number of elements should get us correct renderbuffer width */
316         ps->width = surf_tmpl->u.buf.last_element - surf_tmpl->u.buf.first_element + 1;
317         ps->height = pt->height0;
318         ps->u.buf.first_element = surf_tmpl->u.buf.first_element;
319         ps->u.buf.last_element = surf_tmpl->u.buf.last_element;
320         assert(ps->u.buf.first_element <= ps->u.buf.last_element);
321         assert(ps->u.buf.last_element < ps->width);
322      }
323   }
324   return ps;
325}
326
327
328/**
329 * Free a pipe_surface which was created with softpipe_create_surface().
330 */
331static void
332softpipe_surface_destroy(struct pipe_context *pipe,
333                         struct pipe_surface *surf)
334{
335   /* Effectively do the texture_update work here - if texture images
336    * needed post-processing to put them into hardware layout, this is
337    * where it would happen.  For softpipe, nothing to do.
338    */
339   assert(surf->texture);
340   pipe_resource_reference(&surf->texture, NULL);
341   FREE(surf);
342}
343
344
345/**
346 * Geta pipe_transfer object which is used for moving data in/out of
347 * a resource object.
348 * \param pipe  rendering context
349 * \param resource  the resource to transfer in/out of
350 * \param level  which mipmap level
351 * \param usage  bitmask of PIPE_MAP_x flags
352 * \param box  the 1D/2D/3D region of interest
353 */
354static void *
355softpipe_transfer_map(struct pipe_context *pipe,
356                      struct pipe_resource *resource,
357                      unsigned level,
358                      unsigned usage,
359                      const struct pipe_box *box,
360                      struct pipe_transfer **transfer)
361{
362   struct sw_winsys *winsys = softpipe_screen(pipe->screen)->winsys;
363   struct softpipe_resource *spr = softpipe_resource(resource);
364   struct softpipe_transfer *spt;
365   struct pipe_transfer *pt;
366   enum pipe_format format = resource->format;
367   uint8_t *map;
368
369   assert(resource);
370   assert(level <= resource->last_level);
371
372   /* make sure the requested region is in the image bounds */
373   assert(box->x + box->width <= (int) u_minify(resource->width0, level));
374   if (resource->target == PIPE_TEXTURE_1D_ARRAY) {
375      assert(box->y + box->height <= (int) resource->array_size);
376   }
377   else {
378      assert(box->y + box->height <= (int) u_minify(resource->height0, level));
379      if (resource->target == PIPE_TEXTURE_2D_ARRAY) {
380         assert(box->z + box->depth <= (int) resource->array_size);
381      }
382      else if (resource->target == PIPE_TEXTURE_CUBE) {
383         assert(box->z < 6);
384      }
385      else if (resource->target == PIPE_TEXTURE_CUBE_ARRAY) {
386         assert(box->z <= (int) resource->array_size);
387      }
388      else {
389         assert(box->z + box->depth <= (int) u_minify(resource->depth0, level));
390      }
391   }
392
393   /*
394    * Transfers, like other pipe operations, must happen in order, so flush the
395    * context if necessary.
396    */
397   if (!(usage & PIPE_MAP_UNSYNCHRONIZED)) {
398      boolean read_only = !(usage & PIPE_MAP_WRITE);
399      boolean do_not_block = !!(usage & PIPE_MAP_DONTBLOCK);
400      if (!softpipe_flush_resource(pipe, resource,
401                                   level, box->depth > 1 ? -1 : box->z,
402                                   0, /* flush_flags */
403                                   read_only,
404                                   TRUE, /* cpu_access */
405                                   do_not_block)) {
406         /*
407          * It would have blocked, but state tracker requested no to.
408          */
409         assert(do_not_block);
410         return NULL;
411      }
412   }
413
414   spt = CALLOC_STRUCT(softpipe_transfer);
415   if (!spt)
416      return NULL;
417
418   pt = &spt->base;
419
420   pipe_resource_reference(&pt->resource, resource);
421   pt->level = level;
422   pt->usage = usage;
423   pt->box = *box;
424   pt->stride = spr->stride[level];
425   pt->layer_stride = spr->img_stride[level];
426
427   spt->offset = softpipe_get_tex_image_offset(spr, level, box->z);
428
429   spt->offset +=
430         box->y / util_format_get_blockheight(format) * spt->base.stride +
431         box->x / util_format_get_blockwidth(format) * util_format_get_blocksize(format);
432
433   /* resources backed by display target treated specially:
434    */
435   if (spr->dt) {
436      map = winsys->displaytarget_map(winsys, spr->dt, usage);
437   }
438   else {
439      map = spr->data;
440   }
441
442   if (!map) {
443      pipe_resource_reference(&pt->resource, NULL);
444      FREE(spt);
445      return NULL;
446   }
447
448   *transfer = pt;
449   return map + spt->offset;
450}
451
452
453/**
454 * Unmap memory mapping for given pipe_transfer object.
455 */
456static void
457softpipe_transfer_unmap(struct pipe_context *pipe,
458                        struct pipe_transfer *transfer)
459{
460   struct softpipe_resource *spr;
461
462   assert(transfer->resource);
463   spr = softpipe_resource(transfer->resource);
464
465   if (spr->dt) {
466      /* display target */
467      struct sw_winsys *winsys = softpipe_screen(pipe->screen)->winsys;
468      winsys->displaytarget_unmap(winsys, spr->dt);
469   }
470
471   if (transfer->usage & PIPE_MAP_WRITE) {
472      /* Mark the texture as dirty to expire the tile caches. */
473      spr->timestamp++;
474   }
475
476   pipe_resource_reference(&transfer->resource, NULL);
477   FREE(transfer);
478}
479
480/**
481 * Create buffer which wraps user-space data.
482 */
483struct pipe_resource *
484softpipe_user_buffer_create(struct pipe_screen *screen,
485                            void *ptr,
486                            unsigned bytes,
487			    unsigned bind_flags)
488{
489   struct softpipe_resource *spr;
490
491   spr = CALLOC_STRUCT(softpipe_resource);
492   if (!spr)
493      return NULL;
494
495   pipe_reference_init(&spr->base.reference, 1);
496   spr->base.screen = screen;
497   spr->base.format = PIPE_FORMAT_R8_UNORM; /* ?? */
498   spr->base.bind = bind_flags;
499   spr->base.usage = PIPE_USAGE_IMMUTABLE;
500   spr->base.flags = 0;
501   spr->base.width0 = bytes;
502   spr->base.height0 = 1;
503   spr->base.depth0 = 1;
504   spr->base.array_size = 1;
505   spr->userBuffer = TRUE;
506   spr->data = ptr;
507
508   return &spr->base;
509}
510
511
512void
513softpipe_init_texture_funcs(struct pipe_context *pipe)
514{
515   pipe->buffer_map = softpipe_transfer_map;
516   pipe->buffer_unmap = softpipe_transfer_unmap;
517   pipe->texture_map = softpipe_transfer_map;
518   pipe->texture_unmap = softpipe_transfer_unmap;
519
520   pipe->transfer_flush_region = u_default_transfer_flush_region;
521   pipe->buffer_subdata = u_default_buffer_subdata;
522   pipe->texture_subdata = u_default_texture_subdata;
523
524   pipe->create_surface = softpipe_create_surface;
525   pipe->surface_destroy = softpipe_surface_destroy;
526   pipe->clear_texture = util_clear_texture;
527}
528
529
530void
531softpipe_init_screen_texture_funcs(struct pipe_screen *screen)
532{
533   screen->resource_create = softpipe_resource_create;
534   screen->resource_create_front = softpipe_resource_create_front;
535   screen->resource_destroy = softpipe_resource_destroy;
536   screen->resource_from_handle = softpipe_resource_from_handle;
537   screen->resource_get_handle = softpipe_resource_get_handle;
538   screen->can_create_resource = softpipe_can_create_resource;
539}
540