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