summaryrefslogtreecommitdiffstats
path: root/widget/gtk/vaapitest
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--widget/gtk/vaapitest/moz.build20
-rw-r--r--widget/gtk/vaapitest/vaapitest.cpp255
2 files changed, 275 insertions, 0 deletions
diff --git a/widget/gtk/vaapitest/moz.build b/widget/gtk/vaapitest/moz.build
new file mode 100644
index 0000000000..d9ea481ff1
--- /dev/null
+++ b/widget/gtk/vaapitest/moz.build
@@ -0,0 +1,20 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Toolkit", "Startup and Profile System")
+
+Program("vaapitest")
+SOURCES += [
+ "vaapitest.cpp",
+]
+CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
+CXXFLAGS += CONFIG["MOZ_PANGO_CFLAGS"]
+OS_LIBS += CONFIG["MOZ_X11_LIBS"]
+OS_LIBS += CONFIG["MOZ_GTK3_LIBS"]
+
+USE_LIBS += ["mozva"]
+LOCAL_INCLUDES += ["/media/mozva"]
diff --git a/widget/gtk/vaapitest/vaapitest.cpp b/widget/gtk/vaapitest/vaapitest.cpp
new file mode 100644
index 0000000000..e2c0e0a742
--- /dev/null
+++ b/widget/gtk/vaapitest/vaapitest.cpp
@@ -0,0 +1,255 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <cstdio>
+#include <cstdlib>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#if defined(MOZ_ASAN) || defined(FUZZING)
+# include <signal.h>
+#endif
+
+#include "mozilla/ScopeExit.h"
+
+#ifdef __SUNPRO_CC
+# include <stdio.h>
+#endif
+
+#include "prlink.h"
+#include "va/va.h"
+
+#include "mozilla/GfxInfoUtils.h"
+
+// Print VA-API test results to stdout and logging to stderr
+#define OUTPUT_PIPE 1
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+// bits to use decoding vaapitest() return values.
+constexpr int CODEC_HW_H264 = 1 << 4;
+constexpr int CODEC_HW_VP8 = 1 << 5;
+constexpr int CODEC_HW_VP9 = 1 << 6;
+constexpr int CODEC_HW_AV1 = 1 << 7;
+
+// childgltest is declared inside extern "C" so that the name is not mangled.
+// The name is used in build/valgrind/x86_64-pc-linux-gnu.sup to suppress
+// memory leak errors because we run it inside a short lived fork and we don't
+// care about leaking memory
+extern "C" {
+
+static constexpr struct {
+ VAProfile mVAProfile;
+ const char* mName;
+} kVAAPiProfileName[] = {
+#define MAP(v) \
+ { VAProfile##v, #v }
+ MAP(H264ConstrainedBaseline),
+ MAP(H264Main),
+ MAP(H264High),
+ MAP(VP8Version0_3),
+ MAP(VP9Profile0),
+ MAP(VP9Profile2),
+ MAP(AV1Profile0),
+ MAP(AV1Profile1),
+#undef MAP
+};
+
+static const char* VAProfileName(VAProfile aVAProfile) {
+ for (const auto& profile : kVAAPiProfileName) {
+ if (profile.mVAProfile == aVAProfile) {
+ return profile.mName;
+ }
+ }
+ return nullptr;
+}
+
+static void vaapitest(const char* aRenderDevicePath) {
+ int renderDeviceFD = -1;
+ VAProfile* profiles = nullptr;
+ VAEntrypoint* entryPoints = nullptr;
+ VADisplay display = nullptr;
+ void* libDrm = nullptr;
+
+ log("vaapitest start, device %s\n", aRenderDevicePath);
+
+ auto autoRelease = mozilla::MakeScopeExit([&] {
+ free(profiles);
+ free(entryPoints);
+ if (display) {
+ vaTerminate(display);
+ }
+ if (libDrm) {
+ dlclose(libDrm);
+ }
+ if (renderDeviceFD > -1) {
+ close(renderDeviceFD);
+ }
+ });
+
+ renderDeviceFD = open(aRenderDevicePath, O_RDWR);
+ if (renderDeviceFD == -1) {
+ record_error("VA-API test failed: failed to open renderDeviceFD.");
+ return;
+ }
+
+ libDrm = dlopen("libva-drm.so.2", RTLD_LAZY);
+ if (!libDrm) {
+ log("vaapitest failed: libva-drm.so.2 is missing\n");
+ return;
+ }
+
+ static auto sVaGetDisplayDRM =
+ (void* (*)(int fd))dlsym(libDrm, "vaGetDisplayDRM");
+ if (!sVaGetDisplayDRM) {
+ record_error("VA-API test failed: sVaGetDisplayDRM is missing.");
+ return;
+ }
+
+ display = sVaGetDisplayDRM(renderDeviceFD);
+ if (!display) {
+ record_error("VA-API test failed: vaGetDisplayDRM failed.");
+ return;
+ }
+
+ int major, minor;
+ VAStatus status = vaInitialize(display, &major, &minor);
+ if (status != VA_STATUS_SUCCESS) {
+ log("vaInitialize failed %d\n", status);
+ return;
+ } else {
+ log("vaInitialize finished\n");
+ }
+
+ int maxProfiles = vaMaxNumProfiles(display);
+ int maxEntryPoints = vaMaxNumEntrypoints(display);
+ if (maxProfiles <= 0 || maxEntryPoints <= 0) {
+ record_error("VA-API test failed: wrong VAAPI profiles/entry point nums.");
+ return;
+ }
+
+ profiles = (VAProfile*)malloc(sizeof(VAProfile) * maxProfiles);
+ int numProfiles = 0;
+ status = vaQueryConfigProfiles(display, profiles, &numProfiles);
+ if (status != VA_STATUS_SUCCESS) {
+ record_error("VA-API test failed: vaQueryConfigProfiles() failed.");
+ return;
+ }
+ numProfiles = MIN(numProfiles, maxProfiles);
+
+ entryPoints = (VAEntrypoint*)malloc(sizeof(VAEntrypoint) * maxEntryPoints);
+ int codecs = 0;
+ bool foundProfile = false;
+ for (int p = 0; p < numProfiles; p++) {
+ VAProfile profile = profiles[p];
+
+ // Check only supported profiles
+ if (!VAProfileName(profile)) {
+ continue;
+ }
+
+ int numEntryPoints = 0;
+ status = vaQueryConfigEntrypoints(display, profile, entryPoints,
+ &numEntryPoints);
+ if (status != VA_STATUS_SUCCESS) {
+ continue;
+ }
+ numEntryPoints = MIN(numEntryPoints, maxEntryPoints);
+ for (int entry = 0; entry < numEntryPoints; entry++) {
+ if (entryPoints[entry] != VAEntrypointVLD) {
+ continue;
+ }
+ VAConfigID config = VA_INVALID_ID;
+ status = vaCreateConfig(display, profile, entryPoints[entry], nullptr, 0,
+ &config);
+ if (status == VA_STATUS_SUCCESS) {
+ const char* profstr = VAProfileName(profile);
+ log("Profile: %s\n", profstr);
+ // VAProfileName returns null on failure, making the below calls safe
+ if (!strncmp(profstr, "H264", 4)) {
+ codecs |= CODEC_HW_H264;
+ } else if (!strncmp(profstr, "VP8", 3)) {
+ codecs |= CODEC_HW_VP8;
+ } else if (!strncmp(profstr, "VP9", 3)) {
+ codecs |= CODEC_HW_VP9;
+ } else if (!strncmp(profstr, "AV1", 3)) {
+ codecs |= CODEC_HW_AV1;
+ } else {
+ record_warning("VA-API test unknown profile.");
+ }
+ vaDestroyConfig(display, config);
+ foundProfile = true;
+ }
+ }
+ }
+ if (foundProfile) {
+ record_value("VAAPI_SUPPORTED\nTRUE\n");
+ record_value("VAAPI_HWCODECS\n%d\n", codecs);
+ } else {
+ record_value("VAAPI_SUPPORTED\nFALSE\n");
+ }
+ log("vaapitest finished\n");
+}
+
+} // extern "C"
+
+static void PrintUsage() {
+ printf(
+ "Firefox VA-API probe utility\n"
+ "\n"
+ "usage: vaapitest [options]\n"
+ "\n"
+ "Options:\n"
+ "\n"
+ " -h --help show this message\n"
+ " -d --drm drm_device probe VA-API on drm_device (may be "
+ "/dev/dri/renderD128)\n"
+ "\n");
+}
+
+int main(int argc, char** argv) {
+ struct option longOptions[] = {{"help", no_argument, NULL, 'h'},
+ {"drm", required_argument, NULL, 'd'},
+ {NULL, 0, NULL, 0}};
+ const char* shortOptions = "hd:";
+ int c;
+ const char* drmDevice = nullptr;
+ while ((c = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
+ switch (c) {
+ case 'd':
+ drmDevice = optarg;
+ break;
+ case 'h':
+ default:
+ break;
+ }
+ }
+ if (drmDevice) {
+#if defined(MOZ_ASAN) || defined(FUZZING)
+ // If handle_segv=1 (default), then glxtest crash will print a sanitizer
+ // report which can confuse the harness in fuzzing automation.
+ signal(SIGSEGV, SIG_DFL);
+#endif
+ const char* env = getenv("MOZ_GFX_DEBUG");
+ enable_logging = env && *env == '1';
+ output_pipe = OUTPUT_PIPE;
+ if (!enable_logging) {
+ close_logging();
+ }
+ vaapitest(drmDevice);
+ record_flush();
+ return EXIT_SUCCESS;
+ }
+ PrintUsage();
+ return 0;
+}