/* * Functional test for VDPAU, loosely based on test code from libvdpau-va-git * * Copyright © 2013 Rinat Ibragimov * Copyright © 2020 Collabora Ltd. * * SPDX-License-Identifier: MIT * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include <assert.h> #include <stdbool.h> #include <errno.h> #include <getopt.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sysexits.h> #include <vdpau/vdpau.h> #include <vdpau/vdpau_x11.h> static VdpGetInformationString *vdp_get_information_string; static VdpGetProcAddress *vdp_get_proc_address; static VdpDeviceDestroy *vdp_device_destroy; static VdpGetErrorString *vdp_get_error_string; static VdpOutputSurfaceCreate *vdp_output_surface_create; static VdpOutputSurfaceGetBitsNative *vdp_output_surface_get_bits_native; static VdpOutputSurfacePutBitsNative *vdp_output_surface_put_bits_native; static VdpOutputSurfaceRenderOutputSurface *vdp_output_surface_render_output_surface; enum { OPTION_HELP = 1, OPTION_VERBOSE, OPTION_VERSION, }; struct option long_options[] = { { "help", no_argument, NULL, OPTION_HELP }, { "verbose", no_argument, NULL, OPTION_VERBOSE }, { "version", no_argument, NULL, OPTION_VERSION }, { NULL, 0, NULL, 0 } }; static void usage (int code) __attribute__((__noreturn__)); /* * Print usage information and exit with status @code. */ static void usage (int code) { FILE *fp; if (code == 0) fp = stdout; else fp = stderr; fprintf (fp, "Usage: %s [OPTIONS]\n", program_invocation_short_name); exit (code); } static void _do_vdpau_or_exit (const char *description, VdpStatus status) { if (status != VDP_STATUS_OK) { if (vdp_get_error_string != NULL) fprintf (stderr, "%s failed: %s (%d)\n", description, vdp_get_error_string (status), status); else fprintf (stderr, "%s failed: %d\n", description, status); if (getenv ("SRT_CHECK_VDPAU_CRASH") != NULL) abort (); else exit (1); } } #define do_vdpau_or_exit(expr) _do_vdpau_or_exit (#expr, expr) static void load_vdpau (VdpDevice dev) { #define GET_POINTER(fid, fn) do_vdpau_or_exit (vdp_get_proc_address (dev, fid, (void**)&fn)) GET_POINTER(VDP_FUNC_ID_GET_INFORMATION_STRING, vdp_get_information_string); GET_POINTER(VDP_FUNC_ID_DEVICE_DESTROY, vdp_device_destroy); GET_POINTER(VDP_FUNC_ID_GET_ERROR_STRING, vdp_get_error_string); GET_POINTER(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE, vdp_output_surface_create); GET_POINTER(VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE, vdp_output_surface_get_bits_native); GET_POINTER(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE, vdp_output_surface_put_bits_native); GET_POINTER(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE, vdp_output_surface_render_output_surface); } int main (int argc, char **argv) { Display *display = NULL; VdpDevice device; VdpOutputSurface out_surface_1; VdpOutputSurface out_surface_2; bool verbose = false; int screen; int opt; const char* information = NULL; /* Arbitrarily declare two 4x4 source images */ uint32_t black_box[] = { 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000 }; uint32_t two_red_dots[] = { 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffff0000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffff0000 }; const void * const source_data_1[] = {black_box}; const void * const source_data_2[] = {two_red_dots}; uint32_t source_pitches[] = { 4 * 4 }; while ((opt = getopt_long (argc, argv, "", long_options, NULL)) != -1) { switch (opt) { case OPTION_HELP: usage (0); break; case OPTION_VERBOSE: verbose = true; break; case OPTION_VERSION: /* Output version number as YAML for machine-readability, * inspired by `ostree --version` and `docker version` */ printf ( "%s:\n" " Package: steam-runtime-tools\n" " Version: %s\n", argv[0], VERSION); return 0; case '?': default: usage (1); break; /* not reached */ } } display = XOpenDisplay (NULL); if (display == NULL) { fprintf (stderr, "An error occurred trying to open a connection to the X server\n"); return 1; } screen = DefaultScreen (display); do_vdpau_or_exit (vdp_device_create_x11 (display, screen, &device, &vdp_get_proc_address)); load_vdpau (device); if (verbose) { do_vdpau_or_exit (vdp_get_information_string (&information)); printf ("%s\n", information); } do_vdpau_or_exit (vdp_output_surface_create (device, VDP_RGBA_FORMAT_B8G8R8A8, 4, 4, &out_surface_1)); do_vdpau_or_exit (vdp_output_surface_create (device, VDP_RGBA_FORMAT_B8G8R8A8, 4, 4, &out_surface_2)); /* Upload the first black_box surface and then the two_red_dots one */ do_vdpau_or_exit (vdp_output_surface_put_bits_native (out_surface_1, source_data_1, source_pitches, NULL)); do_vdpau_or_exit (vdp_output_surface_put_bits_native (out_surface_2, source_data_2, source_pitches, NULL)); VdpOutputSurfaceRenderBlendState blend_state = { .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION, .blend_factor_source_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE, .blend_factor_source_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE, .blend_factor_destination_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO, .blend_factor_destination_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO, .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD, .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD, .blend_constant = {0, 0, 0, 0} }; /* Do the rendering of the two surfaces */ do_vdpau_or_exit (vdp_output_surface_render_output_surface (out_surface_1, NULL, out_surface_2, NULL, NULL, &blend_state, VDP_OUTPUT_SURFACE_RENDER_ROTATE_0)); /* Retrieve the data back from the surface */ uint32_t receive_buf[16]; void * const dest_data[] = {receive_buf}; do_vdpau_or_exit (vdp_output_surface_get_bits_native (out_surface_1, NULL, dest_data, source_pitches)); /* We expect that the retrieved buffer is equal to two_red_dots because previously * we blended the two surfaces together */ if (memcmp (receive_buf, two_red_dots, 4*4*4) != 0) { fprintf (stderr, "The rendered surface is not what we expected!\n"); return 1; } vdp_device_destroy (device); return 0; }