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