enable feedback rendering
[mesa.git] / src / mesa / drivers / dri / i915 / intel_ioctl.c
1 /**************************************************************************
2 *
3 * Copyright 2003 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
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <sched.h>
33
34 #include "mtypes.h"
35 #include "context.h"
36 #include "swrast/swrast.h"
37
38 #include "intel_context.h"
39 #include "intel_ioctl.h"
40 #include "intel_batchbuffer.h"
41 #include "drm.h"
42
43 u_int32_t intelGetLastFrame (intelContextPtr intel)
44 {
45 int ret;
46 u_int32_t frame;
47 drm_i915_getparam_t gp;
48
49 gp.param = I915_PARAM_LAST_DISPATCH;
50 gp.value = (int *)&frame;
51 ret = drmCommandWriteRead( intel->driFd, DRM_I915_GETPARAM,
52 &gp, sizeof(gp) );
53 return frame;
54 }
55
56 int intelEmitIrqLocked( intelContextPtr intel )
57 {
58 drmI830IrqEmit ie;
59 int ret, seq;
60
61 assert(((*(int *)intel->driHwLock) & ~DRM_LOCK_CONT) ==
62 (DRM_LOCK_HELD|intel->hHWContext));
63
64 ie.irq_seq = &seq;
65
66 ret = drmCommandWriteRead( intel->driFd, DRM_I830_IRQ_EMIT,
67 &ie, sizeof(ie) );
68 if ( ret ) {
69 fprintf( stderr, "%s: drmI830IrqEmit: %d\n", __FUNCTION__, ret );
70 exit(1);
71 }
72
73 if (0)
74 fprintf(stderr, "%s --> %d\n", __FUNCTION__, seq );
75
76 return seq;
77 }
78
79 void intelWaitIrq( intelContextPtr intel, int seq )
80 {
81 int ret;
82
83 if (0)
84 fprintf(stderr, "%s %d\n", __FUNCTION__, seq );
85
86 intel->iw.irq_seq = seq;
87
88 do {
89 ret = drmCommandWrite( intel->driFd, DRM_I830_IRQ_WAIT, &intel->iw, sizeof(intel->iw) );
90 } while (ret == -EAGAIN || ret == -EINTR);
91
92 if ( ret ) {
93 fprintf( stderr, "%s: drmI830IrqWait: %d\n", __FUNCTION__, ret );
94 if (0)
95 intel_dump_batchbuffer( intel->alloc.offset,
96 intel->alloc.ptr,
97 intel->alloc.size );
98 exit(1);
99 }
100 }
101
102
103
104 static void age_intel( intelContextPtr intel, int age )
105 {
106 GLuint i;
107
108 for (i = 0 ; i < MAX_TEXTURE_UNITS ; i++)
109 if (intel->CurrentTexObj[i])
110 intel->CurrentTexObj[i]->age = age;
111 }
112
113 void intel_dump_batchbuffer( long offset,
114 int *ptr,
115 int count )
116 {
117 int i;
118 fprintf(stderr, "\n\n\nSTART BATCH (%d dwords):\n", count);
119 for (i = 0; i < count/4; i += 4)
120 fprintf(stderr, "\t0x%x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
121 (unsigned int)offset + i*4, ptr[i], ptr[i+1], ptr[i+2], ptr[i+3]);
122 fprintf(stderr, "END BATCH\n\n\n");
123 }
124
125 void intelRefillBatchLocked( intelContextPtr intel, GLboolean allow_unlock )
126 {
127 GLuint last_irq = intel->alloc.irq_emitted;
128 GLuint half = intel->alloc.size / 2;
129 GLuint buf = (intel->alloc.active_buf ^= 1);
130
131 intel->alloc.irq_emitted = intelEmitIrqLocked( intel );
132
133 if (last_irq) {
134 if (allow_unlock) UNLOCK_HARDWARE( intel );
135 intelWaitIrq( intel, last_irq );
136 if (allow_unlock) LOCK_HARDWARE( intel );
137 }
138
139 if (0)
140 fprintf(stderr, "%s: now using half %d\n", __FUNCTION__, buf);
141
142 intel->batch.start_offset = intel->alloc.offset + buf * half;
143 intel->batch.ptr = (unsigned char *)intel->alloc.ptr + buf * half;
144 intel->batch.size = half - 8;
145 intel->batch.space = half - 8;
146 assert(intel->batch.space >= 0);
147 }
148
149 #define MI_BATCH_BUFFER_END (0xA<<23)
150
151
152 void intelFlushBatchLocked( intelContextPtr intel,
153 GLboolean ignore_cliprects,
154 GLboolean refill,
155 GLboolean allow_unlock)
156 {
157 drmI830BatchBuffer batch;
158
159 assert(intel->locked);
160
161 if (0)
162 fprintf(stderr, "%s used %d of %d offset %x..%x refill %d (started in %s)\n",
163 __FUNCTION__,
164 (intel->batch.size - intel->batch.space),
165 intel->batch.size,
166 intel->batch.start_offset,
167 intel->batch.start_offset +
168 (intel->batch.size - intel->batch.space),
169 refill,
170 intel->batch.func);
171
172 /* Throw away non-effective packets. Won't work once we have
173 * hardware contexts which would preserve statechanges beyond a
174 * single buffer.
175 */
176 if (intel->numClipRects == 0 && !ignore_cliprects) {
177
178 /* Without this yeild, an application with no cliprects can hog
179 * the hardware. Without unlocking, the effect is much worse -
180 * effectively a lock-out of other contexts.
181 */
182 if (allow_unlock) {
183 UNLOCK_HARDWARE( intel );
184 sched_yield();
185 LOCK_HARDWARE( intel );
186 }
187
188 /* Note that any state thought to have been emitted actually
189 * hasn't:
190 */
191 intel->batch.ptr -= (intel->batch.size - intel->batch.space);
192 intel->batch.space = intel->batch.size;
193 intel->vtbl.lost_hardware( intel );
194 }
195
196 if (intel->batch.space != intel->batch.size) {
197
198 if (intel->sarea->ctxOwner != intel->hHWContext) {
199 intel->perf_boxes |= I830_BOX_LOST_CONTEXT;
200 intel->sarea->ctxOwner = intel->hHWContext;
201 }
202
203 batch.start = intel->batch.start_offset;
204 batch.used = intel->batch.size - intel->batch.space;
205 batch.cliprects = intel->pClipRects;
206 batch.num_cliprects = ignore_cliprects ? 0 : intel->numClipRects;
207 batch.DR1 = 0;
208 batch.DR4 = ((((GLuint)intel->drawX) & 0xffff) |
209 (((GLuint)intel->drawY) << 16));
210
211 if (intel->alloc.offset) {
212 if ((batch.used & 0x4) == 0) {
213 ((int *)intel->batch.ptr)[0] = 0;
214 ((int *)intel->batch.ptr)[1] = MI_BATCH_BUFFER_END;
215 batch.used += 0x8;
216 intel->batch.ptr += 0x8;
217 }
218 else {
219 ((int *)intel->batch.ptr)[0] = MI_BATCH_BUFFER_END;
220 batch.used += 0x4;
221 intel->batch.ptr += 0x4;
222 }
223 }
224
225 if (0)
226 intel_dump_batchbuffer( batch.start,
227 (int *)(intel->batch.ptr - batch.used),
228 batch.used );
229
230 intel->batch.start_offset += batch.used;
231 intel->batch.size -= batch.used;
232
233 if (intel->batch.size < 8) {
234 refill = GL_TRUE;
235 intel->batch.space = intel->batch.size = 0;
236 }
237 else {
238 intel->batch.size -= 8;
239 intel->batch.space = intel->batch.size;
240 }
241
242
243 assert(intel->batch.space >= 0);
244 assert(batch.start >= intel->alloc.offset);
245 assert(batch.start < intel->alloc.offset + intel->alloc.size);
246 assert(batch.start + batch.used > intel->alloc.offset);
247 assert(batch.start + batch.used <=
248 intel->alloc.offset + intel->alloc.size);
249
250
251 if (intel->alloc.offset) {
252 if (drmCommandWrite (intel->driFd, DRM_I830_BATCHBUFFER, &batch,
253 sizeof(batch))) {
254 fprintf(stderr, "DRM_I830_BATCHBUFFER: %d\n", -errno);
255 UNLOCK_HARDWARE(intel);
256 exit(1);
257 }
258 } else {
259 drmI830CmdBuffer cmd;
260 cmd.buf = (char *)intel->alloc.ptr + batch.start;
261 cmd.sz = batch.used;
262 cmd.DR1 = batch.DR1;
263 cmd.DR4 = batch.DR4;
264 cmd.num_cliprects = batch.num_cliprects;
265 cmd.cliprects = batch.cliprects;
266
267 if (drmCommandWrite (intel->driFd, DRM_I830_CMDBUFFER, &cmd,
268 sizeof(cmd))) {
269 fprintf(stderr, "DRM_I830_CMDBUFFER: %d\n", -errno);
270 UNLOCK_HARDWARE(intel);
271 exit(1);
272 }
273 }
274
275
276 age_intel(intel, intel->sarea->last_enqueue);
277
278 /* FIXME: use hardware contexts to avoid 'losing' hardware after
279 * each buffer flush.
280 */
281 if (intel->batch.contains_geometry)
282 assert(intel->batch.last_emit_state == intel->batch.counter);
283
284 intel->batch.counter++;
285 intel->batch.contains_geometry = 0;
286 intel->batch.func = 0;
287 intel->vtbl.lost_hardware( intel );
288 }
289
290 if (refill)
291 intelRefillBatchLocked( intel, allow_unlock );
292 }
293
294 void intelFlushBatch( intelContextPtr intel, GLboolean refill )
295 {
296 if (intel->locked) {
297 intelFlushBatchLocked( intel, GL_FALSE, refill, GL_FALSE );
298 }
299 else {
300 LOCK_HARDWARE(intel);
301 intelFlushBatchLocked( intel, GL_FALSE, refill, GL_TRUE );
302 UNLOCK_HARDWARE(intel);
303 }
304 }
305
306
307 void intelWaitForIdle( intelContextPtr intel )
308 {
309 if (0)
310 fprintf(stderr, "%s\n", __FUNCTION__);
311
312 intel->vtbl.emit_flush( intel );
313 intelFlushBatch( intel, GL_TRUE );
314
315 /* Use an irq to wait for dma idle -- Need to track lost contexts
316 * to shortcircuit consecutive calls to this function:
317 */
318 intelWaitIrq( intel, intel->alloc.irq_emitted );
319 intel->alloc.irq_emitted = 0;
320 }
321
322
323 /**
324 * Check if we need to rotate/warp the front color buffer to the
325 * rotated screen. We generally need to do this when we get a glFlush
326 * or glFinish after drawing to the front color buffer.
327 */
328 static void
329 intelCheckFrontRotate(GLcontext *ctx)
330 {
331 intelContextPtr intel = INTEL_CONTEXT( ctx );
332 if (intel->ctx.DrawBuffer->_ColorDrawBufferMask[0] == BUFFER_BIT_FRONT_LEFT) {
333 intelScreenPrivate *screen = intel->intelScreen;
334 if (screen->current_rotation != 0) {
335 __DRIdrawablePrivate *dPriv = intel->driDrawable;
336 intelRotateWindow(intel, dPriv, BUFFER_BIT_FRONT_LEFT);
337 }
338 }
339 }
340
341
342 /**
343 * NOT directly called via glFlush.
344 */
345 void intelFlush( GLcontext *ctx )
346 {
347 intelContextPtr intel = INTEL_CONTEXT( ctx );
348
349 if (intel->Fallback)
350 _swrast_flush( ctx );
351
352 INTEL_FIREVERTICES( intel );
353
354 if (intel->batch.size != intel->batch.space)
355 intelFlushBatch( intel, GL_FALSE );
356 }
357
358
359 /**
360 * Called via glFlush.
361 */
362 void intelglFlush( GLcontext *ctx )
363 {
364 intelFlush(ctx);
365 intelCheckFrontRotate(ctx);
366 }
367
368
369 void intelFinish( GLcontext *ctx )
370 {
371 intelContextPtr intel = INTEL_CONTEXT( ctx );
372 intelFlush( ctx );
373 intelWaitForIdle( intel );
374 intelCheckFrontRotate(ctx);
375 }
376
377
378 void intelClear(GLcontext *ctx, GLbitfield mask, GLboolean all,
379 GLint cx, GLint cy, GLint cw, GLint ch)
380 {
381 intelContextPtr intel = INTEL_CONTEXT( ctx );
382 const GLuint colorMask = *((GLuint *) &ctx->Color.ColorMask);
383 GLbitfield tri_mask = 0;
384 GLbitfield blit_mask = 0;
385 GLbitfield swrast_mask = 0;
386
387 if (0)
388 fprintf(stderr, "%s\n", __FUNCTION__);
389
390 /* Take care of cliprects, which are handled differently for
391 * clears, etc.
392 */
393 intelFlush( &intel->ctx );
394
395 if (mask & BUFFER_BIT_FRONT_LEFT) {
396 if (colorMask == ~0) {
397 blit_mask |= BUFFER_BIT_FRONT_LEFT;
398 }
399 else {
400 tri_mask |= BUFFER_BIT_FRONT_LEFT;
401 }
402 }
403
404 if (mask & BUFFER_BIT_BACK_LEFT) {
405 if (colorMask == ~0) {
406 blit_mask |= BUFFER_BIT_BACK_LEFT;
407 }
408 else {
409 tri_mask |= BUFFER_BIT_BACK_LEFT;
410 }
411 }
412
413 if (mask & BUFFER_BIT_DEPTH) {
414 blit_mask |= BUFFER_BIT_DEPTH;
415 }
416
417 if (mask & BUFFER_BIT_STENCIL) {
418 if (!intel->hw_stencil) {
419 swrast_mask |= BUFFER_BIT_STENCIL;
420 }
421 else if (ctx->Stencil.WriteMask[0] != 0xff) {
422 tri_mask |= BUFFER_BIT_STENCIL;
423 }
424 else {
425 blit_mask |= BUFFER_BIT_STENCIL;
426 }
427 }
428
429 swrast_mask |= (mask & BUFFER_BIT_ACCUM);
430
431 if (blit_mask)
432 intelClearWithBlit( ctx, blit_mask, all, cx, cy, cw, ch );
433
434 if (tri_mask)
435 intel->vtbl.clear_with_tris( intel, tri_mask, all, cx, cy, cw, ch);
436
437 if (swrast_mask)
438 _swrast_Clear( ctx, swrast_mask, all, cx, cy, cw, ch );
439 }
440
441
442 void
443 intelRotateWindow(intelContextPtr intel, __DRIdrawablePrivate *dPriv,
444 GLuint srcBuffer)
445 {
446 if (intel->vtbl.rotate_window) {
447 intel->vtbl.rotate_window(intel, dPriv, srcBuffer);
448 }
449 }
450
451
452 void *intelAllocateAGP( intelContextPtr intel, GLsizei size )
453 {
454 int region_offset;
455 drmI830MemAlloc alloc;
456 int ret;
457
458 if (0)
459 fprintf(stderr, "%s: %d bytes\n", __FUNCTION__, size);
460
461 alloc.region = I830_MEM_REGION_AGP;
462 alloc.alignment = 0;
463 alloc.size = size;
464 alloc.region_offset = &region_offset;
465
466 LOCK_HARDWARE(intel);
467
468 /* Make sure the global heap is initialized
469 */
470 if (intel->texture_heaps[0])
471 driAgeTextures( intel->texture_heaps[0] );
472
473
474 ret = drmCommandWriteRead( intel->driFd,
475 DRM_I830_ALLOC,
476 &alloc, sizeof(alloc));
477
478 if (ret) {
479 fprintf(stderr, "%s: DRM_I830_ALLOC ret %d\n", __FUNCTION__, ret);
480 UNLOCK_HARDWARE(intel);
481 return NULL;
482 }
483
484 if (0)
485 fprintf(stderr, "%s: allocated %d bytes\n", __FUNCTION__, size);
486
487 /* Need to propogate this information (agp memory in use) to our
488 * local texture lru. The kernel has already updated the global
489 * lru. An alternative would have been to allocate memory the
490 * usual way and then notify the kernel to pin the allocation.
491 */
492 if (intel->texture_heaps[0])
493 driAgeTextures( intel->texture_heaps[0] );
494
495 UNLOCK_HARDWARE(intel);
496
497 return (void *)((char *)intel->intelScreen->tex.map + region_offset);
498 }
499
500 void intelFreeAGP( intelContextPtr intel, void *pointer )
501 {
502 int region_offset;
503 drmI830MemFree memfree;
504 int ret;
505
506 region_offset = (char *)pointer - (char *)intel->intelScreen->tex.map;
507
508 if (region_offset < 0 ||
509 region_offset > intel->intelScreen->tex.size) {
510 fprintf(stderr, "offset %d outside range 0..%d\n", region_offset,
511 intel->intelScreen->tex.size);
512 return;
513 }
514
515 memfree.region = I830_MEM_REGION_AGP;
516 memfree.region_offset = region_offset;
517
518 ret = drmCommandWrite( intel->driFd,
519 DRM_I830_FREE,
520 &memfree, sizeof(memfree));
521
522 if (ret)
523 fprintf(stderr, "%s: DRM_I830_FREE ret %d\n", __FUNCTION__, ret);
524 }
525
526 /* This version of AllocateMemoryMESA allocates only agp memory, and
527 * only does so after the point at which the driver has been
528 * initialized.
529 *
530 * Theoretically a valid context isn't required. However, in this
531 * implementation, it is, as I'm using the hardware lock to protect
532 * the kernel data structures, and the current context to get the
533 * device fd.
534 */
535 void *intelAllocateMemoryMESA(__DRInativeDisplay *dpy, int scrn,
536 GLsizei size, GLfloat readfreq,
537 GLfloat writefreq, GLfloat priority)
538 {
539 GET_CURRENT_CONTEXT(ctx);
540
541 if (INTEL_DEBUG & DEBUG_IOCTL)
542 fprintf(stderr, "%s sz %d %f/%f/%f\n", __FUNCTION__, size, readfreq,
543 writefreq, priority);
544
545 if (getenv("INTEL_NO_ALLOC"))
546 return NULL;
547
548 if (!ctx || INTEL_CONTEXT(ctx) == 0)
549 return NULL;
550
551 return intelAllocateAGP( INTEL_CONTEXT(ctx), size );
552 }
553
554
555 /* Called via glXFreeMemoryMESA() */
556 void intelFreeMemoryMESA(__DRInativeDisplay *dpy, int scrn, GLvoid *pointer)
557 {
558 GET_CURRENT_CONTEXT(ctx);
559 if (INTEL_DEBUG & DEBUG_IOCTL)
560 fprintf(stderr, "%s %p\n", __FUNCTION__, pointer);
561
562 if (!ctx || INTEL_CONTEXT(ctx) == 0) {
563 fprintf(stderr, "%s: no context\n", __FUNCTION__);
564 return;
565 }
566
567 intelFreeAGP( INTEL_CONTEXT(ctx), pointer );
568 }
569
570 /* Called via glXGetMemoryOffsetMESA()
571 *
572 * Returns offset of pointer from the start of agp aperture.
573 */
574 GLuint intelGetMemoryOffsetMESA(__DRInativeDisplay *dpy, int scrn,
575 const GLvoid *pointer)
576 {
577 GET_CURRENT_CONTEXT(ctx);
578 intelContextPtr intel;
579
580 if (!ctx || !(intel = INTEL_CONTEXT(ctx)) ) {
581 fprintf(stderr, "%s: no context\n", __FUNCTION__);
582 return ~0;
583 }
584
585 if (!intelIsAgpMemory( intel, pointer, 0 ))
586 return ~0;
587
588 return intelAgpOffsetFromVirtual( intel, pointer );
589 }
590
591
592 GLboolean intelIsAgpMemory( intelContextPtr intel, const GLvoid *pointer,
593 GLint size )
594 {
595 int offset = (char *)pointer - (char *)intel->intelScreen->tex.map;
596 int valid = (size >= 0 &&
597 offset >= 0 &&
598 offset + size < intel->intelScreen->tex.size);
599
600 if (INTEL_DEBUG & DEBUG_IOCTL)
601 fprintf(stderr, "intelIsAgpMemory( %p ) : %d\n", pointer, valid );
602
603 return valid;
604 }
605
606
607 GLuint intelAgpOffsetFromVirtual( intelContextPtr intel, const GLvoid *pointer )
608 {
609 int offset = (char *)pointer - (char *)intel->intelScreen->tex.map;
610
611 if (offset < 0 || offset > intel->intelScreen->tex.size)
612 return ~0;
613 else
614 return intel->intelScreen->tex.offset + offset;
615 }
616
617
618
619
620
621 /* Flip the front & back buffes
622 */
623 void intelPageFlip( const __DRIdrawablePrivate *dPriv )
624 {
625 #if 0
626 intelContextPtr intel;
627 int tmp, ret;
628
629 if (INTEL_DEBUG & DEBUG_IOCTL)
630 fprintf(stderr, "%s\n", __FUNCTION__);
631
632 assert(dPriv);
633 assert(dPriv->driContextPriv);
634 assert(dPriv->driContextPriv->driverPrivate);
635
636 intel = (intelContextPtr) dPriv->driContextPriv->driverPrivate;
637
638 intelFlush( &intel->ctx );
639 LOCK_HARDWARE( intel );
640
641 if (dPriv->pClipRects) {
642 *(drm_clip_rect_t *)intel->sarea->boxes = dPriv->pClipRects[0];
643 intel->sarea->nbox = 1;
644 }
645
646 ret = drmCommandNone(intel->driFd, DRM_I830_FLIP);
647 if (ret) {
648 fprintf(stderr, "%s: %d\n", __FUNCTION__, ret);
649 UNLOCK_HARDWARE( intel );
650 exit(1);
651 }
652
653 tmp = intel->sarea->last_enqueue;
654 intelRefillBatchLocked( intel );
655 UNLOCK_HARDWARE( intel );
656
657
658 intelSetDrawBuffer( &intel->ctx, intel->ctx.Color.DriverDrawBuffer );
659 #endif
660 }