1 /**************************************************************************
3 * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
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:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
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.
26 **************************************************************************/
28 #include "intel_screen.h"
29 #include "intel_context.h"
30 #include "intel_swapbuffers.h"
31 #include "intel_batchbuffer.h"
32 #include "intel_reg.h"
33 #include "intel_winsys.h"
36 #include "drirenderbuffer.h"
39 #include "pipe/p_context.h"
40 #include "state_tracker/st_context.h"
41 #include "state_tracker/st_cb_fbo.h"
44 /* This block can be removed when libdrm >= 2.3.1 is required */
46 #ifndef DRM_VBLANK_FLIP
48 #define DRM_VBLANK_FLIP 0x8000000
50 typedef struct drm_i915_flip
{
54 #undef DRM_IOCTL_I915_FLIP
55 #define DRM_IOCTL_I915_FLIP DRM_IOW(DRM_COMMAND_BASE + DRM_I915_FLIP, \
62 * Return the pipe_surface for the given renderbuffer.
64 static struct pipe_surface
*
65 get_color_surface(struct intel_framebuffer
*intel_fb
,
68 struct st_renderbuffer
*strb
69 = st_renderbuffer(intel_fb
->Base
.Attachment
[bufferIndex
].Renderbuffer
);
77 * Display a colorbuffer surface in an X window.
78 * Used for SwapBuffers and flushing front buffer rendering.
80 * \param dPriv the window/drawable to display into
81 * \param surf the surface to display
82 * \param rect optional subrect of surface to display (may be NULL).
85 intelDisplayBuffer(__DRIdrawablePrivate
* dPriv
,
86 struct pipe_surface
*surf
,
87 const drm_clip_rect_t
* rect
)
89 struct intel_context
*intel
;
90 const intelScreenPrivate
*intelScreen
91 = (intelScreenPrivate
*) dPriv
->driScreenPriv
->private;
93 DBG(SWAP
, "%s\n", __FUNCTION__
);
97 intel
= intelScreenContext(dPriv
->driScreenPriv
->private);
101 if (intel
->last_swap_fence
) {
102 driFenceFinish(intel
->last_swap_fence
, DRM_FENCE_TYPE_EXE
, GL_TRUE
);
103 driFenceUnReference(intel
->last_swap_fence
);
104 intel
->last_swap_fence
= NULL
;
106 intel
->last_swap_fence
= intel
->first_swap_fence
;
107 intel
->first_swap_fence
= NULL
;
109 /* The LOCK_HARDWARE is required for the cliprects. Buffer offsets
110 * should work regardless.
112 LOCK_HARDWARE(intel
);
113 /* if this drawable isn't currently bound the LOCK_HARDWARE done on the
114 current context (which is what intelScreenContext should return) might
115 not get a contended lock and thus cliprects not updated (tests/manywin) */
116 if ((struct intel_context
*)dPriv
->driContextPriv
->driverPrivate
!= intel
)
117 DRI_VALIDATE_DRAWABLE_INFO(intel
->driScreen
, dPriv
);
120 if (dPriv
&& dPriv
->numClipRects
) {
121 const int srcWidth
= surf
->width
;
122 const int srcHeight
= surf
->height
;
123 const int nbox
= dPriv
->numClipRects
;
124 const drm_clip_rect_t
*pbox
= dPriv
->pClipRects
;
125 const int pitch
= intelScreen
->front
.pitch
/ intelScreen
->front
.cpp
;
126 const int cpp
= intelScreen
->front
.cpp
;
129 const struct pipe_region
*srcRegion
= surf
->region
;
130 const int srcpitch
= srcRegion
->pitch
;
133 ASSERT(srcRegion
->cpp
== cpp
);
135 DBG(SWAP
, "screen pitch %d src surface pitch %d\n",
136 pitch
, srcRegion
->pitch
);
139 BR13
= (pitch
* cpp
) | (0xCC << 16) | (1 << 24);
140 CMD
= XY_SRC_COPY_BLT_CMD
;
143 BR13
= (pitch
* cpp
) | (0xCC << 16) | (1 << 24) | (1 << 25);
144 CMD
= (XY_SRC_COPY_BLT_CMD
| XY_SRC_COPY_BLT_WRITE_ALPHA
|
145 XY_SRC_COPY_BLT_WRITE_RGB
);
148 for (i
= 0; i
< nbox
; i
++, pbox
++) {
150 drm_clip_rect_t sbox
;
152 if (pbox
->x1
> pbox
->x2
||
153 pbox
->y1
> pbox
->y2
||
154 pbox
->x2
> intelScreen
->front
.width
||
155 pbox
->y2
> intelScreen
->front
.height
) {
156 /* invalid cliprect, skip it */
163 /* intersect cliprect with user-provided src rect */
164 drm_clip_rect_t rrect
;
166 rrect
.x1
= dPriv
->x
+ rect
->x1
;
167 rrect
.y1
= (dPriv
->h
- rect
->y1
- rect
->y2
) + dPriv
->y
;
168 rrect
.x2
= rect
->x2
+ rrect
.x1
;
169 rrect
.y2
= rect
->y2
+ rrect
.y1
;
170 if (rrect
.x1
> box
.x1
)
172 if (rrect
.y1
> box
.y1
)
174 if (rrect
.x2
< box
.x2
)
176 if (rrect
.y2
< box
.y2
)
179 if (box
.x1
> box
.x2
|| box
.y1
> box
.y2
)
183 /* restrict blit to size of actually rendered area */
184 if (box
.x2
- box
.x1
> srcWidth
)
185 box
.x2
= srcWidth
+ box
.x1
;
186 if (box
.y2
- box
.y1
> srcHeight
)
187 box
.y2
= srcHeight
+ box
.y1
;
189 DBG(SWAP
, "box x1 x2 y1 y2 %d %d %d %d\n",
190 box
.x1
, box
.x2
, box
.y1
, box
.y2
);
192 sbox
.x1
= box
.x1
- dPriv
->x
;
193 sbox
.y1
= box
.y1
- dPriv
->y
;
195 BEGIN_BATCH(8, INTEL_BATCH_NO_CLIPRECTS
);
198 OUT_BATCH((box
.y1
<< 16) | box
.x1
);
199 OUT_BATCH((box
.y2
<< 16) | box
.x2
);
201 OUT_RELOC(intelScreen
->front
.buffer
,
202 DRM_BO_FLAG_MEM_TT
| DRM_BO_FLAG_WRITE
,
203 DRM_BO_MASK_MEM
| DRM_BO_FLAG_WRITE
, 0);
204 OUT_BATCH((sbox
.y1
<< 16) | sbox
.x1
);
205 OUT_BATCH((srcpitch
* cpp
) & 0xffff);
206 OUT_RELOC(dri_bo(srcRegion
->buffer
),
207 DRM_BO_FLAG_MEM_TT
| DRM_BO_FLAG_READ
,
208 DRM_BO_MASK_MEM
| DRM_BO_FLAG_READ
, 0);
213 if (intel
->first_swap_fence
)
214 driFenceUnReference(intel
->first_swap_fence
);
215 intel
->first_swap_fence
= intel_batchbuffer_flush(intel
->batch
);
216 driFenceReference(intel
->first_swap_fence
);
219 UNLOCK_HARDWARE(intel
);
221 /* XXX this is bogus. The context here may not even be bound to this drawable! */
222 if (intel
->lastStamp
!= dPriv
->lastStamp
) {
223 GET_CURRENT_CONTEXT(currctx
);
224 struct intel_context
*intelcurrent
= intel_context(currctx
);
225 if (intelcurrent
== intel
&& intelcurrent
->driDrawable
== dPriv
) {
226 intelWindowMoved(intel
);
227 intel
->lastStamp
= dPriv
->lastStamp
;
236 * This will be called whenever the currently bound window is moved/resized.
237 * XXX: actually, it seems to NOT be called when the window is only moved (BP).
240 intelWindowMoved(struct intel_context
*intel
)
242 GLcontext
*ctx
= intel
->st
->ctx
;
243 __DRIdrawablePrivate
*dPriv
= intel
->driDrawable
;
244 struct intel_framebuffer
*intel_fb
= dPriv
->driverPrivate
;
246 if (!intel
->st
->ctx
->DrawBuffer
) {
247 /* when would this happen? -BP */
249 intel
->numClipRects
= 0;
252 /* Update Mesa's notion of window size */
253 intelUpdateFramebufferSize(ctx
, dPriv
);
254 intel_fb
->Base
.Initialized
= GL_TRUE
; /* XXX remove someday */
257 drmI830Sarea
*sarea
= intel
->sarea
;
258 drm_clip_rect_t drw_rect
= { .x1
= dPriv
->x
, .x2
= dPriv
->x
+ dPriv
->w
,
259 .y1
= dPriv
->y
, .y2
= dPriv
->y
+ dPriv
->h
};
260 drm_clip_rect_t pipeA_rect
= { .x1
= sarea
->pipeA_x
, .y1
= sarea
->pipeA_y
,
261 .x2
= sarea
->pipeA_x
+ sarea
->pipeA_w
,
262 .y2
= sarea
->pipeA_y
+ sarea
->pipeA_h
};
263 drm_clip_rect_t pipeB_rect
= { .x1
= sarea
->pipeB_x
, .y1
= sarea
->pipeB_y
,
264 .x2
= sarea
->pipeB_x
+ sarea
->pipeB_w
,
265 .y2
= sarea
->pipeB_y
+ sarea
->pipeB_h
};
266 GLint areaA
= driIntersectArea( drw_rect
, pipeA_rect
);
267 GLint areaB
= driIntersectArea( drw_rect
, pipeB_rect
);
268 GLuint flags
= intel_fb
->vblank_flags
;
272 /* Update page flipping info
282 intel_fb
->pf_current_page
= (intel
->sarea
->pf_current_page
>>
283 (intel_fb
->pf_planes
& 0x2)) & 0x3;
285 intel_fb
->pf_num_pages
= 2 /*intel->intelScreen->third.handle ? 3 : 2*/;
287 pf_active
= pf_planes
&& (pf_planes
& intel
->sarea
->pf_active
) == pf_planes
;
289 if (pf_active
!= intel_fb
->pf_active
)
290 DBG(LOCK
, "%s - Page flipping %sactive\n",
291 __progname
, pf_active
? "" : "in");
294 /* Sync pages between planes if we're flipping on both at the same time */
295 if (pf_planes
== 0x3 && pf_planes
!= intel_fb
->pf_planes
&&
296 (intel
->sarea
->pf_current_page
& 0x3) !=
297 (((intel
->sarea
->pf_current_page
) >> 2) & 0x3)) {
298 drm_i915_flip_t flip
;
300 if (intel_fb
->pf_current_page
==
301 (intel
->sarea
->pf_current_page
& 0x3)) {
302 /* XXX: This is ugly, but emitting two flips 'in a row' can cause
303 * lockups for unknown reasons.
305 intel
->sarea
->pf_current_page
=
306 intel
->sarea
->pf_current_page
& 0x3;
307 intel
->sarea
->pf_current_page
|=
308 ((intel_fb
->pf_current_page
+ intel_fb
->pf_num_pages
- 1) %
309 intel_fb
->pf_num_pages
) << 2;
313 intel
->sarea
->pf_current_page
=
314 intel
->sarea
->pf_current_page
& (0x3 << 2);
315 intel
->sarea
->pf_current_page
|=
316 (intel_fb
->pf_current_page
+ intel_fb
->pf_num_pages
- 1) %
317 intel_fb
->pf_num_pages
;
322 drmCommandWrite(intel
->driFd
, DRM_I915_FLIP
, &flip
, sizeof(flip
));
325 intel_fb
->pf_planes
= pf_planes
;
328 intel_fb
->pf_active
= pf_active
;
330 intel_flip_renderbuffers(intel_fb
);
331 intel_draw_buffer(&intel
->ctx
, intel
->ctx
.DrawBuffer
);
334 /* Update vblank info
336 if (areaB
> areaA
|| (areaA
== areaB
&& areaB
> 0)) {
337 flags
= intel_fb
->vblank_flags
| VBLANK_FLAG_SECONDARY
;
339 flags
= intel_fb
->vblank_flags
& ~VBLANK_FLAG_SECONDARY
;
342 if (flags
!= intel_fb
->vblank_flags
&& intel_fb
->vblank_flags
&&
343 !(intel_fb
->vblank_flags
& VBLANK_FLAG_NO_IRQ
)) {
347 vbl
.request
.type
= DRM_VBLANK_ABSOLUTE
;
349 if ( intel_fb
->vblank_flags
& VBLANK_FLAG_SECONDARY
) {
350 vbl
.request
.type
|= DRM_VBLANK_SECONDARY
;
353 for (i
= 0; i
< intel_fb
->pf_num_pages
; i
++) {
354 if ((intel_fb
->vbl_waited
- intel_fb
->vbl_pending
[i
]) <= (1<<23))
357 vbl
.request
.sequence
= intel_fb
->vbl_pending
[i
];
358 drmWaitVBlank(intel
->driFd
, &vbl
);
361 intel_fb
->vblank_flags
= flags
;
362 driGetCurrentVBlank(dPriv
, intel_fb
->vblank_flags
, &intel_fb
->vbl_seq
);
363 intel_fb
->vbl_waited
= intel_fb
->vbl_seq
;
365 for (i
= 0; i
< intel_fb
->pf_num_pages
; i
++) {
366 intel_fb
->vbl_pending
[i
] = intel_fb
->vbl_waited
;
371 /* This will be picked up by looking at the dirty state flags:
374 /* Update hardware scissor */
375 // ctx->Driver.Scissor(ctx, ctx->Scissor.X, ctx->Scissor.Y,
376 // ctx->Scissor.Width, ctx->Scissor.Height);
378 /* Re-calculate viewport related state */
379 // ctx->Driver.DepthRange( ctx, ctx->Viewport.Near, ctx->Viewport.Far );
386 /* Emit wait for pending flips */
389 intel_wait_flips(struct intel_context
*intel
, GLuint batch_flags
)
391 struct intel_framebuffer
*intel_fb
=
392 (struct intel_framebuffer
*) intel
->ctx
.DrawBuffer
;
393 struct intel_renderbuffer
*intel_rb
=
394 intel_get_renderbuffer(&intel_fb
->Base
,
395 intel_fb
->Base
._ColorDrawBufferMask
[0] ==
396 BUFFER_BIT_FRONT_LEFT
? BUFFER_FRONT_LEFT
:
399 if (intel_fb
->Base
.Name
== 0 && intel_rb
->pf_pending
== intel_fb
->pf_seq
) {
400 GLint pf_planes
= intel_fb
->pf_planes
;
403 /* Wait for pending flips to take effect */
404 BEGIN_BATCH(2, batch_flags
);
405 OUT_BATCH(pf_planes
& 0x1 ? (MI_WAIT_FOR_EVENT
| MI_WAIT_FOR_PLANE_A_FLIP
)
407 OUT_BATCH(pf_planes
& 0x2 ? (MI_WAIT_FOR_EVENT
| MI_WAIT_FOR_PLANE_B_FLIP
)
411 intel_rb
->pf_pending
--;
417 /* Flip the front & back buffers
420 intelPageFlip(const __DRIdrawablePrivate
* dPriv
)
422 struct intel_context
*intel
;
424 struct intel_framebuffer
*intel_fb
= dPriv
->driverPrivate
;
426 DBG(SWAP
, "%s\n", __FUNCTION__
);
429 assert(dPriv
->driContextPriv
);
430 assert(dPriv
->driContextPriv
->driverPrivate
);
432 intel
= (struct intel_context
*) dPriv
->driContextPriv
->driverPrivate
;
434 if (intel
->intelScreen
->drmMinor
< 9)
437 intelFlush(&intel
->ctx
);
441 LOCK_HARDWARE(intel
);
443 if (dPriv
->numClipRects
&& intel_fb
->pf_active
) {
444 drm_i915_flip_t flip
;
446 flip
.pipes
= intel_fb
->pf_planes
;
448 ret
= drmCommandWrite(intel
->driFd
, DRM_I915_FLIP
, &flip
, sizeof(flip
));
451 UNLOCK_HARDWARE(intel
);
453 if (ret
|| !intel_fb
->pf_active
)
456 if (!dPriv
->numClipRects
) {
457 usleep(10000); /* throttle invisible client 10ms */
460 intel_fb
->pf_current_page
= (intel
->sarea
->pf_current_page
>>
461 (intel_fb
->pf_planes
& 0x2)) & 0x3;
463 if (dPriv
->numClipRects
!= 0) {
464 intel_get_renderbuffer(&intel_fb
->Base
, BUFFER_FRONT_LEFT
)->pf_pending
=
465 intel_get_renderbuffer(&intel_fb
->Base
, BUFFER_BACK_LEFT
)->pf_pending
=
470 intel_flip_renderbuffers(intel_fb
);
471 intel_draw_buffer(&intel
->ctx
, &intel_fb
->Base
);
474 DBG(SWAP
, "%s: success\n", __FUNCTION__
);
482 intelScheduleSwap(const __DRIdrawablePrivate
* dPriv
, GLboolean
*missed_target
)
484 struct intel_framebuffer
*intel_fb
= dPriv
->driverPrivate
;
485 unsigned int interval
= driGetVBlankInterval(dPriv
, intel_fb
->vblank_flags
);
486 struct intel_context
*intel
=
487 intelScreenContext(dPriv
->driScreenPriv
->private);
488 const intelScreenPrivate
*intelScreen
= intel
->intelScreen
;
490 drm_i915_vblank_swap_t swap
;
493 /* XXX: Scheduled buffer swaps don't work with private back buffers yet */
494 if (1 || !intel_fb
->vblank_flags
||
495 (intel_fb
->vblank_flags
& VBLANK_FLAG_NO_IRQ
) ||
496 intelScreen
->drmMinor
< (intel_fb
->pf_active
? 9 : 6))
499 swap
.seqtype
= DRM_VBLANK_ABSOLUTE
;
501 if (intel_fb
->vblank_flags
& VBLANK_FLAG_SYNC
) {
502 swap
.seqtype
|= DRM_VBLANK_NEXTONMISS
;
503 } else if (interval
== 0) {
507 swap
.drawable
= dPriv
->hHWDrawable
;
508 target
= swap
.sequence
= intel_fb
->vbl_seq
+ interval
;
510 if ( intel_fb
->vblank_flags
& VBLANK_FLAG_SECONDARY
) {
511 swap
.seqtype
|= DRM_VBLANK_SECONDARY
;
514 LOCK_HARDWARE(intel
);
516 intel_batchbuffer_flush(intel
->batch
);
518 if ( intel_fb
->pf_active
) {
519 swap
.seqtype
|= DRM_VBLANK_FLIP
;
521 intel_fb
->pf_current_page
= (((intel
->sarea
->pf_current_page
>>
522 (intel_fb
->pf_planes
& 0x2)) & 0x3) + 1) %
523 intel_fb
->pf_num_pages
;
526 if (!drmCommandWriteRead(intel
->driFd
, DRM_I915_VBLANK_SWAP
, &swap
,
528 intel_fb
->vbl_seq
= swap
.sequence
;
529 swap
.sequence
-= target
;
530 *missed_target
= swap
.sequence
> 0 && swap
.sequence
<= (1 << 23);
533 intel_fb
->vbl_pending
[1] = intel_fb
->vbl_pending
[0] = intel_fb
->vbl_seq
;
535 intel_get_renderbuffer(&intel_fb
->Base
, BUFFER_BACK_LEFT
)->vbl_pending
=
536 intel_get_renderbuffer(&intel_fb
->Base
,
537 BUFFER_FRONT_LEFT
)->vbl_pending
=
541 if (swap
.seqtype
& DRM_VBLANK_FLIP
) {
543 intel_flip_renderbuffers(intel_fb
);
544 intel_draw_buffer(&intel
->ctx
, intel
->ctx
.DrawBuffer
);
550 if (swap
.seqtype
& DRM_VBLANK_FLIP
) {
551 intel_fb
->pf_current_page
= ((intel
->sarea
->pf_current_page
>>
552 (intel_fb
->pf_planes
& 0x2)) & 0x3) %
553 intel_fb
->pf_num_pages
;
559 UNLOCK_HARDWARE(intel
);
565 intelSwapBuffers(__DRIdrawablePrivate
* dPriv
)
567 if (dPriv
->driContextPriv
&& dPriv
->driContextPriv
->driverPrivate
) {
568 GET_CURRENT_CONTEXT(ctx
);
569 struct intel_context
*intel
;
574 intel
= intel_context(ctx
);
576 if (ctx
->Visual
.doubleBufferMode
) {
577 GLboolean missed_target
;
578 struct intel_framebuffer
*intel_fb
= dPriv
->driverPrivate
;
581 _mesa_notifySwapBuffers(ctx
); /* flush pending rendering comands */
583 if (!intelScheduleSwap(dPriv
, &missed_target
)) {
584 struct pipe_surface
*back_surf
585 = get_color_surface(intel_fb
, BUFFER_BACK_LEFT
);
587 driWaitForVBlank(dPriv
, &intel_fb
->vbl_seq
, intel_fb
->vblank_flags
,
590 intelDisplayBuffer(dPriv
, back_surf
, NULL
);
593 intel_fb
->swap_count
++;
594 (*dri_interface
->getUST
) (&ust
);
596 intel_fb
->swap_missed_count
++;
597 intel_fb
->swap_missed_ust
= ust
- intel_fb
->swap_ust
;
600 intel_fb
->swap_ust
= ust
;
604 /* XXX this shouldn't be an error but we can't handle it for now */
605 fprintf(stderr
, "%s: drawable has no context!\n", __FUNCTION__
);
611 * Called via glXCopySubBufferMESA() to copy a subrect of the back
612 * buffer to the front buffer/screen.
615 intelCopySubBuffer(__DRIdrawablePrivate
* dPriv
, int x
, int y
, int w
, int h
)
617 if (dPriv
->driContextPriv
&& dPriv
->driContextPriv
->driverPrivate
) {
618 struct intel_context
*intel
=
619 (struct intel_context
*) dPriv
->driContextPriv
->driverPrivate
;
620 GLcontext
*ctx
= intel
->st
->ctx
;
622 if (ctx
->Visual
.doubleBufferMode
) {
623 struct intel_framebuffer
*intel_fb
= dPriv
->driverPrivate
;
624 struct pipe_surface
*back_surf
625 = get_color_surface(intel_fb
, BUFFER_BACK_LEFT
);
627 drm_clip_rect_t rect
;
628 /* fixup cliprect (driDrawable may have changed?) later */
633 _mesa_notifySwapBuffers(ctx
); /* flush pending rendering comands */
634 intelDisplayBuffer(dPriv
, back_surf
, &rect
);
638 /* XXX this shouldn't be an error but we can't handle it for now */
639 fprintf(stderr
, "%s: drawable has no context!\n", __FUNCTION__
);