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