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