Merge remote branch 'origin/7.8'
[mesa.git] / src / gallium / state_trackers / dri / drm / dri1.c
1 /**************************************************************************
2 *
3 * Copyright 2009, VMware, Inc.
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 VMWARE 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 * Author: Keith Whitwell <keithw@vmware.com>
29 * Author: Jakob Bornecrantz <wallbraker@gmail.com>
30 */
31
32 /* XXX DRI1 is untested after the switch to st_api.h */
33
34 #include "util/u_memory.h"
35 #include "util/u_rect.h"
36 #include "util/u_inlines.h"
37 #include "pipe/p_context.h"
38 #include "state_tracker/dri1_api.h"
39
40 #include "dri_screen.h"
41 #include "dri_context.h"
42 #include "dri_drawable.h"
43 #include "dri_st_api.h"
44 #include "dri1_helper.h"
45 #include "dri1.h"
46
47 static INLINE void
48 dri1_lock(struct dri_context *ctx)
49 {
50 drm_context_t hw_context = ctx->cPriv->hHWContext;
51 char ret = 0;
52
53 DRM_CAS(ctx->lock, hw_context, DRM_LOCK_HELD | hw_context, ret);
54 if (ret) {
55 drmGetLock(ctx->sPriv->fd, hw_context, 0);
56 ctx->stLostLock = TRUE;
57 ctx->wsLostLock = TRUE;
58 }
59 ctx->isLocked = TRUE;
60 }
61
62 static INLINE void
63 dri1_unlock(struct dri_context *ctx)
64 {
65 ctx->isLocked = FALSE;
66 DRM_UNLOCK(ctx->sPriv->fd, ctx->lock, ctx->cPriv->hHWContext);
67 }
68
69 static void
70 dri1_update_drawables_locked(struct dri_context *ctx,
71 __DRIdrawable * driDrawPriv,
72 __DRIdrawable * driReadPriv)
73 {
74 if (ctx->stLostLock) {
75 ctx->stLostLock = FALSE;
76 if (driDrawPriv == driReadPriv)
77 DRI_VALIDATE_DRAWABLE_INFO(ctx->sPriv, driDrawPriv);
78 else
79 DRI_VALIDATE_TWO_DRAWABLES_INFO(ctx->sPriv, driDrawPriv,
80 driReadPriv);
81 }
82 }
83
84 /**
85 * This ensures all contexts which bind to a drawable pick up the
86 * drawable change and signal new buffer state.
87 */
88 static void
89 dri1_propagate_drawable_change(struct dri_context *ctx)
90 {
91 __DRIdrawable *dPriv = ctx->dPriv;
92 __DRIdrawable *rPriv = ctx->rPriv;
93 struct dri_drawable *draw = dri_drawable(dPriv);
94 struct dri_drawable *read = dri_drawable(rPriv);
95 boolean flushed = FALSE;
96
97 if (dPriv && draw->texture_stamp != dPriv->lastStamp) {
98 ctx->st->flush(ctx->st, PIPE_FLUSH_RENDER_CACHE, NULL);
99 flushed = TRUE;
100 ctx->st->notify_invalid_framebuffer(ctx->st, draw->stfb);
101 }
102
103 if (rPriv && dPriv != rPriv && read->texture_stamp != rPriv->lastStamp) {
104 if (!flushed)
105 ctx->st->flush(ctx->st, PIPE_FLUSH_RENDER_CACHE, NULL);
106 ctx->st->notify_invalid_framebuffer(ctx->st, read->stfb);
107 }
108 }
109
110 static INLINE boolean
111 dri1_intersect_src_bbox(struct drm_clip_rect *dst,
112 int dst_x,
113 int dst_y,
114 const struct drm_clip_rect *src,
115 const struct drm_clip_rect *bbox)
116 {
117 int xy1;
118 int xy2;
119
120 xy1 = ((int)src->x1 > (int)bbox->x1 + dst_x) ? src->x1 :
121 (int)bbox->x1 + dst_x;
122 xy2 = ((int)src->x2 < (int)bbox->x2 + dst_x) ? src->x2 :
123 (int)bbox->x2 + dst_x;
124 if (xy1 >= xy2 || xy1 < 0)
125 return FALSE;
126
127 dst->x1 = xy1;
128 dst->x2 = xy2;
129
130 xy1 = ((int)src->y1 > (int)bbox->y1 + dst_y) ? src->y1 :
131 (int)bbox->y1 + dst_y;
132 xy2 = ((int)src->y2 < (int)bbox->y2 + dst_y) ? src->y2 :
133 (int)bbox->y2 + dst_y;
134 if (xy1 >= xy2 || xy1 < 0)
135 return FALSE;
136
137 dst->y1 = xy1;
138 dst->y2 = xy2;
139 return TRUE;
140 }
141
142 static void
143 dri1_swap_copy(struct pipe_context *pipe,
144 struct pipe_surface *dst,
145 struct pipe_surface *src,
146 __DRIdrawable * dPriv, const struct drm_clip_rect *bbox)
147 {
148 struct drm_clip_rect clip;
149 struct drm_clip_rect *cur;
150 int i;
151
152 cur = dPriv->pClipRects;
153
154 for (i = 0; i < dPriv->numClipRects; ++i) {
155 if (dri1_intersect_src_bbox(&clip, dPriv->x, dPriv->y, cur++, bbox)) {
156 if (pipe->surface_copy) {
157 pipe->surface_copy(pipe, dst, clip.x1, clip.y1,
158 src,
159 (int)clip.x1 - dPriv->x,
160 (int)clip.y1 - dPriv->y,
161 clip.x2 - clip.x1, clip.y2 - clip.y1);
162 } else {
163 util_surface_copy(pipe, FALSE, dst, clip.x1, clip.y1,
164 src,
165 (int)clip.x1 - dPriv->x,
166 (int)clip.y1 - dPriv->y,
167 clip.x2 - clip.x1, clip.y2 - clip.y1);
168 }
169 }
170 }
171 }
172
173 static void
174 dri1_present_texture_locked(__DRIdrawable * dPriv,
175 struct pipe_texture *ptex,
176 const struct drm_clip_rect *sub_box,
177 struct pipe_fence_handle **fence)
178 {
179 struct dri_drawable *drawable = dri_drawable(dPriv);
180 struct dri_screen *screen = dri_screen(drawable->sPriv);
181 struct pipe_context *pipe;
182 struct pipe_surface *psurf;
183 struct drm_clip_rect bbox;
184 boolean visible = TRUE;
185
186 *fence = NULL;
187
188 bbox.x1 = 0;
189 bbox.x2 = ptex->width0;
190 bbox.y1 = 0;
191 bbox.y2 = ptex->height0;
192
193 if (sub_box)
194 visible = dri1_intersect_src_bbox(&bbox, 0, 0, &bbox, sub_box);
195 if (!visible)
196 return;
197
198 pipe = dri1_get_pipe_context(screen);
199 psurf = dri1_get_pipe_surface(drawable, ptex);
200 if (!pipe || !psurf)
201 return;
202
203 if (__dri1_api_hooks->present_locked) {
204 __dri1_api_hooks->present_locked(pipe, psurf,
205 dPriv->pClipRects, dPriv->numClipRects,
206 dPriv->x, dPriv->y, &bbox, fence);
207 } else if (__dri1_api_hooks->front_srf_locked) {
208 struct pipe_surface *front = __dri1_api_hooks->front_srf_locked(pipe);
209
210 if (front)
211 dri1_swap_copy(pipe, front, psurf, dPriv, &bbox);
212
213 pipe->flush(pipe, PIPE_FLUSH_RENDER_CACHE, fence);
214 }
215 }
216
217 static void
218 dri1_copy_to_front(struct dri_context *ctx,
219 struct pipe_texture *ptex,
220 __DRIdrawable * dPriv,
221 const struct drm_clip_rect *sub_box,
222 struct pipe_fence_handle **fence)
223 {
224 boolean save_lost_lock;
225
226 dri1_lock(ctx);
227 save_lost_lock = ctx->stLostLock;
228 dri1_update_drawables_locked(ctx, dPriv, dPriv);
229
230 dri1_present_texture_locked(dPriv, ptex, sub_box, fence);
231
232 ctx->stLostLock = save_lost_lock;
233
234 /**
235 * FIXME: Revisit this: Update drawables on copy_sub_buffer ?
236 */
237
238 if (!sub_box)
239 dri1_update_drawables_locked(ctx, ctx->dPriv, ctx->rPriv);
240
241 dri1_unlock(ctx);
242 dri1_propagate_drawable_change(ctx);
243 }
244
245 /*
246 * Backend functions for st_framebuffer interface and swap_buffers.
247 */
248
249 void
250 dri1_flush_frontbuffer(struct dri_drawable *draw,
251 enum st_attachment_type statt)
252 {
253 struct dri_context *ctx = dri_get_current();
254 struct dri_screen *screen = dri_screen(draw->sPriv);
255 struct pipe_screen *pipe_screen = screen->pipe_screen;
256 struct pipe_fence_handle *dummy_fence;
257 struct pipe_texture *ptex;
258
259 if (!ctx)
260 return; /* For now */
261
262 ptex = draw->textures[statt];
263 if (ptex) {
264 dri1_copy_to_front(ctx, ptex, ctx->dPriv, NULL, &dummy_fence);
265 pipe_screen->fence_reference(pipe_screen, &dummy_fence, NULL);
266 }
267
268 /**
269 * FIXME: Do we need swap throttling here?
270 */
271 }
272
273 void
274 dri1_swap_buffers(__DRIdrawable * dPriv)
275 {
276 struct dri_context *ctx = dri_get_current();
277 struct dri_drawable *draw = dri_drawable(dPriv);
278 struct dri_screen *screen = dri_screen(draw->sPriv);
279 struct pipe_screen *pipe_screen = screen->pipe_screen;
280 struct pipe_fence_handle *fence;
281 struct pipe_texture *ptex;
282
283 assert(__dri1_api_hooks != NULL);
284
285 if (!ctx)
286 return; /* For now */
287
288 ptex = draw->textures[ST_ATTACHMENT_BACK_LEFT];
289 if (ptex) {
290 ctx->st->flush(ctx->st, PIPE_FLUSH_RENDER_CACHE, NULL);
291 fence = dri1_swap_fences_pop_front(draw);
292 if (fence) {
293 (void)pipe_screen->fence_finish(pipe_screen, fence, 0);
294 pipe_screen->fence_reference(pipe_screen, &fence, NULL);
295 }
296 dri1_copy_to_front(ctx, ptex, dPriv, NULL, &fence);
297 dri1_swap_fences_push_back(draw, fence);
298 pipe_screen->fence_reference(pipe_screen, &fence, NULL);
299 }
300 }
301
302 void
303 dri1_copy_sub_buffer(__DRIdrawable * dPriv, int x, int y, int w, int h)
304 {
305 struct dri_context *ctx = dri_get_current();
306 struct dri_screen *screen = dri_screen(dPriv->driScreenPriv);
307 struct pipe_screen *pipe_screen = screen->pipe_screen;
308 struct drm_clip_rect sub_bbox;
309 struct dri_drawable *draw = dri_drawable(dPriv);
310 struct pipe_fence_handle *dummy_fence;
311 struct pipe_texture *ptex;
312
313 assert(__dri1_api_hooks != NULL);
314
315 if (!ctx)
316 return;
317
318 sub_bbox.x1 = x;
319 sub_bbox.x2 = x + w;
320 sub_bbox.y1 = y;
321 sub_bbox.y2 = y + h;
322
323 ptex = draw->textures[ST_ATTACHMENT_BACK_LEFT];
324 if (ptex) {
325 ctx->st->flush(ctx->st, PIPE_FLUSH_RENDER_CACHE, NULL);
326 dri1_copy_to_front(ctx, ptex, dPriv, &sub_bbox, &dummy_fence);
327 pipe_screen->fence_reference(pipe_screen, &dummy_fence, NULL);
328 }
329 }
330
331 /**
332 * Allocate framebuffer attachments.
333 *
334 * During fixed-size operation, the function keeps allocating new attachments
335 * as they are requested. Unused attachments are not removed, not until the
336 * framebuffer is resized or destroyed.
337 */
338 void
339 dri1_allocate_textures(struct dri_drawable *drawable,
340 unsigned mask)
341 {
342 struct dri_screen *screen = dri_screen(drawable->sPriv);
343 struct pipe_texture templ;
344 unsigned width, height;
345 boolean resized;
346 int i;
347
348 width = drawable->dPriv->w;
349 height = drawable->dPriv->h;
350
351 resized = (drawable->old_w != width ||
352 drawable->old_h != height);
353
354 /* remove outdated textures */
355 if (resized) {
356 for (i = 0; i < ST_ATTACHMENT_COUNT; i++)
357 pipe_texture_reference(&drawable->textures[i], NULL);
358 }
359
360 memset(&templ, 0, sizeof(templ));
361 templ.target = PIPE_TEXTURE_2D;
362 templ.width0 = width;
363 templ.height0 = height;
364 templ.depth0 = 1;
365 templ.last_level = 0;
366
367 for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
368 enum pipe_format format;
369 unsigned tex_usage;
370
371 /* the texture already exists or not requested */
372 if (drawable->textures[i] || !(mask & (1 << i))) {
373 continue;
374 }
375
376 switch (i) {
377 case ST_ATTACHMENT_FRONT_LEFT:
378 case ST_ATTACHMENT_BACK_LEFT:
379 case ST_ATTACHMENT_FRONT_RIGHT:
380 case ST_ATTACHMENT_BACK_RIGHT:
381 format = drawable->stvis.color_format;
382 tex_usage = PIPE_TEXTURE_USAGE_DISPLAY_TARGET |
383 PIPE_TEXTURE_USAGE_RENDER_TARGET;
384 break;
385 case ST_ATTACHMENT_DEPTH_STENCIL:
386 format = drawable->stvis.depth_stencil_format;
387 tex_usage = PIPE_TEXTURE_USAGE_DEPTH_STENCIL;
388 break;
389 default:
390 format = PIPE_FORMAT_NONE;
391 break;
392 }
393
394 if (format != PIPE_FORMAT_NONE) {
395 templ.format = format;
396 templ.tex_usage = tex_usage;
397
398 drawable->textures[i] =
399 screen->pipe_screen->texture_create(screen->pipe_screen, &templ);
400 }
401 }
402
403 drawable->old_w = width;
404 drawable->old_h = height;
405 }
406
407 /*
408 * Backend function for init_screen.
409 */
410
411 static const __DRIextension *dri1_screen_extensions[] = {
412 &driReadDrawableExtension,
413 &driCopySubBufferExtension.base,
414 &driSwapControlExtension.base,
415 &driFrameTrackingExtension.base,
416 &driMediaStreamCounterExtension.base,
417 NULL
418 };
419
420 static void
421 st_dri_lock(struct pipe_context *pipe)
422 {
423 dri1_lock((struct dri_context *)pipe->priv);
424 }
425
426 static void
427 st_dri_unlock(struct pipe_context *pipe)
428 {
429 dri1_unlock((struct dri_context *)pipe->priv);
430 }
431
432 static boolean
433 st_dri_is_locked(struct pipe_context *pipe)
434 {
435 return ((struct dri_context *)pipe->priv)->isLocked;
436 }
437
438 static boolean
439 st_dri_lost_lock(struct pipe_context *pipe)
440 {
441 return ((struct dri_context *)pipe->priv)->wsLostLock;
442 }
443
444 static void
445 st_dri_clear_lost_lock(struct pipe_context *pipe)
446 {
447 ((struct dri_context *)pipe->priv)->wsLostLock = FALSE;
448 }
449
450 static struct dri1_api_lock_funcs dri1_lf = {
451 .lock = st_dri_lock,
452 .unlock = st_dri_unlock,
453 .is_locked = st_dri_is_locked,
454 .is_lock_lost = st_dri_lost_lock,
455 .clear_lost_lock = st_dri_clear_lost_lock
456 };
457
458 static INLINE void
459 dri1_copy_version(struct dri1_api_version *dst,
460 const struct __DRIversionRec *src)
461 {
462 dst->major = src->major;
463 dst->minor = src->minor;
464 dst->patch_level = src->patch;
465 }
466
467 struct dri1_api *__dri1_api_hooks = NULL;
468
469 const __DRIconfig **
470 dri1_init_screen(__DRIscreen * sPriv)
471 {
472 const __DRIconfig **configs;
473 struct dri_screen *screen;
474 struct dri1_create_screen_arg arg;
475
476 screen = CALLOC_STRUCT(dri_screen);
477 if (!screen)
478 return NULL;
479
480 screen->api = drm_api_create();
481 screen->sPriv = sPriv;
482 screen->fd = sPriv->fd;
483 screen->drmLock = (drmLock *) & sPriv->pSAREA->lock;
484
485 sPriv->private = (void *)screen;
486 sPriv->extensions = dri1_screen_extensions;
487
488 arg.base.mode = DRM_CREATE_DRI1;
489 arg.lf = &dri1_lf;
490 arg.ddx_info = sPriv->pDevPriv;
491 arg.ddx_info_size = sPriv->devPrivSize;
492 arg.sarea = sPriv->pSAREA;
493 dri1_copy_version(&arg.ddx_version, &sPriv->ddx_version);
494 dri1_copy_version(&arg.dri_version, &sPriv->dri_version);
495 dri1_copy_version(&arg.drm_version, &sPriv->drm_version);
496 arg.api = NULL;
497
498 /**
499 * FIXME: If the driver supports format conversion swapbuffer blits, we might
500 * want to support other color bit depths than the server is currently
501 * using.
502 */
503
504 configs = dri_init_screen_helper(screen, &arg.base, sPriv->fbBPP);
505 if (!configs)
506 goto fail;
507
508 if (!arg.api) {
509 debug_printf("%s: failed to create dri1 screen\n", __FUNCTION__);
510 goto fail;
511 }
512
513 __dri1_api_hooks = arg.api;
514
515 return configs;
516 fail:
517 if (configs)
518 FREE(configs);
519 dri_destroy_screen_helper(screen);
520 FREE(screen);
521 return NULL;
522 }