4eac859a13356fd8fd4c82a9f19ee1bb41a5a736
[mesa.git] / src / mesa / drivers / dri / i915 / 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_batchbuffer.h"
48
49 #define FILE_DEBUG_FLAG DEBUG_REGION
50
51 void
52 intel_region_idle(intelScreenPrivate *intelScreen, struct intel_region *region)
53 {
54 DBG("%s\n", __FUNCTION__);
55 /* XXX: Using this function is likely bogus -- it ought to only have been
56 * used before a map, anyway, but leave this cheap implementation of it
57 * for now.
58 */
59 if (region && region->buffer) {
60 /* Mapping it for read will ensure that any acceleration to the region
61 * would have landed already.
62 */
63 dri_bo_map(region->buffer, GL_TRUE);
64 dri_bo_unmap(region->buffer);
65 }
66 }
67
68 /* XXX: Thread safety?
69 */
70 GLubyte *
71 intel_region_map(intelScreenPrivate *intelScreen, struct intel_region *region)
72 {
73 DBG("%s\n", __FUNCTION__);
74 if (!region->map_refcount++) {
75 if (region->pbo)
76 intel_region_cow(intelScreen, region);
77
78 dri_bo_map(region->buffer, GL_TRUE);
79 region->map = region->buffer->virtual;
80 }
81
82 return region->map;
83 }
84
85 void
86 intel_region_unmap(intelScreenPrivate *intelScreen, struct intel_region *region)
87 {
88 DBG("%s\n", __FUNCTION__);
89 if (!--region->map_refcount) {
90 dri_bo_unmap(region->buffer);
91 region->map = NULL;
92 }
93 }
94
95 struct intel_region *
96 intel_region_alloc(intelScreenPrivate *intelScreen,
97 GLuint cpp, GLuint pitch, GLuint height)
98 {
99 struct intel_region *region = calloc(sizeof(*region), 1);
100
101 DBG("%s\n", __FUNCTION__);
102
103 region->cpp = cpp;
104 region->pitch = pitch;
105 region->height = height; /* needed? */
106 region->refcount = 1;
107
108 region->buffer = dri_bo_alloc(intelScreen->bufmgr, "region",
109 pitch * cpp * height, 64, DRM_BO_FLAG_MEM_TT);
110 return region;
111 }
112
113 void
114 intel_region_reference(struct intel_region **dst, struct intel_region *src)
115 {
116 assert(*dst == NULL);
117 if (src) {
118 src->refcount++;
119 *dst = src;
120 }
121 }
122
123 void
124 intel_region_release(struct intel_region **region)
125 {
126 if (!*region)
127 return;
128
129 DBG("%s %d\n", __FUNCTION__, (*region)->refcount - 1);
130
131 ASSERT((*region)->refcount > 0);
132 (*region)->refcount--;
133
134 if ((*region)->refcount == 0) {
135 assert((*region)->map_refcount == 0);
136
137 if ((*region)->pbo)
138 (*region)->pbo->region = NULL;
139 (*region)->pbo = NULL;
140 dri_bo_unreference((*region)->buffer);
141 free(*region);
142 }
143 *region = NULL;
144 }
145
146
147 struct intel_region *
148 intel_region_create_static(intelScreenPrivate *intelScreen,
149 GLuint mem_type,
150 unsigned int bo_handle,
151 GLuint offset,
152 void *virtual,
153 GLuint cpp, GLuint pitch, GLuint height)
154 {
155 struct intel_region *region = calloc(sizeof(*region), 1);
156 DBG("%s\n", __FUNCTION__);
157
158 region->cpp = cpp;
159 region->pitch = pitch;
160 region->height = height; /* needed? */
161 region->refcount = 1;
162
163 if (intelScreen->ttm) {
164 assert(bo_handle != -1);
165 region->buffer = dri_ttm_bo_create_from_handle(intelScreen->bufmgr,
166 "static region",
167 bo_handle);
168 } else {
169 region->buffer = dri_bo_alloc_static(intelScreen->bufmgr,
170 "static region",
171 offset, pitch * cpp * height,
172 virtual,
173 DRM_BO_FLAG_MEM_TT);
174 }
175
176 return region;
177 }
178
179
180
181 void
182 intel_region_update_static(intelScreenPrivate *intelScreen,
183 struct intel_region *region,
184 GLuint mem_type,
185 unsigned int bo_handle,
186 GLuint offset,
187 void *virtual,
188 GLuint cpp, GLuint pitch, GLuint height)
189 {
190 DBG("%s\n", __FUNCTION__);
191
192 region->cpp = cpp;
193 region->pitch = pitch;
194 region->height = height; /* needed? */
195
196 /*
197 * We use a "shared" buffer type to indicate buffers created and
198 * shared by others.
199 */
200
201 dri_bo_unreference(region->buffer);
202 if (intelScreen->ttm) {
203 assert(bo_handle != -1);
204 region->buffer = dri_ttm_bo_create_from_handle(intelScreen->bufmgr,
205 "static region",
206 bo_handle);
207 } else {
208 region->buffer = dri_bo_alloc_static(intelScreen->bufmgr,
209 "static region",
210 offset, pitch * cpp * height,
211 virtual,
212 DRM_BO_FLAG_MEM_TT);
213 }
214 }
215
216
217
218 /*
219 * XXX Move this into core Mesa?
220 */
221 static void
222 _mesa_copy_rect(GLubyte * dst,
223 GLuint cpp,
224 GLuint dst_pitch,
225 GLuint dst_x,
226 GLuint dst_y,
227 GLuint width,
228 GLuint height,
229 const GLubyte * src,
230 GLuint src_pitch, GLuint src_x, GLuint src_y)
231 {
232 GLuint i;
233
234 dst_pitch *= cpp;
235 src_pitch *= cpp;
236 dst += dst_x * cpp;
237 src += src_x * cpp;
238 dst += dst_y * dst_pitch;
239 src += src_y * dst_pitch;
240 width *= cpp;
241
242 if (width == dst_pitch && width == src_pitch)
243 memcpy(dst, src, height * width);
244 else {
245 for (i = 0; i < height; i++) {
246 memcpy(dst, src, width);
247 dst += dst_pitch;
248 src += src_pitch;
249 }
250 }
251 }
252
253
254 /* Upload data to a rectangular sub-region. Lots of choices how to do this:
255 *
256 * - memcpy by span to current destination
257 * - upload data as new buffer and blit
258 *
259 * Currently always memcpy.
260 */
261 void
262 intel_region_data(intelScreenPrivate *intelScreen,
263 struct intel_region *dst,
264 GLuint dst_offset,
265 GLuint dstx, GLuint dsty,
266 const void *src, GLuint src_pitch,
267 GLuint srcx, GLuint srcy, GLuint width, GLuint height)
268 {
269 struct intel_context *intel = intelScreenContext(intelScreen);
270
271 DBG("%s\n", __FUNCTION__);
272
273 if (intel == NULL)
274 return;
275
276 if (dst->pbo) {
277 if (dstx == 0 &&
278 dsty == 0 && width == dst->pitch && height == dst->height)
279 intel_region_release_pbo(intelScreen, dst);
280 else
281 intel_region_cow(intelScreen, dst);
282 }
283
284
285 LOCK_HARDWARE(intel);
286
287 _mesa_copy_rect(intel_region_map(intelScreen, dst) + dst_offset,
288 dst->cpp,
289 dst->pitch,
290 dstx, dsty, width, height, src, src_pitch, srcx, srcy);
291
292 intel_region_unmap(intelScreen, dst);
293
294 UNLOCK_HARDWARE(intel);
295
296 }
297
298 /* Copy rectangular sub-regions. Need better logic about when to
299 * push buffers into AGP - will currently do so whenever possible.
300 */
301 void
302 intel_region_copy(intelScreenPrivate *intelScreen,
303 struct intel_region *dst,
304 GLuint dst_offset,
305 GLuint dstx, GLuint dsty,
306 struct intel_region *src,
307 GLuint src_offset,
308 GLuint srcx, GLuint srcy, GLuint width, GLuint height)
309 {
310 struct intel_context *intel = intelScreenContext(intelScreen);
311
312 DBG("%s\n", __FUNCTION__);
313
314 if (intel == NULL)
315 return;
316
317 if (dst->pbo) {
318 if (dstx == 0 &&
319 dsty == 0 && width == dst->pitch && height == dst->height)
320 intel_region_release_pbo(intelScreen, dst);
321 else
322 intel_region_cow(intelScreen, dst);
323 }
324
325 assert(src->cpp == dst->cpp);
326
327 intelEmitCopyBlit(intel,
328 dst->cpp,
329 src->pitch, src->buffer, src_offset,
330 dst->pitch, dst->buffer, dst_offset,
331 srcx, srcy, dstx, dsty, width, height,
332 GL_COPY);
333 }
334
335 /* Fill a rectangular sub-region. Need better logic about when to
336 * push buffers into AGP - will currently do so whenever possible.
337 */
338 void
339 intel_region_fill(intelScreenPrivate *intelScreen,
340 struct intel_region *dst,
341 GLuint dst_offset,
342 GLuint dstx, GLuint dsty,
343 GLuint width, GLuint height, GLuint color)
344 {
345 struct intel_context *intel = intelScreenContext(intelScreen);
346
347 DBG("%s\n", __FUNCTION__);
348
349 if (intel == NULL)
350 return;
351
352 if (dst->pbo) {
353 if (dstx == 0 &&
354 dsty == 0 && width == dst->pitch && height == dst->height)
355 intel_region_release_pbo(intelScreen, dst);
356 else
357 intel_region_cow(intelScreen, dst);
358 }
359
360 intelEmitFillBlit(intel,
361 dst->cpp,
362 dst->pitch, dst->buffer, dst_offset,
363 dstx, dsty, width, height, color);
364 }
365
366 /* Attach to a pbo, discarding our data. Effectively zero-copy upload
367 * the pbo's data.
368 */
369 void
370 intel_region_attach_pbo(intelScreenPrivate *intelScreen,
371 struct intel_region *region,
372 struct intel_buffer_object *pbo)
373 {
374 if (region->pbo == pbo)
375 return;
376
377 /* If there is already a pbo attached, break the cow tie now.
378 * Don't call intel_region_release_pbo() as that would
379 * unnecessarily allocate a new buffer we would have to immediately
380 * discard.
381 */
382 if (region->pbo) {
383 region->pbo->region = NULL;
384 region->pbo = NULL;
385 }
386
387 if (region->buffer) {
388 dri_bo_unreference(region->buffer);
389 region->buffer = NULL;
390 }
391
392 region->pbo = pbo;
393 region->pbo->region = region;
394 dri_bo_reference(pbo->buffer);
395 region->buffer = pbo->buffer;
396 }
397
398
399 /* Break the COW tie to the pbo and allocate a new buffer.
400 * The pbo gets to keep the data.
401 */
402 void
403 intel_region_release_pbo(intelScreenPrivate *intelScreen,
404 struct intel_region *region)
405 {
406 assert(region->buffer == region->pbo->buffer);
407 region->pbo->region = NULL;
408 region->pbo = NULL;
409 dri_bo_unreference(region->buffer);
410 region->buffer = NULL;
411
412 region->buffer = dri_bo_alloc(intelScreen->bufmgr, "region",
413 region->pitch * region->cpp * region->height,
414 64, DRM_BO_FLAG_MEM_TT);
415 }
416
417 /* Break the COW tie to the pbo. Both the pbo and the region end up
418 * with a copy of the data.
419 */
420 void
421 intel_region_cow(intelScreenPrivate *intelScreen, struct intel_region *region)
422 {
423 struct intel_context *intel = intelScreenContext(intelScreen);
424 struct intel_buffer_object *pbo = region->pbo;
425
426 if (intel == NULL)
427 return;
428
429 intel_region_release_pbo(intelScreen, region);
430
431 assert(region->cpp * region->pitch * region->height == pbo->Base.Size);
432
433 DBG("%s (%d bytes)\n", __FUNCTION__, pbo->Base.Size);
434
435 /* Now blit from the texture buffer to the new buffer:
436 */
437
438 intel_batchbuffer_flush(intel->batch);
439
440 if (!intel->locked) {
441 LOCK_HARDWARE(intel);
442 intelEmitCopyBlit(intel,
443 region->cpp,
444 region->pitch,
445 region->buffer, 0,
446 region->pitch,
447 pbo->buffer, 0,
448 0, 0, 0, 0,
449 region->pitch, region->height,
450 GL_COPY);
451
452 intel_batchbuffer_flush(intel->batch);
453 UNLOCK_HARDWARE(intel);
454 }
455 else {
456 intelEmitCopyBlit(intel,
457 region->cpp,
458 region->pitch,
459 region->buffer, 0,
460 region->pitch,
461 pbo->buffer, 0,
462 0, 0, 0, 0,
463 region->pitch, region->height,
464 GL_COPY);
465
466 intel_batchbuffer_flush(intel->batch);
467 }
468 }
469
470 dri_bo *
471 intel_region_buffer(intelScreenPrivate *intelScreen,
472 struct intel_region *region, GLuint flag)
473 {
474 if (region->pbo) {
475 if (flag == INTEL_WRITE_PART)
476 intel_region_cow(intelScreen, region);
477 else if (flag == INTEL_WRITE_FULL)
478 intel_region_release_pbo(intelScreen, region);
479 }
480
481 return region->buffer;
482 }