st/vdpau: avoid an unnessasary copy
[mesa.git] / src / gallium / state_trackers / vdpau / presentation.c
1 /**************************************************************************
2 *
3 * Copyright 2010 Thomas Balling Sørensen.
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 <stdio.h>
29 #include <time.h>
30 #include <sys/timeb.h>
31
32 #include <vdpau/vdpau.h>
33
34 #include "util/u_debug.h"
35 #include "util/u_memory.h"
36
37 #include "vdpau_private.h"
38
39 /**
40 * Create a VdpPresentationQueue.
41 */
42 VdpStatus
43 vlVdpPresentationQueueCreate(VdpDevice device,
44 VdpPresentationQueueTarget presentation_queue_target,
45 VdpPresentationQueue *presentation_queue)
46 {
47 vlVdpPresentationQueue *pq = NULL;
48 VdpStatus ret;
49
50 if (!presentation_queue)
51 return VDP_STATUS_INVALID_POINTER;
52
53 vlVdpDevice *dev = vlGetDataHTAB(device);
54 if (!dev)
55 return VDP_STATUS_INVALID_HANDLE;
56
57 vlVdpPresentationQueueTarget *pqt = vlGetDataHTAB(presentation_queue_target);
58 if (!pqt)
59 return VDP_STATUS_INVALID_HANDLE;
60
61 if (dev != pqt->device)
62 return VDP_STATUS_HANDLE_DEVICE_MISMATCH;
63
64 pq = CALLOC(1, sizeof(vlVdpPresentationQueue));
65 if (!pq)
66 return VDP_STATUS_RESOURCES;
67
68 pq->device = dev;
69 pq->drawable = pqt->drawable;
70
71 if (!vl_compositor_init_state(&pq->cstate, dev->context)) {
72 ret = VDP_STATUS_ERROR;
73 goto no_compositor;
74 }
75
76 *presentation_queue = vlAddDataHTAB(pq);
77 if (*presentation_queue == 0) {
78 ret = VDP_STATUS_ERROR;
79 goto no_handle;
80 }
81
82 return VDP_STATUS_OK;
83
84 no_handle:
85 no_compositor:
86 FREE(pq);
87 return ret;
88 }
89
90 /**
91 * Destroy a VdpPresentationQueue.
92 */
93 VdpStatus
94 vlVdpPresentationQueueDestroy(VdpPresentationQueue presentation_queue)
95 {
96 vlVdpPresentationQueue *pq;
97
98 pq = vlGetDataHTAB(presentation_queue);
99 if (!pq)
100 return VDP_STATUS_INVALID_HANDLE;
101
102 vl_compositor_cleanup_state(&pq->cstate);
103
104 vlRemoveDataHTAB(presentation_queue);
105 FREE(pq);
106
107 return VDP_STATUS_OK;
108 }
109
110 /**
111 * Configure the background color setting.
112 */
113 VdpStatus
114 vlVdpPresentationQueueSetBackgroundColor(VdpPresentationQueue presentation_queue,
115 VdpColor *const background_color)
116 {
117 vlVdpPresentationQueue *pq;
118 union pipe_color_union color;
119
120 if (!background_color)
121 return VDP_STATUS_INVALID_POINTER;
122
123 pq = vlGetDataHTAB(presentation_queue);
124 if (!pq)
125 return VDP_STATUS_INVALID_HANDLE;
126
127 color.f[0] = background_color->red;
128 color.f[1] = background_color->green;
129 color.f[2] = background_color->blue;
130 color.f[3] = background_color->alpha;
131
132 vl_compositor_set_clear_color(&pq->cstate, &color);
133
134 return VDP_STATUS_OK;
135 }
136
137 /**
138 * Retrieve the current background color setting.
139 */
140 VdpStatus
141 vlVdpPresentationQueueGetBackgroundColor(VdpPresentationQueue presentation_queue,
142 VdpColor *const background_color)
143 {
144 vlVdpPresentationQueue *pq;
145 union pipe_color_union color;
146
147 if (!background_color)
148 return VDP_STATUS_INVALID_POINTER;
149
150 pq = vlGetDataHTAB(presentation_queue);
151 if (!pq)
152 return VDP_STATUS_INVALID_HANDLE;
153
154 vl_compositor_get_clear_color(&pq->cstate, &color);
155
156 background_color->red = color.f[0];
157 background_color->green = color.f[1];
158 background_color->blue = color.f[2];
159 background_color->alpha = color.f[3];
160
161 return VDP_STATUS_OK;
162 }
163
164 /**
165 * Retrieve the presentation queue's "current" time.
166 */
167 VdpStatus
168 vlVdpPresentationQueueGetTime(VdpPresentationQueue presentation_queue,
169 VdpTime *current_time)
170 {
171 vlVdpPresentationQueue *pq;
172 struct timespec ts;
173
174 if (!current_time)
175 return VDP_STATUS_INVALID_POINTER;
176
177 pq = vlGetDataHTAB(presentation_queue);
178 if (!pq)
179 return VDP_STATUS_INVALID_HANDLE;
180
181 clock_gettime(CLOCK_REALTIME, &ts);
182 *current_time = (uint64_t)ts.tv_sec * 1000000000LL + (uint64_t)ts.tv_nsec;
183
184 return VDP_STATUS_OK;
185 }
186
187 /**
188 * Enter a surface into the presentation queue.
189 */
190 VdpStatus
191 vlVdpPresentationQueueDisplay(VdpPresentationQueue presentation_queue,
192 VdpOutputSurface surface,
193 uint32_t clip_width,
194 uint32_t clip_height,
195 VdpTime earliest_presentation_time)
196 {
197 static int dump_window = -1;
198
199 vlVdpPresentationQueue *pq;
200 vlVdpOutputSurface *surf;
201
202 struct pipe_context *pipe;
203 struct pipe_resource *tex;
204 struct pipe_surface surf_templ, *surf_draw;
205 struct u_rect src_rect, dst_clip, *dirty_area;
206
207 struct vl_compositor *compositor;
208 struct vl_compositor_state *cstate;
209
210 pq = vlGetDataHTAB(presentation_queue);
211 if (!pq)
212 return VDP_STATUS_INVALID_HANDLE;
213
214
215 pipe = pq->device->context;
216 compositor = &pq->device->compositor;
217 cstate = &pq->cstate;
218
219 tex = vl_screen_texture_from_drawable(pq->device->vscreen, pq->drawable);
220 if (!tex)
221 return VDP_STATUS_INVALID_HANDLE;
222
223 dirty_area = vl_screen_get_dirty_area(pq->device->vscreen);
224
225 memset(&surf_templ, 0, sizeof(surf_templ));
226 surf_templ.format = tex->format;
227 surf_templ.usage = PIPE_BIND_RENDER_TARGET;
228 surf_draw = pipe->create_surface(pipe, tex, &surf_templ);
229
230 surf = vlGetDataHTAB(surface);
231 if (!surf)
232 return VDP_STATUS_INVALID_HANDLE;
233
234 surf->timestamp = (vlVdpTime)earliest_presentation_time;
235
236 dst_clip.x0 = 0;
237 dst_clip.y0 = 0;
238 dst_clip.x1 = clip_width ? clip_width : surf_draw->width;
239 dst_clip.y1 = clip_height ? clip_height : surf_draw->height;
240
241 if (pq->device->delayed_rendering.surface == surface &&
242 dst_clip.x1 == surf_draw->width && dst_clip.y1 == surf_draw->height) {
243
244 // TODO: we correctly support the clipping here, but not the pq background color in the clipped area....
245 cstate = pq->device->delayed_rendering.cstate;
246 vl_compositor_set_dst_clip(cstate, &dst_clip);
247 vlVdpResolveDelayedRendering(pq->device, surf_draw, dirty_area);
248
249 } else {
250 vlVdpResolveDelayedRendering(pq->device, NULL, NULL);
251
252 src_rect.x0 = 0;
253 src_rect.y0 = 0;
254 src_rect.x1 = surf_draw->width;
255 src_rect.y1 = surf_draw->height;
256
257 vl_compositor_clear_layers(cstate);
258 vl_compositor_set_rgba_layer(cstate, compositor, 0, surf->sampler_view, &src_rect, NULL, NULL);
259 vl_compositor_set_dst_clip(cstate, &dst_clip);
260 vl_compositor_render(cstate, compositor, surf_draw, dirty_area);
261 }
262
263 pipe->screen->flush_frontbuffer
264 (
265 pipe->screen, tex, 0, 0,
266 vl_screen_get_private(pq->device->vscreen)
267 );
268
269 pipe->screen->fence_reference(pipe->screen, &surf->fence, NULL);
270 pipe->flush(pipe, &surf->fence);
271
272 if (dump_window == -1) {
273 dump_window = debug_get_num_option("VDPAU_DUMP", 0);
274 }
275
276 if (dump_window) {
277 static unsigned int framenum = 0;
278 char cmd[256];
279
280 sprintf(cmd, "xwd -id %d -out vdpau_frame_%08d.xwd", (int)pq->drawable, ++framenum);
281 if (system(cmd) != 0)
282 VDPAU_MSG(VDPAU_ERR, "[VDPAU] Dumping surface %d failed.\n", surface);
283 }
284
285 pipe_resource_reference(&tex, NULL);
286 pipe_surface_reference(&surf_draw, NULL);
287
288 return VDP_STATUS_OK;
289 }
290
291 /**
292 * Wait for a surface to finish being displayed.
293 */
294 VdpStatus
295 vlVdpPresentationQueueBlockUntilSurfaceIdle(VdpPresentationQueue presentation_queue,
296 VdpOutputSurface surface,
297 VdpTime *first_presentation_time)
298 {
299 vlVdpPresentationQueue *pq;
300 vlVdpOutputSurface *surf;
301 struct pipe_screen *screen;
302
303 if (!first_presentation_time)
304 return VDP_STATUS_INVALID_POINTER;
305
306 pq = vlGetDataHTAB(presentation_queue);
307 if (!pq)
308 return VDP_STATUS_INVALID_HANDLE;
309
310 surf = vlGetDataHTAB(surface);
311 if (!surf)
312 return VDP_STATUS_INVALID_HANDLE;
313
314 if (surf->fence) {
315 screen = pq->device->vscreen->pscreen;
316 screen->fence_finish(screen, surf->fence, 0);
317 }
318
319 // We actually need to query the timestamp of the last VSYNC event from the hardware
320 vlVdpPresentationQueueGetTime(presentation_queue, first_presentation_time);
321
322 return VDP_STATUS_OK;
323 }
324
325 /**
326 * Poll the current queue status of a surface.
327 */
328 VdpStatus
329 vlVdpPresentationQueueQuerySurfaceStatus(VdpPresentationQueue presentation_queue,
330 VdpOutputSurface surface,
331 VdpPresentationQueueStatus *status,
332 VdpTime *first_presentation_time)
333 {
334 vlVdpPresentationQueue *pq;
335 vlVdpOutputSurface *surf;
336 struct pipe_screen *screen;
337
338 if (!(status && first_presentation_time))
339 return VDP_STATUS_INVALID_POINTER;
340
341 pq = vlGetDataHTAB(presentation_queue);
342 if (!pq)
343 return VDP_STATUS_INVALID_HANDLE;
344
345 surf = vlGetDataHTAB(surface);
346 if (!surf)
347 return VDP_STATUS_INVALID_HANDLE;
348
349 *first_presentation_time = 0;
350
351 if (!surf->fence) {
352 *status = VDP_PRESENTATION_QUEUE_STATUS_IDLE;
353 } else {
354 screen = pq->device->vscreen->pscreen;
355 if (screen->fence_signalled(screen, surf->fence)) {
356 screen->fence_reference(screen, &surf->fence, NULL);
357 *status = VDP_PRESENTATION_QUEUE_STATUS_VISIBLE;
358
359 // We actually need to query the timestamp of the last VSYNC event from the hardware
360 vlVdpPresentationQueueGetTime(presentation_queue, first_presentation_time);
361 *first_presentation_time += 1;
362 } else {
363 *status = VDP_PRESENTATION_QUEUE_STATUS_QUEUED;
364 }
365 }
366
367 return VDP_STATUS_OK;
368 }