i915: Bug #14313: Fix accelerated (PBO) ReadPixels.
[mesa.git] / src / mesa / drivers / dri / intel / intel_regions.c
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 /* Provide additional functionality on top of bufmgr buffers:
29 * - 2d semantics and blit operations
30 * - refcounting of buffers for multiple images in a buffer.
31 * - refcounting of buffer mappings.
32 * - some logic for moving the buffers to the best memory pools for
33 * given operations.
34 *
35 * Most of this is to make it easier to implement the fixed-layout
36 * mipmap tree required by intel hardware in the face of GL's
37 * programming interface where each image can be specifed in random
38 * order and it isn't clear what layout the tree should have until the
39 * last moment.
40 */
41
42 #include "intel_context.h"
43 #include "intel_regions.h"
44 #include "intel_blit.h"
45 #include "intel_buffer_objects.h"
46 #include "dri_bufmgr.h"
47 #include "intel_bufmgr_ttm.h"
48 #include "intel_batchbuffer.h"
49
50 #define FILE_DEBUG_FLAG DEBUG_REGION
51
52 /* XXX: Thread safety?
53 */
54 GLubyte *
55 intel_region_map(struct intel_context *intel, struct intel_region *region)
56 {
57 DBG("%s\n", __FUNCTION__);
58 if (!region->map_refcount++) {
59 if (region->pbo)
60 intel_region_cow(intel, region);
61
62 dri_bo_map(region->buffer, GL_TRUE);
63 region->map = region->buffer->virtual;
64 }
65
66 return region->map;
67 }
68
69 void
70 intel_region_unmap(struct intel_context *intel, struct intel_region *region)
71 {
72 DBG("%s\n", __FUNCTION__);
73 if (!--region->map_refcount) {
74 dri_bo_unmap(region->buffer);
75 region->map = NULL;
76 }
77 }
78
79 static struct intel_region *
80 intel_region_alloc_internal(struct intel_context *intel,
81 GLuint cpp, GLuint pitch, GLuint height,
82 GLuint tiled, dri_bo *buffer)
83 {
84 struct intel_region *region;
85
86 DBG("%s\n", __FUNCTION__);
87
88 if (buffer == NULL)
89 return NULL;
90
91 region = calloc(sizeof(*region), 1);
92 region->cpp = cpp;
93 region->pitch = pitch;
94 region->height = height; /* needed? */
95 region->refcount = 1;
96 region->tiled = tiled;
97 region->buffer = buffer;
98
99 return region;
100 }
101
102 struct intel_region *
103 intel_region_alloc(struct intel_context *intel,
104 GLuint cpp, GLuint pitch, GLuint height)
105 {
106 dri_bo *buffer;
107
108 buffer = dri_bo_alloc(intel->bufmgr, "region",
109 pitch * cpp * height, 64,
110 DRM_BO_FLAG_MEM_LOCAL |
111 DRM_BO_FLAG_CACHED |
112 DRM_BO_FLAG_CACHED_MAPPED);
113
114 return intel_region_alloc_internal(intel, cpp, pitch, height, 0, buffer);
115 }
116
117 struct intel_region *
118 intel_region_alloc_for_handle(struct intel_context *intel,
119 GLuint cpp, GLuint pitch, GLuint height,
120 GLuint tiled, GLuint handle)
121 {
122 dri_bo *buffer;
123
124 buffer = intel_ttm_bo_create_from_handle(intel->bufmgr, "region", handle);
125
126 return intel_region_alloc_internal(intel,
127 cpp, pitch, height, tiled, buffer);
128 }
129
130 void
131 intel_region_reference(struct intel_region **dst, struct intel_region *src)
132 {
133 assert(*dst == NULL);
134 if (src) {
135 src->refcount++;
136 *dst = src;
137 }
138 }
139
140 void
141 intel_region_release(struct intel_region **region)
142 {
143 if (!*region)
144 return;
145
146 DBG("%s %d\n", __FUNCTION__, (*region)->refcount - 1);
147
148 ASSERT((*region)->refcount > 0);
149 (*region)->refcount--;
150
151 if ((*region)->refcount == 0) {
152 assert((*region)->map_refcount == 0);
153
154 if ((*region)->pbo)
155 (*region)->pbo->region = NULL;
156 (*region)->pbo = NULL;
157 dri_bo_unreference((*region)->buffer);
158 free(*region);
159 }
160 *region = NULL;
161 }
162
163 /*
164 * XXX Move this into core Mesa?
165 */
166 void
167 _mesa_copy_rect(GLubyte * dst,
168 GLuint cpp,
169 GLuint dst_pitch,
170 GLuint dst_x,
171 GLuint dst_y,
172 GLuint width,
173 GLuint height,
174 const GLubyte * src,
175 GLuint src_pitch, GLuint src_x, GLuint src_y)
176 {
177 GLuint i;
178
179 dst_pitch *= cpp;
180 src_pitch *= cpp;
181 dst += dst_x * cpp;
182 src += src_x * cpp;
183 dst += dst_y * dst_pitch;
184 src += src_y * dst_pitch;
185 width *= cpp;
186
187 if (width == dst_pitch && width == src_pitch)
188 memcpy(dst, src, height * width);
189 else {
190 for (i = 0; i < height; i++) {
191 memcpy(dst, src, width);
192 dst += dst_pitch;
193 src += src_pitch;
194 }
195 }
196 }
197
198
199 /* Upload data to a rectangular sub-region. Lots of choices how to do this:
200 *
201 * - memcpy by span to current destination
202 * - upload data as new buffer and blit
203 *
204 * Currently always memcpy.
205 */
206 void
207 intel_region_data(struct intel_context *intel,
208 struct intel_region *dst,
209 GLuint dst_offset,
210 GLuint dstx, GLuint dsty,
211 const void *src, GLuint src_pitch,
212 GLuint srcx, GLuint srcy, GLuint width, GLuint height)
213 {
214 GLboolean locked = GL_FALSE;
215
216 DBG("%s\n", __FUNCTION__);
217
218 if (intel == NULL)
219 return;
220
221 if (dst->pbo) {
222 if (dstx == 0 &&
223 dsty == 0 && width == dst->pitch && height == dst->height)
224 intel_region_release_pbo(intel, dst);
225 else
226 intel_region_cow(intel, dst);
227 }
228
229 if (!intel->locked) {
230 LOCK_HARDWARE(intel);
231 locked = GL_TRUE;
232 }
233
234 _mesa_copy_rect(intel_region_map(intel, dst) + dst_offset,
235 dst->cpp,
236 dst->pitch,
237 dstx, dsty, width, height, src, src_pitch, srcx, srcy);
238
239 intel_region_unmap(intel, dst);
240
241 if (locked)
242 UNLOCK_HARDWARE(intel);
243
244 }
245
246 /* Copy rectangular sub-regions. Need better logic about when to
247 * push buffers into AGP - will currently do so whenever possible.
248 */
249 void
250 intel_region_copy(struct intel_context *intel,
251 struct intel_region *dst,
252 GLuint dst_offset,
253 GLuint dstx, GLuint dsty,
254 struct intel_region *src,
255 GLuint src_offset,
256 GLuint srcx, GLuint srcy, GLuint width, GLuint height)
257 {
258 DBG("%s\n", __FUNCTION__);
259
260 if (intel == NULL)
261 return;
262
263 if (dst->pbo) {
264 if (dstx == 0 &&
265 dsty == 0 && width == dst->pitch && height == dst->height)
266 intel_region_release_pbo(intel, dst);
267 else
268 intel_region_cow(intel, dst);
269 }
270
271 assert(src->cpp == dst->cpp);
272
273 intelEmitCopyBlit(intel,
274 dst->cpp,
275 src->pitch, src->buffer, src_offset, src->tiled,
276 dst->pitch, dst->buffer, dst_offset, dst->tiled,
277 srcx, srcy, dstx, dsty, width, height,
278 GL_COPY);
279 }
280
281 /* Fill a rectangular sub-region. Need better logic about when to
282 * push buffers into AGP - will currently do so whenever possible.
283 */
284 void
285 intel_region_fill(struct intel_context *intel,
286 struct intel_region *dst,
287 GLuint dst_offset,
288 GLuint dstx, GLuint dsty,
289 GLuint width, GLuint height, GLuint color)
290 {
291 DBG("%s\n", __FUNCTION__);
292
293 if (intel == NULL)
294 return;
295
296 if (dst->pbo) {
297 if (dstx == 0 &&
298 dsty == 0 && width == dst->pitch && height == dst->height)
299 intel_region_release_pbo(intel, dst);
300 else
301 intel_region_cow(intel, dst);
302 }
303
304 intelEmitFillBlit(intel,
305 dst->cpp,
306 dst->pitch, dst->buffer, dst_offset, dst->tiled,
307 dstx, dsty, width, height, color);
308 }
309
310 /* Attach to a pbo, discarding our data. Effectively zero-copy upload
311 * the pbo's data.
312 */
313 void
314 intel_region_attach_pbo(struct intel_context *intel,
315 struct intel_region *region,
316 struct intel_buffer_object *pbo)
317 {
318 if (region->pbo == pbo)
319 return;
320
321 /* If there is already a pbo attached, break the cow tie now.
322 * Don't call intel_region_release_pbo() as that would
323 * unnecessarily allocate a new buffer we would have to immediately
324 * discard.
325 */
326 if (region->pbo) {
327 region->pbo->region = NULL;
328 region->pbo = NULL;
329 }
330
331 if (region->buffer) {
332 dri_bo_unreference(region->buffer);
333 region->buffer = NULL;
334 }
335
336 region->pbo = pbo;
337 region->pbo->region = region;
338 dri_bo_reference(pbo->buffer);
339 region->buffer = pbo->buffer;
340 }
341
342
343 /* Break the COW tie to the pbo and allocate a new buffer.
344 * The pbo gets to keep the data.
345 */
346 void
347 intel_region_release_pbo(struct intel_context *intel,
348 struct intel_region *region)
349 {
350 assert(region->buffer == region->pbo->buffer);
351 region->pbo->region = NULL;
352 region->pbo = NULL;
353 dri_bo_unreference(region->buffer);
354 region->buffer = NULL;
355
356 region->buffer = dri_bo_alloc(intel->bufmgr, "region",
357 region->pitch * region->cpp * region->height,
358 64,
359 DRM_BO_FLAG_MEM_LOCAL |
360 DRM_BO_FLAG_CACHED |
361 DRM_BO_FLAG_CACHED_MAPPED);
362 }
363
364 /* Break the COW tie to the pbo. Both the pbo and the region end up
365 * with a copy of the data.
366 */
367 void
368 intel_region_cow(struct intel_context *intel, struct intel_region *region)
369 {
370 struct intel_buffer_object *pbo = region->pbo;
371 GLboolean was_locked = intel->locked;
372
373 if (intel == NULL)
374 return;
375
376 intel_region_release_pbo(intel, region);
377
378 assert(region->cpp * region->pitch * region->height == pbo->Base.Size);
379
380 DBG("%s (%d bytes)\n", __FUNCTION__, pbo->Base.Size);
381
382 /* Now blit from the texture buffer to the new buffer:
383 */
384
385 intel_batchbuffer_flush(intel->batch);
386
387 was_locked = intel->locked;
388 if (intel->locked)
389 LOCK_HARDWARE(intel);
390
391 intelEmitCopyBlit(intel,
392 region->cpp,
393 region->pitch, region->buffer, 0, region->tiled,
394 region->pitch, pbo->buffer, 0, region->tiled,
395 0, 0, 0, 0,
396 region->pitch, region->height,
397 GL_COPY);
398
399 intel_batchbuffer_flush(intel->batch);
400
401 if (was_locked)
402 UNLOCK_HARDWARE(intel);
403 }
404
405 dri_bo *
406 intel_region_buffer(struct intel_context *intel,
407 struct intel_region *region, GLuint flag)
408 {
409 if (region->pbo) {
410 if (flag == INTEL_WRITE_PART)
411 intel_region_cow(intel, region);
412 else if (flag == INTEL_WRITE_FULL)
413 intel_region_release_pbo(intel, region);
414 }
415
416 return region->buffer;
417 }
418
419 static struct intel_region *
420 intel_recreate_static(struct intel_context *intel,
421 const char *name,
422 struct intel_region *region,
423 intelRegion *region_desc,
424 GLuint mem_type)
425 {
426 intelScreenPrivate *intelScreen = intel->intelScreen;
427
428 if (region == NULL) {
429 region = calloc(sizeof(*region), 1);
430 region->refcount = 1;
431 }
432
433 if (intel->ctx.Visual.rgbBits == 24)
434 region->cpp = 4;
435 else
436 region->cpp = intel->ctx.Visual.rgbBits / 8;
437 region->pitch = intelScreen->pitch;
438 region->height = intelScreen->height; /* needed? */
439 region->tiled = region_desc->tiled;
440
441 if (intel->ttm) {
442 assert(region_desc->bo_handle != -1);
443 region->buffer = intel_ttm_bo_create_from_handle(intel->bufmgr,
444 name,
445 region_desc->bo_handle);
446 } else {
447 region->buffer = dri_bo_alloc_static(intel->bufmgr,
448 name,
449 region_desc->offset,
450 intelScreen->pitch *
451 intelScreen->height,
452 region_desc->map,
453 DRM_BO_FLAG_MEM_TT);
454 }
455
456 assert(region->buffer != NULL);
457
458 return region;
459 }
460
461 /**
462 * Create intel_region structs to describe the static front, back, and depth
463 * buffers created by the xserver.
464 *
465 * Although FBO's mean we now no longer use these as render targets in
466 * all circumstances, they won't go away until the back and depth
467 * buffers become private, and the front buffer will remain even then.
468 *
469 * Note that these don't allocate video memory, just describe
470 * allocations alread made by the X server.
471 */
472 void
473 intel_recreate_static_regions(struct intel_context *intel)
474 {
475 intelScreenPrivate *intelScreen = intel->intelScreen;
476
477 intel->front_region =
478 intel_recreate_static(intel, "front",
479 intel->front_region,
480 &intelScreen->front,
481 DRM_BO_FLAG_MEM_TT);
482
483 intel->back_region =
484 intel_recreate_static(intel, "back",
485 intel->back_region,
486 &intelScreen->back,
487 DRM_BO_FLAG_MEM_TT);
488
489 #ifdef I915
490 if (intelScreen->third.handle) {
491 intel->third_region =
492 intel_recreate_static(intel, "third",
493 intel->third_region,
494 &intelScreen->third,
495 DRM_BO_FLAG_MEM_TT);
496 }
497 #endif /* I915 */
498
499 /* Still assumes front.cpp == depth.cpp. We can kill this when we move to
500 * private buffers.
501 */
502 intel->depth_region =
503 intel_recreate_static(intel, "depth",
504 intel->depth_region,
505 &intelScreen->depth,
506 DRM_BO_FLAG_MEM_TT);
507 }