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 */
39VdpStatus
40vlVdpPresentationQueueCreate(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   mtx_lock(&dev->mutex);
69   if (!vl_compositor_init_state(&pq->cstate, dev->context)) {
70      mtx_unlock(&dev->mutex);
71      ret = VDP_STATUS_ERROR;
72      goto no_compositor;
73   }
74   mtx_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
84no_handle:
85no_compositor:
86   DeviceReference(&pq->device, NULL);
87   FREE(pq);
88   return ret;
89}
90
91/**
92 * Destroy a VdpPresentationQueue.
93 */
94VdpStatus
95vlVdpPresentationQueueDestroy(VdpPresentationQueue presentation_queue)
96{
97   vlVdpPresentationQueue *pq;
98
99   pq = vlGetDataHTAB(presentation_queue);
100   if (!pq)
101      return VDP_STATUS_INVALID_HANDLE;
102
103   mtx_lock(&pq->device->mutex);
104   vl_compositor_cleanup_state(&pq->cstate);
105   mtx_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 */
117VdpStatus
118vlVdpPresentationQueueSetBackgroundColor(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   mtx_lock(&pq->device->mutex);
137   vl_compositor_set_clear_color(&pq->cstate, &color);
138   mtx_unlock(&pq->device->mutex);
139
140   return VDP_STATUS_OK;
141}
142
143/**
144 * Retrieve the current background color setting.
145 */
146VdpStatus
147vlVdpPresentationQueueGetBackgroundColor(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   mtx_lock(&pq->device->mutex);
161   vl_compositor_get_clear_color(&pq->cstate, &color);
162   mtx_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 */
175VdpStatus
176vlVdpPresentationQueueGetTime(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   mtx_lock(&pq->device->mutex);
189   *current_time = pq->device->vscreen->get_timestamp(pq->device->vscreen,
190                                                      (void *)pq->drawable);
191   mtx_unlock(&pq->device->mutex);
192
193   return VDP_STATUS_OK;
194}
195
196/**
197 * Enter a surface into the presentation queue.
198 */
199VdpStatus
200vlVdpPresentationQueueDisplay(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 = NULL;
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   mtx_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      mtx_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, pipe, 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   mtx_unlock(&pq->device->mutex);
297
298   return VDP_STATUS_OK;
299}
300
301/**
302 * Wait for a surface to finish being displayed.
303 */
304VdpStatus
305vlVdpPresentationQueueBlockUntilSurfaceIdle(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   mtx_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   mtx_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 */
338VdpStatus
339vlVdpPresentationQueueQuerySurfaceStatus(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      mtx_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         mtx_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         mtx_unlock(&pq->device->mutex);
380      }
381   }
382
383   return VDP_STATUS_OK;
384}
385