efbebaa4ccfe9e2e4999733caa153eb8e817700b
[mesa.git] / src / gallium / state_trackers / xorg / xvmc / surface.c
1 /**************************************************************************
2 *
3 * Copyright 2009 Younes Manton.
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 #include <assert.h>
29 #include <stdio.h>
30 #include <X11/Xlibint.h>
31 #include <vl_winsys.h>
32 #include <pipe/p_video_context.h>
33 #include <pipe/p_video_state.h>
34 #include <pipe/p_state.h>
35 #include <util/u_inlines.h>
36 #include <util/u_memory.h>
37 #include <util/u_math.h>
38 #include "xvmc_private.h"
39
40 static enum pipe_mpeg12_macroblock_type TypeToPipe(int xvmc_mb_type)
41 {
42 if (xvmc_mb_type & XVMC_MB_TYPE_INTRA)
43 return PIPE_MPEG12_MACROBLOCK_TYPE_INTRA;
44 if ((xvmc_mb_type & (XVMC_MB_TYPE_MOTION_FORWARD | XVMC_MB_TYPE_MOTION_BACKWARD)) == XVMC_MB_TYPE_MOTION_FORWARD)
45 return PIPE_MPEG12_MACROBLOCK_TYPE_FWD;
46 if ((xvmc_mb_type & (XVMC_MB_TYPE_MOTION_FORWARD | XVMC_MB_TYPE_MOTION_BACKWARD)) == XVMC_MB_TYPE_MOTION_BACKWARD)
47 return PIPE_MPEG12_MACROBLOCK_TYPE_BKWD;
48 if ((xvmc_mb_type & (XVMC_MB_TYPE_MOTION_FORWARD | XVMC_MB_TYPE_MOTION_BACKWARD)) == (XVMC_MB_TYPE_MOTION_FORWARD | XVMC_MB_TYPE_MOTION_BACKWARD))
49 return PIPE_MPEG12_MACROBLOCK_TYPE_BI;
50
51 assert(0);
52
53 XVMC_MSG(XVMC_ERR, "[XvMC] Unrecognized mb type 0x%08X.\n", xvmc_mb_type);
54
55 return -1;
56 }
57
58 static enum pipe_mpeg12_picture_type PictureToPipe(int xvmc_pic)
59 {
60 switch (xvmc_pic) {
61 case XVMC_TOP_FIELD:
62 return PIPE_MPEG12_PICTURE_TYPE_FIELD_TOP;
63 case XVMC_BOTTOM_FIELD:
64 return PIPE_MPEG12_PICTURE_TYPE_FIELD_BOTTOM;
65 case XVMC_FRAME_PICTURE:
66 return PIPE_MPEG12_PICTURE_TYPE_FRAME;
67 default:
68 assert(0);
69 }
70
71 XVMC_MSG(XVMC_ERR, "[XvMC] Unrecognized picture type 0x%08X.\n", xvmc_pic);
72
73 return -1;
74 }
75
76 static enum pipe_mpeg12_motion_type MotionToPipe(int xvmc_motion_type, unsigned int xvmc_picture_structure)
77 {
78 switch (xvmc_motion_type) {
79 case XVMC_PREDICTION_FRAME:
80 if (xvmc_picture_structure == XVMC_FRAME_PICTURE)
81 return PIPE_MPEG12_MOTION_TYPE_FRAME;
82 else
83 return PIPE_MPEG12_MOTION_TYPE_16x8;
84 break;
85 case XVMC_PREDICTION_FIELD:
86 return PIPE_MPEG12_MOTION_TYPE_FIELD;
87 case XVMC_PREDICTION_DUAL_PRIME:
88 return PIPE_MPEG12_MOTION_TYPE_DUALPRIME;
89 default:
90 assert(0);
91 }
92
93 XVMC_MSG(XVMC_ERR, "[XvMC] Unrecognized motion type 0x%08X (with picture structure 0x%08X).\n", xvmc_motion_type, xvmc_picture_structure);
94
95 return -1;
96 }
97
98 static void
99 MacroBlocksToPipe(struct pipe_screen *screen,
100 unsigned int xvmc_picture_structure,
101 const XvMCMacroBlockArray *xvmc_macroblocks,
102 const XvMCBlockArray *xvmc_blocks,
103 unsigned int first_macroblock,
104 unsigned int num_macroblocks,
105 struct pipe_mpeg12_macroblock *mb)
106 {
107 unsigned int i, j;
108 XvMCMacroBlock *xvmc_mb;
109
110 assert(xvmc_macroblocks);
111 assert(xvmc_blocks);
112 assert(mb);
113 assert(num_macroblocks);
114
115 xvmc_mb = xvmc_macroblocks->macro_blocks + first_macroblock;
116
117 for (i = 0; i < num_macroblocks; ++i) {
118 mb->base.codec = PIPE_VIDEO_CODEC_MPEG12;
119 mb->mbx = xvmc_mb->x;
120 mb->mby = xvmc_mb->y;
121 mb->mb_type = TypeToPipe(xvmc_mb->macroblock_type);
122 if (mb->mb_type != PIPE_MPEG12_MACROBLOCK_TYPE_INTRA)
123 mb->mo_type = MotionToPipe(xvmc_mb->motion_type, xvmc_picture_structure);
124 /* Get rid of Valgrind 'undefined' warnings */
125 else
126 mb->mo_type = -1;
127 mb->dct_type = xvmc_mb->dct_type == XVMC_DCT_TYPE_FIELD ?
128 PIPE_MPEG12_DCT_TYPE_FIELD : PIPE_MPEG12_DCT_TYPE_FRAME;
129
130 for (j = 0; j < 2; ++j) {
131 mb->mv[j].top.x = xvmc_mb->PMV[0][j][0];
132 mb->mv[j].top.y = xvmc_mb->PMV[0][j][1];
133 mb->mv[j].bottom.x = xvmc_mb->PMV[1][j][0];
134 mb->mv[j].bottom.y = xvmc_mb->PMV[1][j][1];
135 }
136
137 mb->mv[0].top.field_select = xvmc_mb->motion_vertical_field_select & XVMC_SELECT_FIRST_FORWARD;
138 mb->mv[1].top.field_select = xvmc_mb->motion_vertical_field_select & XVMC_SELECT_FIRST_BACKWARD;
139 mb->mv[0].bottom.field_select = xvmc_mb->motion_vertical_field_select & XVMC_SELECT_SECOND_FORWARD;
140 mb->mv[1].bottom.field_select = xvmc_mb->motion_vertical_field_select & XVMC_SELECT_SECOND_BACKWARD;
141
142 mb->cbp = xvmc_mb->coded_block_pattern;
143 mb->blocks = xvmc_blocks->blocks + xvmc_mb->index * BLOCK_SIZE_SAMPLES;
144
145 ++mb;
146 ++xvmc_mb;
147 }
148 }
149
150 static void
151 unmap_and_flush_surface(XvMCSurfacePrivate *surface)
152 {
153 struct pipe_video_buffer *ref_frames[2];
154 unsigned i;
155
156 assert(surface);
157
158 for ( i = 0; i < 2; ++i ) {
159 if (surface->ref_surfaces[i]) {
160 XvMCSurfacePrivate *ref = surface->ref_surfaces[i]->privData;
161
162 assert(ref);
163
164 unmap_and_flush_surface(ref);
165 surface->ref_surfaces[i] = NULL;
166 ref_frames[i] = ref->pipe_buffer;
167 } else {
168 ref_frames[i] = NULL;
169 }
170 }
171
172 if (surface->mapped) {
173 surface->pipe_buffer->unmap(surface->pipe_buffer);
174 surface->pipe_buffer->flush(surface->pipe_buffer,
175 ref_frames,
176 &surface->flush_fence);
177 surface->mapped = 0;
178 }
179 }
180
181 PUBLIC
182 Status XvMCCreateSurface(Display *dpy, XvMCContext *context, XvMCSurface *surface)
183 {
184 XvMCContextPrivate *context_priv;
185 struct pipe_video_context *vpipe;
186 XvMCSurfacePrivate *surface_priv;
187
188 XVMC_MSG(XVMC_TRACE, "[XvMC] Creating surface %p.\n", surface);
189
190 assert(dpy);
191
192 if (!context)
193 return XvMCBadContext;
194 if (!surface)
195 return XvMCBadSurface;
196
197 context_priv = context->privData;
198 vpipe = context_priv->vctx->vpipe;
199
200 surface_priv = CALLOC(1, sizeof(XvMCSurfacePrivate));
201 if (!surface_priv)
202 return BadAlloc;
203
204 surface_priv->pipe_buffer = vpipe->create_buffer(vpipe);
205 surface_priv->context = context;
206
207 surface->surface_id = XAllocID(dpy);
208 surface->context_id = context->context_id;
209 surface->surface_type_id = context->surface_type_id;
210 surface->width = context->width;
211 surface->height = context->height;
212 surface->privData = surface_priv;
213
214 SyncHandle();
215
216 XVMC_MSG(XVMC_TRACE, "[XvMC] Surface %p created.\n", surface);
217
218 return Success;
219 }
220
221 PUBLIC
222 Status XvMCRenderSurface(Display *dpy, XvMCContext *context, unsigned int picture_structure,
223 XvMCSurface *target_surface, XvMCSurface *past_surface, XvMCSurface *future_surface,
224 unsigned int flags, unsigned int num_macroblocks, unsigned int first_macroblock,
225 XvMCMacroBlockArray *macroblocks, XvMCBlockArray *blocks
226 )
227 {
228 struct pipe_video_context *vpipe;
229 struct pipe_video_buffer *t_buffer;
230 XvMCContextPrivate *context_priv;
231 XvMCSurfacePrivate *target_surface_priv;
232 XvMCSurfacePrivate *past_surface_priv;
233 XvMCSurfacePrivate *future_surface_priv;
234 struct pipe_mpeg12_macroblock pipe_macroblocks[num_macroblocks];
235
236 XVMC_MSG(XVMC_TRACE, "[XvMC] Rendering to surface %p, with past %p and future %p\n",
237 target_surface, past_surface, future_surface);
238
239 assert(dpy);
240
241 if (!context || !context->privData)
242 return XvMCBadContext;
243 if (!target_surface || !target_surface->privData)
244 return XvMCBadSurface;
245
246 if (picture_structure != XVMC_TOP_FIELD &&
247 picture_structure != XVMC_BOTTOM_FIELD &&
248 picture_structure != XVMC_FRAME_PICTURE)
249 return BadValue;
250 /* Bkwd pred equivalent to fwd (past && !future) */
251 if (future_surface && !past_surface)
252 return BadMatch;
253
254 assert(context->context_id == target_surface->context_id);
255 assert(!past_surface || context->context_id == past_surface->context_id);
256 assert(!future_surface || context->context_id == future_surface->context_id);
257
258 assert(macroblocks);
259 assert(blocks);
260
261 assert(macroblocks->context_id == context->context_id);
262 assert(blocks->context_id == context->context_id);
263
264 assert(flags == 0 || flags == XVMC_SECOND_FIELD);
265
266 target_surface_priv = target_surface->privData;
267 past_surface_priv = past_surface ? past_surface->privData : NULL;
268 future_surface_priv = future_surface ? future_surface->privData : NULL;
269
270 assert(target_surface_priv->context == context);
271 assert(!past_surface || past_surface_priv->context == context);
272 assert(!future_surface || future_surface_priv->context == context);
273
274 context_priv = context->privData;
275 vpipe = context_priv->vctx->vpipe;
276
277 t_buffer = target_surface_priv->pipe_buffer;
278
279 // enshure that all reference frames are flushed
280 // not really nessasary, but speeds ups rendering
281 if (past_surface)
282 unmap_and_flush_surface(past_surface->privData);
283
284 if (future_surface)
285 unmap_and_flush_surface(future_surface->privData);
286
287 /* If the surface we're rendering hasn't changed the ref frames shouldn't change. */
288 if (target_surface_priv->mapped && (
289 target_surface_priv->ref_surfaces[0] != past_surface ||
290 target_surface_priv->ref_surfaces[1] != future_surface)) {
291
292 // If they change anyway we need to flush our surface
293 unmap_and_flush_surface(target_surface_priv);
294 }
295
296 MacroBlocksToPipe(vpipe->screen, picture_structure, macroblocks, blocks, first_macroblock,
297 num_macroblocks, pipe_macroblocks);
298
299 if (!target_surface_priv->mapped) {
300 t_buffer->map(t_buffer);
301 target_surface_priv->ref_surfaces[0] = past_surface;
302 target_surface_priv->ref_surfaces[1] = future_surface;
303 target_surface_priv->mapped = 1;
304 }
305
306 t_buffer->add_macroblocks(t_buffer, num_macroblocks, &pipe_macroblocks->base);
307
308 XVMC_MSG(XVMC_TRACE, "[XvMC] Submitted surface %p for rendering.\n", target_surface);
309
310 return Success;
311 }
312
313 PUBLIC
314 Status XvMCFlushSurface(Display *dpy, XvMCSurface *surface)
315 {
316 assert(dpy);
317
318 if (!surface)
319 return XvMCBadSurface;
320
321 // don't call flush here, because this is usually
322 // called once for every slice instead of every frame
323
324 XVMC_MSG(XVMC_TRACE, "[XvMC] Flushing surface %p\n", surface);
325
326 return Success;
327 }
328
329 PUBLIC
330 Status XvMCSyncSurface(Display *dpy, XvMCSurface *surface)
331 {
332 assert(dpy);
333
334 if (!surface)
335 return XvMCBadSurface;
336
337 XVMC_MSG(XVMC_TRACE, "[XvMC] Syncing surface %p\n", surface);
338
339 return Success;
340 }
341
342 PUBLIC
343 Status XvMCPutSurface(Display *dpy, XvMCSurface *surface, Drawable drawable,
344 short srcx, short srcy, unsigned short srcw, unsigned short srch,
345 short destx, short desty, unsigned short destw, unsigned short desth,
346 int flags)
347 {
348 static int dump_window = -1;
349
350 struct pipe_video_context *vpipe;
351 struct pipe_video_compositor *compositor;
352
353 XvMCSurfacePrivate *surface_priv;
354 XvMCContextPrivate *context_priv;
355 XvMCSubpicturePrivate *subpicture_priv;
356 XvMCContext *context;
357 struct pipe_video_rect src_rect = {srcx, srcy, srcw, srch};
358 struct pipe_video_rect dst_rect = {destx, desty, destw, desth};
359 struct pipe_surface *drawable_surface;
360
361 XVMC_MSG(XVMC_TRACE, "[XvMC] Displaying surface %p.\n", surface);
362
363 assert(dpy);
364
365 if (!surface || !surface->privData)
366 return XvMCBadSurface;
367
368 surface_priv = surface->privData;
369 context = surface_priv->context;
370 context_priv = context->privData;
371
372 drawable_surface = vl_drawable_surface_get(context_priv->vctx, drawable);
373 if (!drawable_surface)
374 return BadDrawable;
375
376 assert(flags == XVMC_TOP_FIELD || flags == XVMC_BOTTOM_FIELD || flags == XVMC_FRAME_PICTURE);
377 assert(srcx + srcw - 1 < surface->width);
378 assert(srcy + srch - 1 < surface->height);
379 /*
380 * Some apps (mplayer) hit these asserts because they call
381 * this function after the window has been resized by the WM
382 * but before they've handled the corresponding XEvent and
383 * know about the new dimensions. The output should be clipped
384 * until the app updates destw and desth.
385 */
386 /*
387 assert(destx + destw - 1 < drawable_surface->width);
388 assert(desty + desth - 1 < drawable_surface->height);
389 */
390
391 subpicture_priv = surface_priv->subpicture ? surface_priv->subpicture->privData : NULL;
392 vpipe = context_priv->vctx->vpipe;
393 compositor = context_priv->compositor;
394
395 unmap_and_flush_surface(surface_priv);
396
397 compositor->clear_layers(compositor);
398 compositor->set_buffer_layer(compositor, 0, surface_priv->pipe_buffer, &src_rect, NULL);
399
400 if (subpicture_priv) {
401 struct pipe_video_rect src_rect = {surface_priv->subx, surface_priv->suby, surface_priv->subw, surface_priv->subh};
402 struct pipe_video_rect dst_rect = {surface_priv->surfx, surface_priv->surfy, surface_priv->surfw, surface_priv->surfh};
403
404 XVMC_MSG(XVMC_TRACE, "[XvMC] Surface %p has subpicture %p.\n", surface, surface_priv->subpicture);
405
406 assert(subpicture_priv->surface == surface);
407 if (subpicture_priv->palette)
408 compositor->set_palette_layer(compositor, 1, subpicture_priv->sampler, subpicture_priv->palette, &src_rect, &dst_rect);
409 else
410 compositor->set_rgba_layer(compositor, 1, subpicture_priv->sampler, &src_rect, &dst_rect);
411
412 surface_priv->subpicture = NULL;
413 subpicture_priv->surface = NULL;
414 }
415
416 compositor->render_picture(compositor, PictureToPipe(flags), drawable_surface, &dst_rect, &surface_priv->disp_fence);
417
418 XVMC_MSG(XVMC_TRACE, "[XvMC] Submitted surface %p for display. Pushing to front buffer.\n", surface);
419
420 vpipe->screen->flush_frontbuffer
421 (
422 vpipe->screen,
423 drawable_surface->texture,
424 0, 0,
425 vl_contextprivate_get(context_priv->vctx, drawable_surface)
426 );
427
428 pipe_surface_reference(&drawable_surface, NULL);
429
430 if(dump_window == -1) {
431 dump_window = debug_get_num_option("XVMC_DUMP", 0);
432 }
433
434 if(dump_window) {
435 static unsigned int framenum = 0;
436 char cmd[256];
437 sprintf(cmd, "xwd -id %d -out xvmc_frame_%08d.xwd", (int)drawable, ++framenum);
438 system(cmd);
439 }
440
441 XVMC_MSG(XVMC_TRACE, "[XvMC] Pushed surface %p to front buffer.\n", surface);
442
443 return Success;
444 }
445
446 PUBLIC
447 Status XvMCGetSurfaceStatus(Display *dpy, XvMCSurface *surface, int *status)
448 {
449 assert(dpy);
450
451 if (!surface)
452 return XvMCBadSurface;
453
454 assert(status);
455
456 *status = 0;
457
458 return Success;
459 }
460
461 PUBLIC
462 Status XvMCDestroySurface(Display *dpy, XvMCSurface *surface)
463 {
464 XvMCSurfacePrivate *surface_priv;
465
466 XVMC_MSG(XVMC_TRACE, "[XvMC] Destroying surface %p.\n", surface);
467
468 assert(dpy);
469
470 if (!surface || !surface->privData)
471 return XvMCBadSurface;
472
473 surface_priv = surface->privData;
474 surface_priv->pipe_buffer->destroy(surface_priv->pipe_buffer);
475 FREE(surface_priv);
476 surface->privData = NULL;
477
478 XVMC_MSG(XVMC_TRACE, "[XvMC] Surface %p destroyed.\n", surface);
479
480 return Success;
481 }
482
483 PUBLIC
484 Status XvMCHideSurface(Display *dpy, XvMCSurface *surface)
485 {
486 assert(dpy);
487
488 if (!surface || !surface->privData)
489 return XvMCBadSurface;
490
491 /* No op, only for overlaid rendering */
492
493 return Success;
494 }