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  */
53 static boolean
softpipe_resource_layout(struct pipe_screen * screen,struct softpipe_resource * spr)54 softpipe_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  */
95 static boolean
softpipe_displaytarget_layout(struct pipe_screen * screen,struct softpipe_resource * spr)96 softpipe_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  */
118 static struct pipe_resource *
softpipe_resource_create(struct pipe_screen * screen,const struct pipe_resource * templat)119 softpipe_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 
155 static void
softpipe_resource_destroy(struct pipe_screen * pscreen,struct pipe_resource * pt)156 softpipe_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 
176 static struct pipe_resource *
softpipe_resource_from_handle(struct pipe_screen * screen,const struct pipe_resource * templat,struct winsys_handle * whandle)177 softpipe_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 
209 static boolean
softpipe_resource_get_handle(struct pipe_screen * screen,struct pipe_resource * pt,struct winsys_handle * whandle)210 softpipe_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  */
229 static unsigned
sp_get_tex_image_offset(const struct softpipe_resource * spr,unsigned level,unsigned layer)230 sp_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  */
256 static struct pipe_surface *
softpipe_create_surface(struct pipe_context * pipe,struct pipe_resource * pt,const struct pipe_surface * surf_tmpl)257 softpipe_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  */
288 static void
softpipe_surface_destroy(struct pipe_context * pipe,struct pipe_surface * surf)289 softpipe_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  */
311 static struct pipe_transfer *
softpipe_get_transfer(struct pipe_context * pipe,struct pipe_resource * resource,unsigned level,unsigned usage,const struct pipe_box * box)312 softpipe_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  */
393 static void
softpipe_transfer_destroy(struct pipe_context * pipe,struct pipe_transfer * transfer)394 softpipe_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  */
405 static void *
softpipe_transfer_map(struct pipe_context * pipe,struct pipe_transfer * transfer)406 softpipe_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  */
433 static void
softpipe_transfer_unmap(struct pipe_context * pipe,struct pipe_transfer * transfer)434 softpipe_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  */
457 struct pipe_resource *
softpipe_user_buffer_create(struct pipe_screen * screen,void * ptr,unsigned bytes,unsigned bind_flags)458 softpipe_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 
486 void
softpipe_init_texture_funcs(struct pipe_context * pipe)487 softpipe_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 
502 void
softpipe_init_screen_texture_funcs(struct pipe_screen * screen)503 softpipe_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