1/**************************************************************************
2 *
3 * Copyright 2006 Tungsten Graphics, Inc., Cedar Park, Texas.
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 TUNGSTEN GRAPHICS 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 <keith@tungstengraphics.com>
30  *   Michel Dänzer <michel@tungstengraphics.com>
31  */
32
33#include "pipe/p_defines.h"
34#include "util/u_inlines.h"
35
36#include "util/u_format.h"
37#include "util/u_math.h"
38#include "util/u_memory.h"
39#include "util/u_transfer.h"
40
41#include "sp_context.h"
42#include "sp_flush.h"
43#include "sp_texture.h"
44#include "sp_screen.h"
45
46#include "state_tracker/sw_winsys.h"
47
48
49/**
50 * Conventional allocation path for non-display textures:
51 * Use a simple, maximally packed layout.
52 */
53static boolean
54softpipe_resource_layout(struct pipe_screen *screen,
55                         struct softpipe_resource *spr)
56{
57   struct pipe_resource *pt = &spr->base;
58   unsigned level;
59   unsigned width = pt->width0;
60   unsigned height = pt->height0;
61   unsigned depth = pt->depth0;
62   unsigned buffer_size = 0;
63
64   for (level = 0; level <= pt->last_level; level++) {
65      unsigned slices;
66
67      if (pt->target == PIPE_TEXTURE_CUBE)
68         slices = 6;
69      else if (pt->target == PIPE_TEXTURE_3D)
70         slices = depth;
71      else
72         slices = pt->array_size;
73
74      spr->stride[level] = util_format_get_stride(pt->format, width);
75
76      spr->level_offset[level] = buffer_size;
77
78      buffer_size += (util_format_get_nblocksy(pt->format, height) *
79                      slices * spr->stride[level]);
80
81      width  = u_minify(width, 1);
82      height = u_minify(height, 1);
83      depth = u_minify(depth, 1);
84   }
85
86   spr->data = align_malloc(buffer_size, 16);
87
88   return spr->data != NULL;
89}
90
91
92/**
93 * Texture layout for simple color buffers.
94 */
95static boolean
96softpipe_displaytarget_layout(struct pipe_screen *screen,
97                              struct softpipe_resource *spr)
98{
99   struct sw_winsys *winsys = softpipe_screen(screen)->winsys;
100
101   /* Round up the surface size to a multiple of the tile size?
102    */
103   spr->dt = winsys->displaytarget_create(winsys,
104                                          spr->base.bind,
105                                          spr->base.format,
106                                          spr->base.width0,
107                                          spr->base.height0,
108                                          16,
109                                          &spr->stride[0] );
110
111   return spr->dt != NULL;
112}
113
114
115/**
116 * Create new pipe_resource given the template information.
117 */
118static struct pipe_resource *
119softpipe_resource_create(struct pipe_screen *screen,
120                         const struct pipe_resource *templat)
121{
122   struct softpipe_resource *spr = CALLOC_STRUCT(softpipe_resource);
123   if (!spr)
124      return NULL;
125
126   assert(templat->format != PIPE_FORMAT_NONE);
127
128   spr->base = *templat;
129   pipe_reference_init(&spr->base.reference, 1);
130   spr->base.screen = screen;
131
132   spr->pot = (util_is_power_of_two(templat->width0) &&
133               util_is_power_of_two(templat->height0) &&
134               util_is_power_of_two(templat->depth0));
135
136   if (spr->base.bind & (PIPE_BIND_DISPLAY_TARGET |
137			 PIPE_BIND_SCANOUT |
138			 PIPE_BIND_SHARED)) {
139      if (!softpipe_displaytarget_layout(screen, spr))
140         goto fail;
141   }
142   else {
143      if (!softpipe_resource_layout(screen, spr))
144         goto fail;
145   }
146
147   return &spr->base;
148
149 fail:
150   FREE(spr);
151   return NULL;
152}
153
154
155static void
156softpipe_resource_destroy(struct pipe_screen *pscreen,
157			  struct pipe_resource *pt)
158{
159   struct softpipe_screen *screen = softpipe_screen(pscreen);
160   struct softpipe_resource *spr = softpipe_resource(pt);
161
162   if (spr->dt) {
163      /* display target */
164      struct sw_winsys *winsys = screen->winsys;
165      winsys->displaytarget_destroy(winsys, spr->dt);
166   }
167   else if (!spr->userBuffer) {
168      /* regular texture */
169      align_free(spr->data);
170   }
171
172   FREE(spr);
173}
174
175
176static struct pipe_resource *
177softpipe_resource_from_handle(struct pipe_screen *screen,
178                              const struct pipe_resource *templat,
179                              struct winsys_handle *whandle)
180{
181   struct sw_winsys *winsys = softpipe_screen(screen)->winsys;
182   struct softpipe_resource *spr = CALLOC_STRUCT(softpipe_resource);
183   if (!spr)
184      return NULL;
185
186   spr->base = *templat;
187   pipe_reference_init(&spr->base.reference, 1);
188   spr->base.screen = screen;
189
190   spr->pot = (util_is_power_of_two(templat->width0) &&
191               util_is_power_of_two(templat->height0) &&
192               util_is_power_of_two(templat->depth0));
193
194   spr->dt = winsys->displaytarget_from_handle(winsys,
195                                               templat,
196                                               whandle,
197                                               &spr->stride[0]);
198   if (!spr->dt)
199      goto fail;
200
201   return &spr->base;
202
203 fail:
204   FREE(spr);
205   return NULL;
206}
207
208
209static boolean
210softpipe_resource_get_handle(struct pipe_screen *screen,
211                             struct pipe_resource *pt,
212                             struct winsys_handle *whandle)
213{
214   struct sw_winsys *winsys = softpipe_screen(screen)->winsys;
215   struct softpipe_resource *spr = softpipe_resource(pt);
216
217   assert(spr->dt);
218   if (!spr->dt)
219      return FALSE;
220
221   return winsys->displaytarget_get_handle(winsys, spr->dt, whandle);
222}
223
224
225/**
226 * Helper function to compute offset (in bytes) for a particular
227 * texture level/face/slice from the start of the buffer.
228 */
229static unsigned
230sp_get_tex_image_offset(const struct softpipe_resource *spr,
231                        unsigned level, unsigned layer)
232{
233   const unsigned hgt = u_minify(spr->base.height0, level);
234   const unsigned nblocksy = util_format_get_nblocksy(spr->base.format, hgt);
235   unsigned offset = spr->level_offset[level];
236
237   if (spr->base.target == PIPE_TEXTURE_CUBE ||
238       spr->base.target == PIPE_TEXTURE_3D ||
239       spr->base.target == PIPE_TEXTURE_2D_ARRAY) {
240      offset += layer * nblocksy * spr->stride[level];
241   }
242   else if (spr->base.target == PIPE_TEXTURE_1D_ARRAY) {
243      offset += layer * spr->stride[level];
244   }
245   else {
246      assert(layer == 0);
247   }
248
249   return offset;
250}
251
252
253/**
254 * Get a pipe_surface "view" into a texture resource.
255 */
256static struct pipe_surface *
257softpipe_create_surface(struct pipe_context *pipe,
258                        struct pipe_resource *pt,
259                        const struct pipe_surface *surf_tmpl)
260{
261   struct pipe_surface *ps;
262   unsigned level = surf_tmpl->u.tex.level;
263
264   assert(level <= pt->last_level);
265   assert(surf_tmpl->u.tex.first_layer == surf_tmpl->u.tex.last_layer);
266
267   ps = CALLOC_STRUCT(pipe_surface);
268   if (ps) {
269      pipe_reference_init(&ps->reference, 1);
270      pipe_resource_reference(&ps->texture, pt);
271      ps->context = pipe;
272      ps->format = surf_tmpl->format;
273      ps->width = u_minify(pt->width0, level);
274      ps->height = u_minify(pt->height0, level);
275      ps->usage = surf_tmpl->usage;
276
277      ps->u.tex.level = level;
278      ps->u.tex.first_layer = surf_tmpl->u.tex.first_layer;
279      ps->u.tex.last_layer = surf_tmpl->u.tex.last_layer;
280   }
281   return ps;
282}
283
284
285/**
286 * Free a pipe_surface which was created with softpipe_create_surface().
287 */
288static void
289softpipe_surface_destroy(struct pipe_context *pipe,
290                         struct pipe_surface *surf)
291{
292   /* Effectively do the texture_update work here - if texture images
293    * needed post-processing to put them into hardware layout, this is
294    * where it would happen.  For softpipe, nothing to do.
295    */
296   assert(surf->texture);
297   pipe_resource_reference(&surf->texture, NULL);
298   FREE(surf);
299}
300
301
302/**
303 * Geta pipe_transfer object which is used for moving data in/out of
304 * a resource object.
305 * \param pipe  rendering context
306 * \param resource  the resource to transfer in/out of
307 * \param level  which mipmap level
308 * \param usage  bitmask of PIPE_TRANSFER_x flags
309 * \param box  the 1D/2D/3D region of interest
310 */
311static struct pipe_transfer *
312softpipe_get_transfer(struct pipe_context *pipe,
313                      struct pipe_resource *resource,
314                      unsigned level,
315                      unsigned usage,
316                      const struct pipe_box *box)
317{
318   struct softpipe_resource *spr = softpipe_resource(resource);
319   struct softpipe_transfer *spt;
320
321   assert(resource);
322   assert(level <= resource->last_level);
323
324   /* make sure the requested region is in the image bounds */
325   assert(box->x + box->width <= u_minify(resource->width0, level));
326   if (resource->target == PIPE_TEXTURE_1D_ARRAY) {
327      assert(box->y + box->height <= resource->array_size);
328   }
329   else {
330      assert(box->y + box->height <= u_minify(resource->height0, level));
331      if (resource->target == PIPE_TEXTURE_2D_ARRAY) {
332         assert(box->z + box->depth <= resource->array_size);
333      }
334      else if (resource->target == PIPE_TEXTURE_CUBE) {
335         assert(box->z < 6);
336      }
337      else {
338         assert(box->z + box->depth <= (u_minify(resource->depth0, level)));
339      }
340   }
341
342   /*
343    * Transfers, like other pipe operations, must happen in order, so flush the
344    * context if necessary.
345    */
346   if (!(usage & PIPE_TRANSFER_UNSYNCHRONIZED)) {
347      boolean read_only = !(usage & PIPE_TRANSFER_WRITE);
348      boolean do_not_block = !!(usage & PIPE_TRANSFER_DONTBLOCK);
349      if (!softpipe_flush_resource(pipe, resource,
350                                   level, box->depth > 1 ? -1 : box->z,
351                                   0, /* flush_flags */
352                                   read_only,
353                                   TRUE, /* cpu_access */
354                                   do_not_block)) {
355         /*
356          * It would have blocked, but state tracker requested no to.
357          */
358         assert(do_not_block);
359         return NULL;
360      }
361   }
362
363   spt = CALLOC_STRUCT(softpipe_transfer);
364   if (spt) {
365      struct pipe_transfer *pt = &spt->base;
366      enum pipe_format format = resource->format;
367      const unsigned hgt = u_minify(spr->base.height0, level);
368      const unsigned nblocksy = util_format_get_nblocksy(format, hgt);
369
370      pipe_resource_reference(&pt->resource, resource);
371      pt->level = level;
372      pt->usage = usage;
373      pt->box = *box;
374      pt->stride = spr->stride[level];
375      pt->layer_stride = pt->stride * nblocksy;
376
377      spt->offset = sp_get_tex_image_offset(spr, level, box->z);
378
379      spt->offset +=
380         box->y / util_format_get_blockheight(format) * spt->base.stride +
381         box->x / util_format_get_blockwidth(format) * util_format_get_blocksize(format);
382
383      return pt;
384   }
385   return NULL;
386}
387
388
389/**
390 * Free a pipe_transfer object which was created with
391 * softpipe_get_transfer().
392 */
393static void
394softpipe_transfer_destroy(struct pipe_context *pipe,
395                          struct pipe_transfer *transfer)
396{
397   pipe_resource_reference(&transfer->resource, NULL);
398   FREE(transfer);
399}
400
401
402/**
403 * Create memory mapping for given pipe_transfer object.
404 */
405static void *
406softpipe_transfer_map(struct pipe_context *pipe,
407                      struct pipe_transfer *transfer)
408{
409   struct softpipe_transfer *spt = softpipe_transfer(transfer);
410   struct softpipe_resource *spr = softpipe_resource(transfer->resource);
411   struct sw_winsys *winsys = softpipe_screen(pipe->screen)->winsys;
412   uint8_t *map;
413
414   /* resources backed by display target treated specially:
415    */
416   if (spr->dt) {
417      map = winsys->displaytarget_map(winsys, spr->dt, transfer->usage);
418   }
419   else {
420      map = spr->data;
421   }
422
423   if (map == NULL)
424      return NULL;
425   else
426      return map + spt->offset;
427}
428
429
430/**
431 * Unmap memory mapping for given pipe_transfer object.
432 */
433static void
434softpipe_transfer_unmap(struct pipe_context *pipe,
435                        struct pipe_transfer *transfer)
436{
437   struct softpipe_resource *spr;
438
439   assert(transfer->resource);
440   spr = softpipe_resource(transfer->resource);
441
442   if (spr->dt) {
443      /* display target */
444      struct sw_winsys *winsys = softpipe_screen(pipe->screen)->winsys;
445      winsys->displaytarget_unmap(winsys, spr->dt);
446   }
447
448   if (transfer->usage & PIPE_TRANSFER_WRITE) {
449      /* Mark the texture as dirty to expire the tile caches. */
450      spr->timestamp++;
451   }
452}
453
454/**
455 * Create buffer which wraps user-space data.
456 */
457struct pipe_resource *
458softpipe_user_buffer_create(struct pipe_screen *screen,
459                            void *ptr,
460                            unsigned bytes,
461			    unsigned bind_flags)
462{
463   struct softpipe_resource *spr;
464
465   spr = CALLOC_STRUCT(softpipe_resource);
466   if (!spr)
467      return NULL;
468
469   pipe_reference_init(&spr->base.reference, 1);
470   spr->base.screen = screen;
471   spr->base.format = PIPE_FORMAT_R8_UNORM; /* ?? */
472   spr->base.bind = bind_flags;
473   spr->base.usage = PIPE_USAGE_IMMUTABLE;
474   spr->base.flags = 0;
475   spr->base.width0 = bytes;
476   spr->base.height0 = 1;
477   spr->base.depth0 = 1;
478   spr->base.array_size = 1;
479   spr->userBuffer = TRUE;
480   spr->data = ptr;
481
482   return &spr->base;
483}
484
485
486void
487softpipe_init_texture_funcs(struct pipe_context *pipe)
488{
489   pipe->get_transfer = softpipe_get_transfer;
490   pipe->transfer_destroy = softpipe_transfer_destroy;
491   pipe->transfer_map = softpipe_transfer_map;
492   pipe->transfer_unmap = softpipe_transfer_unmap;
493
494   pipe->transfer_flush_region = u_default_transfer_flush_region;
495   pipe->transfer_inline_write = u_default_transfer_inline_write;
496
497   pipe->create_surface = softpipe_create_surface;
498   pipe->surface_destroy = softpipe_surface_destroy;
499}
500
501
502void
503softpipe_init_screen_texture_funcs(struct pipe_screen *screen)
504{
505   screen->resource_create = softpipe_resource_create;
506   screen->resource_destroy = softpipe_resource_destroy;
507   screen->resource_from_handle = softpipe_resource_from_handle;
508   screen->resource_get_handle = softpipe_resource_get_handle;
509}
510