17ec681f3Smrg/**************************************************************************
27ec681f3Smrg *
37ec681f3Smrg * Copyright 2010 Thomas Balling Sørensen.
47ec681f3Smrg * All Rights Reserved.
57ec681f3Smrg *
67ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
77ec681f3Smrg * copy of this software and associated documentation files (the
87ec681f3Smrg * "Software"), to deal in the Software without restriction, including
97ec681f3Smrg * without limitation the rights to use, copy, modify, merge, publish,
107ec681f3Smrg * distribute, sub license, and/or sell copies of the Software, and to
117ec681f3Smrg * permit persons to whom the Software is furnished to do so, subject to
127ec681f3Smrg * the following conditions:
137ec681f3Smrg *
147ec681f3Smrg * The above copyright notice and this permission notice (including the
157ec681f3Smrg * next paragraph) shall be included in all copies or substantial portions
167ec681f3Smrg * of the Software.
177ec681f3Smrg *
187ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
197ec681f3Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
207ec681f3Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
217ec681f3Smrg * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
227ec681f3Smrg * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
237ec681f3Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
247ec681f3Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
257ec681f3Smrg *
267ec681f3Smrg **************************************************************************/
277ec681f3Smrg
287ec681f3Smrg#include <vdpau/vdpau.h>
297ec681f3Smrg
307ec681f3Smrg#include "util/u_memory.h"
317ec681f3Smrg#include "util/u_debug.h"
327ec681f3Smrg
337ec681f3Smrg#include "vl/vl_csc.h"
347ec681f3Smrg
357ec681f3Smrg#include "vdpau_private.h"
367ec681f3Smrg
377ec681f3Smrg/**
387ec681f3Smrg * Create a VdpVideoMixer.
397ec681f3Smrg */
407ec681f3SmrgVdpStatus
417ec681f3SmrgvlVdpVideoMixerCreate(VdpDevice device,
427ec681f3Smrg                      uint32_t feature_count,
437ec681f3Smrg                      VdpVideoMixerFeature const *features,
447ec681f3Smrg                      uint32_t parameter_count,
457ec681f3Smrg                      VdpVideoMixerParameter const *parameters,
467ec681f3Smrg                      void const *const *parameter_values,
477ec681f3Smrg                      VdpVideoMixer *mixer)
487ec681f3Smrg{
497ec681f3Smrg   vlVdpVideoMixer *vmixer = NULL;
507ec681f3Smrg   VdpStatus ret;
517ec681f3Smrg   struct pipe_screen *screen;
527ec681f3Smrg   unsigned max_size, i;
537ec681f3Smrg
547ec681f3Smrg   vlVdpDevice *dev = vlGetDataHTAB(device);
557ec681f3Smrg   if (!dev)
567ec681f3Smrg      return VDP_STATUS_INVALID_HANDLE;
577ec681f3Smrg   screen = dev->vscreen->pscreen;
587ec681f3Smrg
597ec681f3Smrg   vmixer = CALLOC(1, sizeof(vlVdpVideoMixer));
607ec681f3Smrg   if (!vmixer)
617ec681f3Smrg      return VDP_STATUS_RESOURCES;
627ec681f3Smrg
637ec681f3Smrg   DeviceReference(&vmixer->device, dev);
647ec681f3Smrg
657ec681f3Smrg   mtx_lock(&dev->mutex);
667ec681f3Smrg
677ec681f3Smrg   if (!vl_compositor_init_state(&vmixer->cstate, dev->context)) {
687ec681f3Smrg      ret = VDP_STATUS_ERROR;
697ec681f3Smrg      goto no_compositor_state;
707ec681f3Smrg   }
717ec681f3Smrg
727ec681f3Smrg   vl_csc_get_matrix(VL_CSC_COLOR_STANDARD_BT_601, NULL, true, &vmixer->csc);
737ec681f3Smrg   if (!debug_get_bool_option("G3DVL_NO_CSC", FALSE)) {
747ec681f3Smrg      if (!vl_compositor_set_csc_matrix(&vmixer->cstate, (const vl_csc_matrix *)&vmixer->csc, 1.0f, 0.0f)) {
757ec681f3Smrg         ret = VDP_STATUS_ERROR;
767ec681f3Smrg         goto err_csc_matrix;
777ec681f3Smrg      }
787ec681f3Smrg   }
797ec681f3Smrg
807ec681f3Smrg   *mixer = vlAddDataHTAB(vmixer);
817ec681f3Smrg   if (*mixer == 0) {
827ec681f3Smrg      ret = VDP_STATUS_ERROR;
837ec681f3Smrg      goto no_handle;
847ec681f3Smrg   }
857ec681f3Smrg
867ec681f3Smrg   ret = VDP_STATUS_INVALID_VIDEO_MIXER_FEATURE;
877ec681f3Smrg   for (i = 0; i < feature_count; ++i) {
887ec681f3Smrg      switch (features[i]) {
897ec681f3Smrg      /* they are valid, but we doesn't support them */
907ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL:
917ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2:
927ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3:
937ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4:
947ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5:
957ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6:
967ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7:
977ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8:
987ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9:
997ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE:
1007ec681f3Smrg         break;
1017ec681f3Smrg
1027ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL:
1037ec681f3Smrg         vmixer->deint.supported = true;
1047ec681f3Smrg         break;
1057ec681f3Smrg
1067ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_SHARPNESS:
1077ec681f3Smrg         vmixer->sharpness.supported = true;
1087ec681f3Smrg         break;
1097ec681f3Smrg
1107ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION:
1117ec681f3Smrg         vmixer->noise_reduction.supported = true;
1127ec681f3Smrg         break;
1137ec681f3Smrg
1147ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_LUMA_KEY:
1157ec681f3Smrg         vmixer->luma_key.supported = true;
1167ec681f3Smrg         break;
1177ec681f3Smrg
1187ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1:
1197ec681f3Smrg         vmixer->bicubic.supported = true;
1207ec681f3Smrg         break;
1217ec681f3Smrg      default: goto no_params;
1227ec681f3Smrg      }
1237ec681f3Smrg   }
1247ec681f3Smrg
1257ec681f3Smrg   vmixer->chroma_format = PIPE_VIDEO_CHROMA_FORMAT_420;
1267ec681f3Smrg   ret = VDP_STATUS_INVALID_VIDEO_MIXER_PARAMETER;
1277ec681f3Smrg   for (i = 0; i < parameter_count; ++i) {
1287ec681f3Smrg      switch (parameters[i]) {
1297ec681f3Smrg      case VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH:
1307ec681f3Smrg         vmixer->video_width = *(uint32_t*)parameter_values[i];
1317ec681f3Smrg         break;
1327ec681f3Smrg      case VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT:
1337ec681f3Smrg         vmixer->video_height = *(uint32_t*)parameter_values[i];
1347ec681f3Smrg         break;
1357ec681f3Smrg      case VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE:
1367ec681f3Smrg         vmixer->chroma_format = ChromaToPipe(*(VdpChromaType*)parameter_values[i]);
1377ec681f3Smrg         break;
1387ec681f3Smrg      case VDP_VIDEO_MIXER_PARAMETER_LAYERS:
1397ec681f3Smrg         vmixer->max_layers = *(uint32_t*)parameter_values[i];
1407ec681f3Smrg         break;
1417ec681f3Smrg      default: goto no_params;
1427ec681f3Smrg      }
1437ec681f3Smrg   }
1447ec681f3Smrg   ret = VDP_STATUS_INVALID_VALUE;
1457ec681f3Smrg   if (vmixer->max_layers > 4) {
1467ec681f3Smrg      VDPAU_MSG(VDPAU_WARN, "[VDPAU] Max layers %u > 4 not supported\n", vmixer->max_layers);
1477ec681f3Smrg      goto no_params;
1487ec681f3Smrg   }
1497ec681f3Smrg
1507ec681f3Smrg   max_size = screen->get_param(screen, PIPE_CAP_MAX_TEXTURE_2D_SIZE);
1517ec681f3Smrg   if (vmixer->video_width < 48 || vmixer->video_width > max_size) {
1527ec681f3Smrg      VDPAU_MSG(VDPAU_WARN, "[VDPAU] 48 < %u < %u not valid for width\n",
1537ec681f3Smrg                vmixer->video_width, max_size);
1547ec681f3Smrg      goto no_params;
1557ec681f3Smrg   }
1567ec681f3Smrg   if (vmixer->video_height < 48 || vmixer->video_height > max_size) {
1577ec681f3Smrg      VDPAU_MSG(VDPAU_WARN, "[VDPAU] 48 < %u < %u  not valid for height\n",
1587ec681f3Smrg                vmixer->video_height, max_size);
1597ec681f3Smrg      goto no_params;
1607ec681f3Smrg   }
1617ec681f3Smrg   vmixer->luma_key.luma_min = 1.0f;
1627ec681f3Smrg   vmixer->luma_key.luma_max = 0.0f;
1637ec681f3Smrg   mtx_unlock(&dev->mutex);
1647ec681f3Smrg
1657ec681f3Smrg   return VDP_STATUS_OK;
1667ec681f3Smrg
1677ec681f3Smrgno_params:
1687ec681f3Smrg   vlRemoveDataHTAB(*mixer);
1697ec681f3Smrg
1707ec681f3Smrgno_handle:
1717ec681f3Smrgerr_csc_matrix:
1727ec681f3Smrg   vl_compositor_cleanup_state(&vmixer->cstate);
1737ec681f3Smrgno_compositor_state:
1747ec681f3Smrg   mtx_unlock(&dev->mutex);
1757ec681f3Smrg   DeviceReference(&vmixer->device, NULL);
1767ec681f3Smrg   FREE(vmixer);
1777ec681f3Smrg   return ret;
1787ec681f3Smrg}
1797ec681f3Smrg
1807ec681f3Smrg/**
1817ec681f3Smrg * Destroy a VdpVideoMixer.
1827ec681f3Smrg */
1837ec681f3SmrgVdpStatus
1847ec681f3SmrgvlVdpVideoMixerDestroy(VdpVideoMixer mixer)
1857ec681f3Smrg{
1867ec681f3Smrg   vlVdpVideoMixer *vmixer;
1877ec681f3Smrg
1887ec681f3Smrg   vmixer = vlGetDataHTAB(mixer);
1897ec681f3Smrg   if (!vmixer)
1907ec681f3Smrg      return VDP_STATUS_INVALID_HANDLE;
1917ec681f3Smrg
1927ec681f3Smrg   mtx_lock(&vmixer->device->mutex);
1937ec681f3Smrg
1947ec681f3Smrg   vlRemoveDataHTAB(mixer);
1957ec681f3Smrg
1967ec681f3Smrg   vl_compositor_cleanup_state(&vmixer->cstate);
1977ec681f3Smrg
1987ec681f3Smrg   if (vmixer->deint.filter) {
1997ec681f3Smrg      vl_deint_filter_cleanup(vmixer->deint.filter);
2007ec681f3Smrg      FREE(vmixer->deint.filter);
2017ec681f3Smrg   }
2027ec681f3Smrg
2037ec681f3Smrg   if (vmixer->noise_reduction.filter) {
2047ec681f3Smrg      vl_median_filter_cleanup(vmixer->noise_reduction.filter);
2057ec681f3Smrg      FREE(vmixer->noise_reduction.filter);
2067ec681f3Smrg   }
2077ec681f3Smrg
2087ec681f3Smrg   if (vmixer->sharpness.filter) {
2097ec681f3Smrg      vl_matrix_filter_cleanup(vmixer->sharpness.filter);
2107ec681f3Smrg      FREE(vmixer->sharpness.filter);
2117ec681f3Smrg   }
2127ec681f3Smrg
2137ec681f3Smrg   if (vmixer->bicubic.filter) {
2147ec681f3Smrg      vl_bicubic_filter_cleanup(vmixer->bicubic.filter);
2157ec681f3Smrg      FREE(vmixer->bicubic.filter);
2167ec681f3Smrg   }
2177ec681f3Smrg   mtx_unlock(&vmixer->device->mutex);
2187ec681f3Smrg   DeviceReference(&vmixer->device, NULL);
2197ec681f3Smrg
2207ec681f3Smrg   FREE(vmixer);
2217ec681f3Smrg
2227ec681f3Smrg   return VDP_STATUS_OK;
2237ec681f3Smrg}
2247ec681f3Smrg
2257ec681f3Smrg/**
2267ec681f3Smrg * Perform a video post-processing and compositing operation.
2277ec681f3Smrg */
2287ec681f3SmrgVdpStatus vlVdpVideoMixerRender(VdpVideoMixer mixer,
2297ec681f3Smrg                                VdpOutputSurface background_surface,
2307ec681f3Smrg                                VdpRect const *background_source_rect,
2317ec681f3Smrg                                VdpVideoMixerPictureStructure current_picture_structure,
2327ec681f3Smrg                                uint32_t video_surface_past_count,
2337ec681f3Smrg                                VdpVideoSurface const *video_surface_past,
2347ec681f3Smrg                                VdpVideoSurface video_surface_current,
2357ec681f3Smrg                                uint32_t video_surface_future_count,
2367ec681f3Smrg                                VdpVideoSurface const *video_surface_future,
2377ec681f3Smrg                                VdpRect const *video_source_rect,
2387ec681f3Smrg                                VdpOutputSurface destination_surface,
2397ec681f3Smrg                                VdpRect const *destination_rect,
2407ec681f3Smrg                                VdpRect const *destination_video_rect,
2417ec681f3Smrg                                uint32_t layer_count,
2427ec681f3Smrg                                VdpLayer const *layers)
2437ec681f3Smrg{
2447ec681f3Smrg   enum vl_compositor_deinterlace deinterlace;
2457ec681f3Smrg   struct u_rect rect, clip, *prect, dirty_area;
2467ec681f3Smrg   unsigned i, layer = 0;
2477ec681f3Smrg   struct pipe_video_buffer *video_buffer;
2487ec681f3Smrg   struct pipe_sampler_view *sampler_view, sv_templ;
2497ec681f3Smrg   struct pipe_surface *surface, surf_templ;
2507ec681f3Smrg   struct pipe_context *pipe = NULL;
2517ec681f3Smrg   struct pipe_resource res_tmpl, *res;
2527ec681f3Smrg
2537ec681f3Smrg   vlVdpVideoMixer *vmixer;
2547ec681f3Smrg   vlVdpSurface *surf;
2557ec681f3Smrg   vlVdpOutputSurface *dst, *bg = NULL;
2567ec681f3Smrg
2577ec681f3Smrg   struct vl_compositor *compositor;
2587ec681f3Smrg
2597ec681f3Smrg   vmixer = vlGetDataHTAB(mixer);
2607ec681f3Smrg   if (!vmixer)
2617ec681f3Smrg      return VDP_STATUS_INVALID_HANDLE;
2627ec681f3Smrg
2637ec681f3Smrg   compositor = &vmixer->device->compositor;
2647ec681f3Smrg
2657ec681f3Smrg   surf = vlGetDataHTAB(video_surface_current);
2667ec681f3Smrg   if (!surf)
2677ec681f3Smrg      return VDP_STATUS_INVALID_HANDLE;
2687ec681f3Smrg   video_buffer = surf->video_buffer;
2697ec681f3Smrg
2707ec681f3Smrg   if (surf->device != vmixer->device)
2717ec681f3Smrg      return VDP_STATUS_HANDLE_DEVICE_MISMATCH;
2727ec681f3Smrg
2737ec681f3Smrg   if (vmixer->video_width > video_buffer->width ||
2747ec681f3Smrg       vmixer->video_height > video_buffer->height ||
2757ec681f3Smrg       vmixer->chroma_format != pipe_format_to_chroma_format(video_buffer->buffer_format))
2767ec681f3Smrg      return VDP_STATUS_INVALID_SIZE;
2777ec681f3Smrg
2787ec681f3Smrg   if (layer_count > vmixer->max_layers)
2797ec681f3Smrg      return VDP_STATUS_INVALID_VALUE;
2807ec681f3Smrg
2817ec681f3Smrg   dst = vlGetDataHTAB(destination_surface);
2827ec681f3Smrg   if (!dst)
2837ec681f3Smrg      return VDP_STATUS_INVALID_HANDLE;
2847ec681f3Smrg
2857ec681f3Smrg   if (background_surface != VDP_INVALID_HANDLE) {
2867ec681f3Smrg      bg = vlGetDataHTAB(background_surface);
2877ec681f3Smrg      if (!bg)
2887ec681f3Smrg         return VDP_STATUS_INVALID_HANDLE;
2897ec681f3Smrg   }
2907ec681f3Smrg
2917ec681f3Smrg   mtx_lock(&vmixer->device->mutex);
2927ec681f3Smrg
2937ec681f3Smrg   vl_compositor_clear_layers(&vmixer->cstate);
2947ec681f3Smrg
2957ec681f3Smrg   if (bg)
2967ec681f3Smrg      vl_compositor_set_rgba_layer(&vmixer->cstate, compositor, layer++, bg->sampler_view,
2977ec681f3Smrg                                   RectToPipe(background_source_rect, &rect), NULL, NULL);
2987ec681f3Smrg
2997ec681f3Smrg   switch (current_picture_structure) {
3007ec681f3Smrg   case VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD:
3017ec681f3Smrg      deinterlace = VL_COMPOSITOR_BOB_TOP;
3027ec681f3Smrg      break;
3037ec681f3Smrg
3047ec681f3Smrg   case VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD:
3057ec681f3Smrg      deinterlace = VL_COMPOSITOR_BOB_BOTTOM;
3067ec681f3Smrg      break;
3077ec681f3Smrg
3087ec681f3Smrg   case VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME:
3097ec681f3Smrg      deinterlace = VL_COMPOSITOR_WEAVE;
3107ec681f3Smrg      break;
3117ec681f3Smrg
3127ec681f3Smrg   default:
3137ec681f3Smrg      mtx_unlock(&vmixer->device->mutex);
3147ec681f3Smrg      return VDP_STATUS_INVALID_VIDEO_MIXER_PICTURE_STRUCTURE;
3157ec681f3Smrg   }
3167ec681f3Smrg
3177ec681f3Smrg   if (deinterlace != VL_COMPOSITOR_WEAVE && vmixer->deint.enabled &&
3187ec681f3Smrg       video_surface_past_count > 1 && video_surface_future_count > 0) {
3197ec681f3Smrg      vlVdpSurface *prevprev = vlGetDataHTAB(video_surface_past[1]);
3207ec681f3Smrg      vlVdpSurface *prev = vlGetDataHTAB(video_surface_past[0]);
3217ec681f3Smrg      vlVdpSurface *next = vlGetDataHTAB(video_surface_future[0]);
3227ec681f3Smrg      if (prevprev && prev && next &&
3237ec681f3Smrg          vl_deint_filter_check_buffers(vmixer->deint.filter,
3247ec681f3Smrg          prevprev->video_buffer, prev->video_buffer, surf->video_buffer, next->video_buffer)) {
3257ec681f3Smrg         vl_deint_filter_render(vmixer->deint.filter, prevprev->video_buffer,
3267ec681f3Smrg                                prev->video_buffer, surf->video_buffer,
3277ec681f3Smrg                                next->video_buffer,
3287ec681f3Smrg                                deinterlace == VL_COMPOSITOR_BOB_BOTTOM);
3297ec681f3Smrg         deinterlace = VL_COMPOSITOR_WEAVE;
3307ec681f3Smrg         video_buffer = vmixer->deint.filter->video_buffer;
3317ec681f3Smrg      }
3327ec681f3Smrg   }
3337ec681f3Smrg
3347ec681f3Smrg   if (!destination_video_rect)
3357ec681f3Smrg      destination_video_rect = video_source_rect;
3367ec681f3Smrg
3377ec681f3Smrg   prect = RectToPipe(video_source_rect, &rect);
3387ec681f3Smrg   if (!prect) {
3397ec681f3Smrg      rect.x0 = 0;
3407ec681f3Smrg      rect.y0 = 0;
3417ec681f3Smrg      rect.x1 = surf->templat.width;
3427ec681f3Smrg      rect.y1 = surf->templat.height;
3437ec681f3Smrg      prect = &rect;
3447ec681f3Smrg   }
3457ec681f3Smrg   vl_compositor_set_buffer_layer(&vmixer->cstate, compositor, layer, video_buffer, prect, NULL, deinterlace);
3467ec681f3Smrg
3477ec681f3Smrg   if (vmixer->bicubic.filter || vmixer->sharpness.filter || vmixer->noise_reduction.filter) {
3487ec681f3Smrg      pipe = vmixer->device->context;
3497ec681f3Smrg      memset(&res_tmpl, 0, sizeof(res_tmpl));
3507ec681f3Smrg
3517ec681f3Smrg      res_tmpl.target = PIPE_TEXTURE_2D;
3527ec681f3Smrg      res_tmpl.format = dst->sampler_view->format;
3537ec681f3Smrg      res_tmpl.depth0 = 1;
3547ec681f3Smrg      res_tmpl.array_size = 1;
3557ec681f3Smrg      res_tmpl.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET;
3567ec681f3Smrg      res_tmpl.usage = PIPE_USAGE_DEFAULT;
3577ec681f3Smrg
3587ec681f3Smrg      if (!vmixer->bicubic.filter) {
3597ec681f3Smrg         res_tmpl.width0 = dst->surface->width;
3607ec681f3Smrg         res_tmpl.height0 = dst->surface->height;
3617ec681f3Smrg      } else {
3627ec681f3Smrg         res_tmpl.width0 = surf->templat.width;
3637ec681f3Smrg         res_tmpl.height0 = surf->templat.height;
3647ec681f3Smrg      }
3657ec681f3Smrg
3667ec681f3Smrg      res = pipe->screen->resource_create(pipe->screen, &res_tmpl);
3677ec681f3Smrg
3687ec681f3Smrg      vlVdpDefaultSamplerViewTemplate(&sv_templ, res);
3697ec681f3Smrg      sampler_view = pipe->create_sampler_view(pipe, res, &sv_templ);
3707ec681f3Smrg
3717ec681f3Smrg      memset(&surf_templ, 0, sizeof(surf_templ));
3727ec681f3Smrg      surf_templ.format = res->format;
3737ec681f3Smrg      surface = pipe->create_surface(pipe, res, &surf_templ);
3747ec681f3Smrg
3757ec681f3Smrg      vl_compositor_reset_dirty_area(&dirty_area);
3767ec681f3Smrg      pipe_resource_reference(&res, NULL);
3777ec681f3Smrg   } else {
3787ec681f3Smrg      surface = dst->surface;
3797ec681f3Smrg      sampler_view = dst->sampler_view;
3807ec681f3Smrg      dirty_area = dst->dirty_area;
3817ec681f3Smrg   }
3827ec681f3Smrg
3837ec681f3Smrg   if (!vmixer->bicubic.filter) {
3847ec681f3Smrg      vl_compositor_set_layer_dst_area(&vmixer->cstate, layer++, RectToPipe(destination_video_rect, &rect));
3857ec681f3Smrg      vl_compositor_set_dst_clip(&vmixer->cstate, RectToPipe(destination_rect, &clip));
3867ec681f3Smrg   }
3877ec681f3Smrg
3887ec681f3Smrg   for (i = 0; i < layer_count; ++i) {
3897ec681f3Smrg      vlVdpOutputSurface *src = vlGetDataHTAB(layers->source_surface);
3907ec681f3Smrg      if (!src) {
3917ec681f3Smrg         mtx_unlock(&vmixer->device->mutex);
3927ec681f3Smrg         return VDP_STATUS_INVALID_HANDLE;
3937ec681f3Smrg      }
3947ec681f3Smrg
3957ec681f3Smrg      assert(layers->struct_version == VDP_LAYER_VERSION);
3967ec681f3Smrg
3977ec681f3Smrg      vl_compositor_set_rgba_layer(&vmixer->cstate, compositor, layer, src->sampler_view,
3987ec681f3Smrg                                   RectToPipe(layers->source_rect, &rect), NULL, NULL);
3997ec681f3Smrg      vl_compositor_set_layer_dst_area(&vmixer->cstate, layer++, RectToPipe(layers->destination_rect, &rect));
4007ec681f3Smrg
4017ec681f3Smrg      ++layers;
4027ec681f3Smrg   }
4037ec681f3Smrg
4047ec681f3Smrg   vl_compositor_render(&vmixer->cstate, compositor, surface, &dirty_area, true);
4057ec681f3Smrg
4067ec681f3Smrg   if (vmixer->noise_reduction.filter) {
4077ec681f3Smrg      if (!vmixer->sharpness.filter && !vmixer->bicubic.filter) {
4087ec681f3Smrg         vl_median_filter_render(vmixer->noise_reduction.filter,
4097ec681f3Smrg                                 sampler_view, dst->surface);
4107ec681f3Smrg      } else {
4117ec681f3Smrg         res = pipe->screen->resource_create(pipe->screen, &res_tmpl);
4127ec681f3Smrg         struct pipe_sampler_view *sampler_view_temp = pipe->create_sampler_view(pipe, res, &sv_templ);
4137ec681f3Smrg         struct pipe_surface *surface_temp = pipe->create_surface(pipe, res, &surf_templ);
4147ec681f3Smrg         pipe_resource_reference(&res, NULL);
4157ec681f3Smrg
4167ec681f3Smrg         vl_median_filter_render(vmixer->noise_reduction.filter,
4177ec681f3Smrg                                 sampler_view, surface_temp);
4187ec681f3Smrg
4197ec681f3Smrg         pipe_sampler_view_reference(&sampler_view, NULL);
4207ec681f3Smrg         pipe_surface_reference(&surface, NULL);
4217ec681f3Smrg
4227ec681f3Smrg         sampler_view = sampler_view_temp;
4237ec681f3Smrg         surface = surface_temp;
4247ec681f3Smrg      }
4257ec681f3Smrg   }
4267ec681f3Smrg
4277ec681f3Smrg   if (vmixer->sharpness.filter) {
4287ec681f3Smrg      if (!vmixer->bicubic.filter) {
4297ec681f3Smrg         vl_matrix_filter_render(vmixer->sharpness.filter,
4307ec681f3Smrg                                 sampler_view, dst->surface);
4317ec681f3Smrg      } else {
4327ec681f3Smrg         res = pipe->screen->resource_create(pipe->screen, &res_tmpl);
4337ec681f3Smrg         struct pipe_sampler_view *sampler_view_temp = pipe->create_sampler_view(pipe, res, &sv_templ);
4347ec681f3Smrg         struct pipe_surface *surface_temp = pipe->create_surface(pipe, res, &surf_templ);
4357ec681f3Smrg         pipe_resource_reference(&res, NULL);
4367ec681f3Smrg
4377ec681f3Smrg         vl_matrix_filter_render(vmixer->sharpness.filter,
4387ec681f3Smrg                                 sampler_view, surface_temp);
4397ec681f3Smrg
4407ec681f3Smrg         pipe_sampler_view_reference(&sampler_view, NULL);
4417ec681f3Smrg         pipe_surface_reference(&surface, NULL);
4427ec681f3Smrg
4437ec681f3Smrg         sampler_view = sampler_view_temp;
4447ec681f3Smrg         surface = surface_temp;
4457ec681f3Smrg      }
4467ec681f3Smrg   }
4477ec681f3Smrg
4487ec681f3Smrg   if (vmixer->bicubic.filter)
4497ec681f3Smrg      vl_bicubic_filter_render(vmixer->bicubic.filter,
4507ec681f3Smrg                               sampler_view, dst->surface,
4517ec681f3Smrg                               RectToPipe(destination_video_rect, &rect),
4527ec681f3Smrg                               RectToPipe(destination_rect, &clip));
4537ec681f3Smrg
4547ec681f3Smrg   if(surface != dst->surface) {
4557ec681f3Smrg      pipe_sampler_view_reference(&sampler_view, NULL);
4567ec681f3Smrg      pipe_surface_reference(&surface, NULL);
4577ec681f3Smrg   }
4587ec681f3Smrg   mtx_unlock(&vmixer->device->mutex);
4597ec681f3Smrg
4607ec681f3Smrg   return VDP_STATUS_OK;
4617ec681f3Smrg}
4627ec681f3Smrg
4637ec681f3Smrgstatic void
4647ec681f3SmrgvlVdpVideoMixerUpdateDeinterlaceFilter(vlVdpVideoMixer *vmixer)
4657ec681f3Smrg{
4667ec681f3Smrg   struct pipe_context *pipe = vmixer->device->context;
4677ec681f3Smrg   assert(vmixer);
4687ec681f3Smrg
4697ec681f3Smrg   /* remove existing filter */
4707ec681f3Smrg   if (vmixer->deint.filter) {
4717ec681f3Smrg      vl_deint_filter_cleanup(vmixer->deint.filter);
4727ec681f3Smrg      FREE(vmixer->deint.filter);
4737ec681f3Smrg      vmixer->deint.filter = NULL;
4747ec681f3Smrg   }
4757ec681f3Smrg
4767ec681f3Smrg   /* create a new filter if requested */
4777ec681f3Smrg   if (vmixer->deint.enabled && vmixer->chroma_format == PIPE_VIDEO_CHROMA_FORMAT_420) {
4787ec681f3Smrg      vmixer->deint.filter = MALLOC(sizeof(struct vl_deint_filter));
4797ec681f3Smrg      vmixer->deint.enabled = vl_deint_filter_init(vmixer->deint.filter, pipe,
4807ec681f3Smrg            vmixer->video_width, vmixer->video_height,
4817ec681f3Smrg            vmixer->skip_chroma_deint, vmixer->deint.spatial);
4827ec681f3Smrg      if (!vmixer->deint.enabled) {
4837ec681f3Smrg         FREE(vmixer->deint.filter);
4847ec681f3Smrg      }
4857ec681f3Smrg   }
4867ec681f3Smrg}
4877ec681f3Smrg
4887ec681f3Smrg/**
4897ec681f3Smrg * Update the noise reduction setting
4907ec681f3Smrg */
4917ec681f3Smrgstatic void
4927ec681f3SmrgvlVdpVideoMixerUpdateNoiseReductionFilter(vlVdpVideoMixer *vmixer)
4937ec681f3Smrg{
4947ec681f3Smrg   assert(vmixer);
4957ec681f3Smrg
4967ec681f3Smrg   /* if present remove the old filter first */
4977ec681f3Smrg   if (vmixer->noise_reduction.filter) {
4987ec681f3Smrg      vl_median_filter_cleanup(vmixer->noise_reduction.filter);
4997ec681f3Smrg      FREE(vmixer->noise_reduction.filter);
5007ec681f3Smrg      vmixer->noise_reduction.filter = NULL;
5017ec681f3Smrg   }
5027ec681f3Smrg
5037ec681f3Smrg   /* and create a new filter as needed */
5047ec681f3Smrg   if (vmixer->noise_reduction. enabled && vmixer->noise_reduction.level > 0) {
5057ec681f3Smrg      vmixer->noise_reduction.filter = MALLOC(sizeof(struct vl_median_filter));
5067ec681f3Smrg      vl_median_filter_init(vmixer->noise_reduction.filter, vmixer->device->context,
5077ec681f3Smrg                            vmixer->video_width, vmixer->video_height,
5087ec681f3Smrg                            vmixer->noise_reduction.level + 1,
5097ec681f3Smrg                            VL_MEDIAN_FILTER_CROSS);
5107ec681f3Smrg   }
5117ec681f3Smrg}
5127ec681f3Smrg
5137ec681f3Smrgstatic void
5147ec681f3SmrgvlVdpVideoMixerUpdateSharpnessFilter(vlVdpVideoMixer *vmixer)
5157ec681f3Smrg{
5167ec681f3Smrg   assert(vmixer);
5177ec681f3Smrg
5187ec681f3Smrg   /* if present remove the old filter first */
5197ec681f3Smrg   if (vmixer->sharpness.filter) {
5207ec681f3Smrg      vl_matrix_filter_cleanup(vmixer->sharpness.filter);
5217ec681f3Smrg      FREE(vmixer->sharpness.filter);
5227ec681f3Smrg      vmixer->sharpness.filter = NULL;
5237ec681f3Smrg   }
5247ec681f3Smrg
5257ec681f3Smrg   /* and create a new filter as needed */
5267ec681f3Smrg   if (vmixer->sharpness.enabled && vmixer->sharpness.value != 0.0f) {
5277ec681f3Smrg      float matrix[9];
5287ec681f3Smrg      unsigned i;
5297ec681f3Smrg
5307ec681f3Smrg      if (vmixer->sharpness.value > 0.0f) {
5317ec681f3Smrg         matrix[0] = -1.0f; matrix[1] = -1.0f; matrix[2] = -1.0f;
5327ec681f3Smrg         matrix[3] = -1.0f; matrix[4] =  8.0f; matrix[5] = -1.0f;
5337ec681f3Smrg         matrix[6] = -1.0f; matrix[7] = -1.0f; matrix[8] = -1.0f;
5347ec681f3Smrg
5357ec681f3Smrg         for (i = 0; i < 9; ++i)
5367ec681f3Smrg            matrix[i] *= vmixer->sharpness.value;
5377ec681f3Smrg
5387ec681f3Smrg         matrix[4] += 1.0f;
5397ec681f3Smrg
5407ec681f3Smrg      } else {
5417ec681f3Smrg         matrix[0] = 1.0f; matrix[1] = 2.0f; matrix[2] = 1.0f;
5427ec681f3Smrg         matrix[3] = 2.0f; matrix[4] = 4.0f; matrix[5] = 2.0f;
5437ec681f3Smrg         matrix[6] = 1.0f; matrix[7] = 2.0f; matrix[8] = 1.0f;
5447ec681f3Smrg
5457ec681f3Smrg         for (i = 0; i < 9; ++i)
5467ec681f3Smrg               matrix[i] *= fabsf(vmixer->sharpness.value) / 16.0f;
5477ec681f3Smrg
5487ec681f3Smrg         matrix[4] += 1.0f - fabsf(vmixer->sharpness.value);
5497ec681f3Smrg      }
5507ec681f3Smrg
5517ec681f3Smrg      vmixer->sharpness.filter = MALLOC(sizeof(struct vl_matrix_filter));
5527ec681f3Smrg      vl_matrix_filter_init(vmixer->sharpness.filter, vmixer->device->context,
5537ec681f3Smrg                            vmixer->video_width, vmixer->video_height,
5547ec681f3Smrg                            3, 3, matrix);
5557ec681f3Smrg   }
5567ec681f3Smrg}
5577ec681f3Smrg
5587ec681f3Smrg/**
5597ec681f3Smrg * Update the bicubic filter
5607ec681f3Smrg */
5617ec681f3Smrgstatic void
5627ec681f3SmrgvlVdpVideoMixerUpdateBicubicFilter(vlVdpVideoMixer *vmixer)
5637ec681f3Smrg{
5647ec681f3Smrg   assert(vmixer);
5657ec681f3Smrg
5667ec681f3Smrg   /* if present remove the old filter first */
5677ec681f3Smrg   if (vmixer->bicubic.filter) {
5687ec681f3Smrg      vl_bicubic_filter_cleanup(vmixer->bicubic.filter);
5697ec681f3Smrg      FREE(vmixer->bicubic.filter);
5707ec681f3Smrg      vmixer->bicubic.filter = NULL;
5717ec681f3Smrg   }
5727ec681f3Smrg   /* and create a new filter as needed */
5737ec681f3Smrg   if (vmixer->bicubic.enabled) {
5747ec681f3Smrg      vmixer->bicubic.filter = MALLOC(sizeof(struct vl_bicubic_filter));
5757ec681f3Smrg      vl_bicubic_filter_init(vmixer->bicubic.filter, vmixer->device->context,
5767ec681f3Smrg                            vmixer->video_width, vmixer->video_height);
5777ec681f3Smrg   }
5787ec681f3Smrg}
5797ec681f3Smrg
5807ec681f3Smrg/**
5817ec681f3Smrg * Retrieve whether features were requested at creation time.
5827ec681f3Smrg */
5837ec681f3SmrgVdpStatus
5847ec681f3SmrgvlVdpVideoMixerGetFeatureSupport(VdpVideoMixer mixer,
5857ec681f3Smrg                                 uint32_t feature_count,
5867ec681f3Smrg                                 VdpVideoMixerFeature const *features,
5877ec681f3Smrg                                 VdpBool *feature_supports)
5887ec681f3Smrg{
5897ec681f3Smrg   vlVdpVideoMixer *vmixer;
5907ec681f3Smrg   unsigned i;
5917ec681f3Smrg
5927ec681f3Smrg   if (!(features && feature_supports))
5937ec681f3Smrg      return VDP_STATUS_INVALID_POINTER;
5947ec681f3Smrg
5957ec681f3Smrg   vmixer = vlGetDataHTAB(mixer);
5967ec681f3Smrg   if (!vmixer)
5977ec681f3Smrg      return VDP_STATUS_INVALID_HANDLE;
5987ec681f3Smrg
5997ec681f3Smrg   for (i = 0; i < feature_count; ++i) {
6007ec681f3Smrg      switch (features[i]) {
6017ec681f3Smrg      /* they are valid, but we doesn't support them */
6027ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL:
6037ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2:
6047ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3:
6057ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4:
6067ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5:
6077ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6:
6087ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7:
6097ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8:
6107ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9:
6117ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE:
6127ec681f3Smrg         feature_supports[i] = false;
6137ec681f3Smrg         break;
6147ec681f3Smrg
6157ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL:
6167ec681f3Smrg         feature_supports[i] = vmixer->deint.supported;
6177ec681f3Smrg         break;
6187ec681f3Smrg
6197ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_SHARPNESS:
6207ec681f3Smrg         feature_supports[i] = vmixer->sharpness.supported;
6217ec681f3Smrg         break;
6227ec681f3Smrg
6237ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION:
6247ec681f3Smrg         feature_supports[i] = vmixer->noise_reduction.supported;
6257ec681f3Smrg         break;
6267ec681f3Smrg
6277ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_LUMA_KEY:
6287ec681f3Smrg         feature_supports[i] = vmixer->luma_key.supported;
6297ec681f3Smrg         break;
6307ec681f3Smrg
6317ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1:
6327ec681f3Smrg         feature_supports[i] = vmixer->bicubic.supported;
6337ec681f3Smrg         break;
6347ec681f3Smrg
6357ec681f3Smrg      default:
6367ec681f3Smrg         return VDP_STATUS_INVALID_VIDEO_MIXER_FEATURE;
6377ec681f3Smrg      }
6387ec681f3Smrg   }
6397ec681f3Smrg
6407ec681f3Smrg   return VDP_STATUS_OK;
6417ec681f3Smrg}
6427ec681f3Smrg
6437ec681f3Smrg/**
6447ec681f3Smrg * Enable or disable features.
6457ec681f3Smrg */
6467ec681f3SmrgVdpStatus
6477ec681f3SmrgvlVdpVideoMixerSetFeatureEnables(VdpVideoMixer mixer,
6487ec681f3Smrg                                 uint32_t feature_count,
6497ec681f3Smrg                                 VdpVideoMixerFeature const *features,
6507ec681f3Smrg                                 VdpBool const *feature_enables)
6517ec681f3Smrg{
6527ec681f3Smrg   vlVdpVideoMixer *vmixer;
6537ec681f3Smrg   unsigned i;
6547ec681f3Smrg
6557ec681f3Smrg   if (!(features && feature_enables))
6567ec681f3Smrg      return VDP_STATUS_INVALID_POINTER;
6577ec681f3Smrg
6587ec681f3Smrg   vmixer = vlGetDataHTAB(mixer);
6597ec681f3Smrg   if (!vmixer)
6607ec681f3Smrg      return VDP_STATUS_INVALID_HANDLE;
6617ec681f3Smrg
6627ec681f3Smrg   mtx_lock(&vmixer->device->mutex);
6637ec681f3Smrg   for (i = 0; i < feature_count; ++i) {
6647ec681f3Smrg      switch (features[i]) {
6657ec681f3Smrg      /* they are valid, but we doesn't support them */
6667ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL:
6677ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2:
6687ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3:
6697ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4:
6707ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5:
6717ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6:
6727ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7:
6737ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8:
6747ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9:
6757ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE:
6767ec681f3Smrg         break;
6777ec681f3Smrg
6787ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL:
6797ec681f3Smrg         vmixer->deint.enabled = feature_enables[i];
6807ec681f3Smrg         vlVdpVideoMixerUpdateDeinterlaceFilter(vmixer);
6817ec681f3Smrg         break;
6827ec681f3Smrg
6837ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_SHARPNESS:
6847ec681f3Smrg         vmixer->sharpness.enabled = feature_enables[i];
6857ec681f3Smrg         vlVdpVideoMixerUpdateSharpnessFilter(vmixer);
6867ec681f3Smrg         break;
6877ec681f3Smrg
6887ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION:
6897ec681f3Smrg         vmixer->noise_reduction.enabled = feature_enables[i];
6907ec681f3Smrg         vlVdpVideoMixerUpdateNoiseReductionFilter(vmixer);
6917ec681f3Smrg         break;
6927ec681f3Smrg
6937ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_LUMA_KEY:
6947ec681f3Smrg         vmixer->luma_key.enabled = feature_enables[i];
6957ec681f3Smrg         if (!debug_get_bool_option("G3DVL_NO_CSC", FALSE))
6967ec681f3Smrg            if (!vl_compositor_set_csc_matrix(&vmixer->cstate, (const vl_csc_matrix *)&vmixer->csc,
6977ec681f3Smrg                        vmixer->luma_key.luma_min, vmixer->luma_key.luma_max)) {
6987ec681f3Smrg               mtx_unlock(&vmixer->device->mutex);
6997ec681f3Smrg               return VDP_STATUS_ERROR;
7007ec681f3Smrg            }
7017ec681f3Smrg         break;
7027ec681f3Smrg
7037ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1:
7047ec681f3Smrg         vmixer->bicubic.enabled = feature_enables[i];
7057ec681f3Smrg         vlVdpVideoMixerUpdateBicubicFilter(vmixer);
7067ec681f3Smrg         break;
7077ec681f3Smrg
7087ec681f3Smrg      default:
7097ec681f3Smrg         mtx_unlock(&vmixer->device->mutex);
7107ec681f3Smrg         return VDP_STATUS_INVALID_VIDEO_MIXER_FEATURE;
7117ec681f3Smrg      }
7127ec681f3Smrg   }
7137ec681f3Smrg   mtx_unlock(&vmixer->device->mutex);
7147ec681f3Smrg
7157ec681f3Smrg   return VDP_STATUS_OK;
7167ec681f3Smrg}
7177ec681f3Smrg
7187ec681f3Smrg/**
7197ec681f3Smrg * Retrieve whether features are enabled.
7207ec681f3Smrg */
7217ec681f3SmrgVdpStatus
7227ec681f3SmrgvlVdpVideoMixerGetFeatureEnables(VdpVideoMixer mixer,
7237ec681f3Smrg                                 uint32_t feature_count,
7247ec681f3Smrg                                 VdpVideoMixerFeature const *features,
7257ec681f3Smrg                                 VdpBool *feature_enables)
7267ec681f3Smrg{
7277ec681f3Smrg   vlVdpVideoMixer *vmixer;
7287ec681f3Smrg   unsigned i;
7297ec681f3Smrg
7307ec681f3Smrg   if (!(features && feature_enables))
7317ec681f3Smrg      return VDP_STATUS_INVALID_POINTER;
7327ec681f3Smrg
7337ec681f3Smrg   vmixer = vlGetDataHTAB(mixer);
7347ec681f3Smrg   if (!vmixer)
7357ec681f3Smrg      return VDP_STATUS_INVALID_HANDLE;
7367ec681f3Smrg
7377ec681f3Smrg   for (i = 0; i < feature_count; ++i) {
7387ec681f3Smrg      switch (features[i]) {
7397ec681f3Smrg      /* they are valid, but we doesn't support them */
7407ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL:
7417ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL:
7427ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2:
7437ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3:
7447ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4:
7457ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5:
7467ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6:
7477ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7:
7487ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8:
7497ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9:
7507ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE:
7517ec681f3Smrg         break;
7527ec681f3Smrg
7537ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_SHARPNESS:
7547ec681f3Smrg         feature_enables[i] = vmixer->sharpness.enabled;
7557ec681f3Smrg         break;
7567ec681f3Smrg
7577ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION:
7587ec681f3Smrg         feature_enables[i] = vmixer->noise_reduction.enabled;
7597ec681f3Smrg         break;
7607ec681f3Smrg
7617ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_LUMA_KEY:
7627ec681f3Smrg         feature_enables[i] = vmixer->luma_key.enabled;
7637ec681f3Smrg         break;
7647ec681f3Smrg
7657ec681f3Smrg      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1:
7667ec681f3Smrg         feature_enables[i] = vmixer->bicubic.enabled;
7677ec681f3Smrg         break;
7687ec681f3Smrg
7697ec681f3Smrg      default:
7707ec681f3Smrg         return VDP_STATUS_INVALID_VIDEO_MIXER_FEATURE;
7717ec681f3Smrg      }
7727ec681f3Smrg   }
7737ec681f3Smrg
7747ec681f3Smrg   return VDP_STATUS_OK;
7757ec681f3Smrg}
7767ec681f3Smrg
7777ec681f3Smrg/**
7787ec681f3Smrg * Set attribute values.
7797ec681f3Smrg */
7807ec681f3SmrgVdpStatus
7817ec681f3SmrgvlVdpVideoMixerSetAttributeValues(VdpVideoMixer mixer,
7827ec681f3Smrg                                  uint32_t attribute_count,
7837ec681f3Smrg                                  VdpVideoMixerAttribute const *attributes,
7847ec681f3Smrg                                  void const *const *attribute_values)
7857ec681f3Smrg{
7867ec681f3Smrg   const VdpColor *background_color;
7877ec681f3Smrg   union pipe_color_union color;
7887ec681f3Smrg   const float *vdp_csc;
7897ec681f3Smrg   float val;
7907ec681f3Smrg   unsigned i;
7917ec681f3Smrg   VdpStatus ret;
7927ec681f3Smrg
7937ec681f3Smrg   if (!(attributes && attribute_values))
7947ec681f3Smrg      return VDP_STATUS_INVALID_POINTER;
7957ec681f3Smrg
7967ec681f3Smrg   vlVdpVideoMixer *vmixer = vlGetDataHTAB(mixer);
7977ec681f3Smrg   if (!vmixer)
7987ec681f3Smrg      return VDP_STATUS_INVALID_HANDLE;
7997ec681f3Smrg
8007ec681f3Smrg   mtx_lock(&vmixer->device->mutex);
8017ec681f3Smrg   for (i = 0; i < attribute_count; ++i) {
8027ec681f3Smrg      switch (attributes[i]) {
8037ec681f3Smrg      case VDP_VIDEO_MIXER_ATTRIBUTE_BACKGROUND_COLOR:
8047ec681f3Smrg         background_color = attribute_values[i];
8057ec681f3Smrg         color.f[0] = background_color->red;
8067ec681f3Smrg         color.f[1] = background_color->green;
8077ec681f3Smrg         color.f[2] = background_color->blue;
8087ec681f3Smrg         color.f[3] = background_color->alpha;
8097ec681f3Smrg         vl_compositor_set_clear_color(&vmixer->cstate, &color);
8107ec681f3Smrg         break;
8117ec681f3Smrg      case VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX:
8127ec681f3Smrg         vdp_csc = attribute_values[i];
8137ec681f3Smrg         vmixer->custom_csc = !!vdp_csc;
8147ec681f3Smrg         if (!vdp_csc)
8157ec681f3Smrg            vl_csc_get_matrix(VL_CSC_COLOR_STANDARD_BT_601, NULL, 1, &vmixer->csc);
8167ec681f3Smrg         else
8177ec681f3Smrg            memcpy(vmixer->csc, vdp_csc, sizeof(vl_csc_matrix));
8187ec681f3Smrg         if (!debug_get_bool_option("G3DVL_NO_CSC", FALSE))
8197ec681f3Smrg            if (!vl_compositor_set_csc_matrix(&vmixer->cstate, (const vl_csc_matrix *)&vmixer->csc,
8207ec681f3Smrg                                         vmixer->luma_key.luma_min, vmixer->luma_key.luma_max)) {
8217ec681f3Smrg               ret = VDP_STATUS_ERROR;
8227ec681f3Smrg               goto fail;
8237ec681f3Smrg            }
8247ec681f3Smrg         break;
8257ec681f3Smrg
8267ec681f3Smrg      case VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL:
8277ec681f3Smrg
8287ec681f3Smrg         val = *(float*)attribute_values[i];
8297ec681f3Smrg         if (val < 0.0f || val > 1.0f) {
8307ec681f3Smrg            ret = VDP_STATUS_INVALID_VALUE;
8317ec681f3Smrg            goto fail;
8327ec681f3Smrg         }
8337ec681f3Smrg
8347ec681f3Smrg         vmixer->noise_reduction.level = val * 10;
8357ec681f3Smrg         vlVdpVideoMixerUpdateNoiseReductionFilter(vmixer);
8367ec681f3Smrg         break;
8377ec681f3Smrg
8387ec681f3Smrg      case VDP_VIDEO_MIXER_ATTRIBUTE_LUMA_KEY_MIN_LUMA:
8397ec681f3Smrg         val = *(float*)attribute_values[i];
8407ec681f3Smrg         if (val < 0.0f || val > 1.0f) {
8417ec681f3Smrg            ret = VDP_STATUS_INVALID_VALUE;
8427ec681f3Smrg            goto fail;
8437ec681f3Smrg         }
8447ec681f3Smrg         vmixer->luma_key.luma_min = val;
8457ec681f3Smrg         if (!debug_get_bool_option("G3DVL_NO_CSC", FALSE))
8467ec681f3Smrg            if (!vl_compositor_set_csc_matrix(&vmixer->cstate, (const vl_csc_matrix *)&vmixer->csc,
8477ec681f3Smrg                        vmixer->luma_key.luma_min, vmixer->luma_key.luma_max)) {
8487ec681f3Smrg               ret = VDP_STATUS_ERROR;
8497ec681f3Smrg               goto fail;
8507ec681f3Smrg            }
8517ec681f3Smrg         break;
8527ec681f3Smrg
8537ec681f3Smrg      case VDP_VIDEO_MIXER_ATTRIBUTE_LUMA_KEY_MAX_LUMA:
8547ec681f3Smrg         val = *(float*)attribute_values[i];
8557ec681f3Smrg         if (val < 0.0f || val > 1.0f) {
8567ec681f3Smrg            ret = VDP_STATUS_INVALID_VALUE;
8577ec681f3Smrg            goto fail;
8587ec681f3Smrg         }
8597ec681f3Smrg         vmixer->luma_key.luma_max = val;
8607ec681f3Smrg         if (!debug_get_bool_option("G3DVL_NO_CSC", FALSE))
8617ec681f3Smrg            if (!vl_compositor_set_csc_matrix(&vmixer->cstate, (const vl_csc_matrix *)&vmixer->csc,
8627ec681f3Smrg                        vmixer->luma_key.luma_min, vmixer->luma_key.luma_max)) {
8637ec681f3Smrg               ret = VDP_STATUS_ERROR;
8647ec681f3Smrg               goto fail;
8657ec681f3Smrg            }
8667ec681f3Smrg         break;
8677ec681f3Smrg
8687ec681f3Smrg      case VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL:
8697ec681f3Smrg
8707ec681f3Smrg         val = *(float*)attribute_values[i];
8717ec681f3Smrg         if (val < -1.0f || val > 1.0f) {
8727ec681f3Smrg            ret = VDP_STATUS_INVALID_VALUE;
8737ec681f3Smrg            goto fail;
8747ec681f3Smrg         }
8757ec681f3Smrg
8767ec681f3Smrg         vmixer->sharpness.value = val;
8777ec681f3Smrg         vlVdpVideoMixerUpdateSharpnessFilter(vmixer);
8787ec681f3Smrg         break;
8797ec681f3Smrg
8807ec681f3Smrg      case VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE:
8817ec681f3Smrg         if (*(uint8_t*)attribute_values[i] > 1) {
8827ec681f3Smrg            ret = VDP_STATUS_INVALID_VALUE;
8837ec681f3Smrg            goto fail;
8847ec681f3Smrg         }
8857ec681f3Smrg         vmixer->skip_chroma_deint = *(uint8_t*)attribute_values[i];
8867ec681f3Smrg         vlVdpVideoMixerUpdateDeinterlaceFilter(vmixer);
8877ec681f3Smrg         break;
8887ec681f3Smrg      default:
8897ec681f3Smrg         ret = VDP_STATUS_INVALID_VIDEO_MIXER_ATTRIBUTE;
8907ec681f3Smrg         goto fail;
8917ec681f3Smrg      }
8927ec681f3Smrg   }
8937ec681f3Smrg   mtx_unlock(&vmixer->device->mutex);
8947ec681f3Smrg
8957ec681f3Smrg   return VDP_STATUS_OK;
8967ec681f3Smrgfail:
8977ec681f3Smrg   mtx_unlock(&vmixer->device->mutex);
8987ec681f3Smrg   return ret;
8997ec681f3Smrg}
9007ec681f3Smrg
9017ec681f3Smrg/**
9027ec681f3Smrg * Retrieve parameter values given at creation time.
9037ec681f3Smrg */
9047ec681f3SmrgVdpStatus
9057ec681f3SmrgvlVdpVideoMixerGetParameterValues(VdpVideoMixer mixer,
9067ec681f3Smrg                                  uint32_t parameter_count,
9077ec681f3Smrg                                  VdpVideoMixerParameter const *parameters,
9087ec681f3Smrg                                  void *const *parameter_values)
9097ec681f3Smrg{
9107ec681f3Smrg   vlVdpVideoMixer *vmixer = vlGetDataHTAB(mixer);
9117ec681f3Smrg   unsigned i;
9127ec681f3Smrg   if (!vmixer)
9137ec681f3Smrg      return VDP_STATUS_INVALID_HANDLE;
9147ec681f3Smrg
9157ec681f3Smrg   if (!parameter_count)
9167ec681f3Smrg      return VDP_STATUS_OK;
9177ec681f3Smrg   if (!(parameters && parameter_values))
9187ec681f3Smrg      return VDP_STATUS_INVALID_POINTER;
9197ec681f3Smrg   for (i = 0; i < parameter_count; ++i) {
9207ec681f3Smrg      switch (parameters[i]) {
9217ec681f3Smrg      case VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH:
9227ec681f3Smrg         *(uint32_t*)parameter_values[i] = vmixer->video_width;
9237ec681f3Smrg         break;
9247ec681f3Smrg      case VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT:
9257ec681f3Smrg         *(uint32_t*)parameter_values[i] = vmixer->video_height;
9267ec681f3Smrg         break;
9277ec681f3Smrg      case VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE:
9287ec681f3Smrg         *(VdpChromaType*)parameter_values[i] = PipeToChroma(vmixer->chroma_format);
9297ec681f3Smrg         break;
9307ec681f3Smrg      case VDP_VIDEO_MIXER_PARAMETER_LAYERS:
9317ec681f3Smrg         *(uint32_t*)parameter_values[i] = vmixer->max_layers;
9327ec681f3Smrg         break;
9337ec681f3Smrg      default:
9347ec681f3Smrg         return VDP_STATUS_INVALID_VIDEO_MIXER_PARAMETER;
9357ec681f3Smrg      }
9367ec681f3Smrg   }
9377ec681f3Smrg   return VDP_STATUS_OK;
9387ec681f3Smrg}
9397ec681f3Smrg
9407ec681f3Smrg/**
9417ec681f3Smrg * Retrieve current attribute values.
9427ec681f3Smrg */
9437ec681f3SmrgVdpStatus
9447ec681f3SmrgvlVdpVideoMixerGetAttributeValues(VdpVideoMixer mixer,
9457ec681f3Smrg                                  uint32_t attribute_count,
9467ec681f3Smrg                                  VdpVideoMixerAttribute const *attributes,
9477ec681f3Smrg                                  void *const *attribute_values)
9487ec681f3Smrg{
9497ec681f3Smrg   unsigned i;
9507ec681f3Smrg   VdpCSCMatrix **vdp_csc;
9517ec681f3Smrg
9527ec681f3Smrg   if (!(attributes && attribute_values))
9537ec681f3Smrg      return VDP_STATUS_INVALID_POINTER;
9547ec681f3Smrg
9557ec681f3Smrg   vlVdpVideoMixer *vmixer = vlGetDataHTAB(mixer);
9567ec681f3Smrg   if (!vmixer)
9577ec681f3Smrg      return VDP_STATUS_INVALID_HANDLE;
9587ec681f3Smrg
9597ec681f3Smrg   mtx_lock(&vmixer->device->mutex);
9607ec681f3Smrg   for (i = 0; i < attribute_count; ++i) {
9617ec681f3Smrg      switch (attributes[i]) {
9627ec681f3Smrg      case VDP_VIDEO_MIXER_ATTRIBUTE_BACKGROUND_COLOR:
9637ec681f3Smrg         vl_compositor_get_clear_color(&vmixer->cstate, attribute_values[i]);
9647ec681f3Smrg         break;
9657ec681f3Smrg      case VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX:
9667ec681f3Smrg         vdp_csc = attribute_values[i];
9677ec681f3Smrg         if (!vmixer->custom_csc) {
9687ec681f3Smrg             *vdp_csc = NULL;
9697ec681f3Smrg            break;
9707ec681f3Smrg         }
9717ec681f3Smrg         memcpy(*vdp_csc, vmixer->csc, sizeof(float)*12);
9727ec681f3Smrg         break;
9737ec681f3Smrg
9747ec681f3Smrg      case VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL:
9757ec681f3Smrg         *(float*)attribute_values[i] = (float)vmixer->noise_reduction.level / 10.0f;
9767ec681f3Smrg         break;
9777ec681f3Smrg
9787ec681f3Smrg      case VDP_VIDEO_MIXER_ATTRIBUTE_LUMA_KEY_MIN_LUMA:
9797ec681f3Smrg         *(float*)attribute_values[i] = vmixer->luma_key.luma_min;
9807ec681f3Smrg         break;
9817ec681f3Smrg      case VDP_VIDEO_MIXER_ATTRIBUTE_LUMA_KEY_MAX_LUMA:
9827ec681f3Smrg         *(float*)attribute_values[i] = vmixer->luma_key.luma_max;
9837ec681f3Smrg         break;
9847ec681f3Smrg      case VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL:
9857ec681f3Smrg         *(float*)attribute_values[i] = vmixer->sharpness.value;
9867ec681f3Smrg         break;
9877ec681f3Smrg      case VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE:
9887ec681f3Smrg         *(uint8_t*)attribute_values[i] = vmixer->skip_chroma_deint;
9897ec681f3Smrg         break;
9907ec681f3Smrg      default:
9917ec681f3Smrg         mtx_unlock(&vmixer->device->mutex);
9927ec681f3Smrg         return VDP_STATUS_INVALID_VIDEO_MIXER_ATTRIBUTE;
9937ec681f3Smrg      }
9947ec681f3Smrg   }
9957ec681f3Smrg   mtx_unlock(&vmixer->device->mutex);
9967ec681f3Smrg   return VDP_STATUS_OK;
9977ec681f3Smrg}
9987ec681f3Smrg
9997ec681f3Smrg/**
10007ec681f3Smrg * Generate a color space conversion matrix.
10017ec681f3Smrg */
10027ec681f3SmrgVdpStatus
10037ec681f3SmrgvlVdpGenerateCSCMatrix(VdpProcamp *procamp,
10047ec681f3Smrg                       VdpColorStandard standard,
10057ec681f3Smrg                       VdpCSCMatrix *csc_matrix)
10067ec681f3Smrg{
10077ec681f3Smrg   enum VL_CSC_COLOR_STANDARD vl_std;
10087ec681f3Smrg   struct vl_procamp camp;
10097ec681f3Smrg
10107ec681f3Smrg   if (!csc_matrix)
10117ec681f3Smrg      return VDP_STATUS_INVALID_POINTER;
10127ec681f3Smrg
10137ec681f3Smrg   switch (standard) {
10147ec681f3Smrg      case VDP_COLOR_STANDARD_ITUR_BT_601: vl_std = VL_CSC_COLOR_STANDARD_BT_601; break;
10157ec681f3Smrg      case VDP_COLOR_STANDARD_ITUR_BT_709: vl_std = VL_CSC_COLOR_STANDARD_BT_709; break;
10167ec681f3Smrg      case VDP_COLOR_STANDARD_SMPTE_240M:  vl_std = VL_CSC_COLOR_STANDARD_SMPTE_240M; break;
10177ec681f3Smrg      default: return VDP_STATUS_INVALID_COLOR_STANDARD;
10187ec681f3Smrg   }
10197ec681f3Smrg
10207ec681f3Smrg   if (procamp) {
10217ec681f3Smrg      if (procamp->struct_version > VDP_PROCAMP_VERSION)
10227ec681f3Smrg         return VDP_STATUS_INVALID_STRUCT_VERSION;
10237ec681f3Smrg      camp.brightness = procamp->brightness;
10247ec681f3Smrg      camp.contrast = procamp->contrast;
10257ec681f3Smrg      camp.saturation = procamp->saturation;
10267ec681f3Smrg      camp.hue = procamp->hue;
10277ec681f3Smrg   }
10287ec681f3Smrg
10297ec681f3Smrg   vl_csc_get_matrix(vl_std, procamp ? &camp : NULL, true, csc_matrix);
10307ec681f3Smrg   return VDP_STATUS_OK;
10317ec681f3Smrg}
1032