platform/libva-fake-driver: initial commit This CL presents the initial code for a fake libva driver. Libva (x11-libs/libva) is an API for video/image decoding/encoding acceleration, and this package provides a Fake backend for it for VMs and other test-related images. Note that weirdly enough libva wants drivers to be in /usr/lib64/va/drivers/fake_drv_video.so (no lib prefix) this package installs the generated .so in /usr/lib64 as is customary and makes a symbolic link to it. crrev.com/c/2193152 has the ebuild file for this. BUG=chromium:1080871 TEST=cros_workon --board=$BOARD start libva-fake-driver with BOARD=amd64-generic and then `emerge-$BOARD -v libva-fake-driver` Change-Id: Icc3c23f414f1a36b8e8cd85b6941819c68a08eef Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/libva-fake-driver/+/2200238 Commit-Queue: Miguel Casas <mcasas@chromium.org> Tested-by: Miguel Casas <mcasas@chromium.org> Reviewed-by: Jao-ke Chin-Lee <jchinlee@google.com> Reviewed-by: Andres Calderon Jaramillo <andrescj@google.com>
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..14d5dc7 --- /dev/null +++ b/Makefile
@@ -0,0 +1,25 @@ +# Copyright 2020 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +include common.mk + +PC_DEPS = libva +PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) +PC_LIBS := $(shell $(PKG_CONFIG) --libs $(PC_DEPS)) + +CFLAGS += -Wall -fPIC -I$(OUT) +LDFLAGS += -shared +LDLIBS += $(PC_LIBS) + +LIBDIR ?= /usr/lib64/va/drivers + +LIBLIBVAFAKEDRIVER := fake_drv_video.so + +CC_LIBRARY($(LIBLIBVAFAKEDRIVER)): $(C_OBJECTS) + +all: CC_LIBRARY($(LIBLIBVAFAKEDRIVER)) + +install: all + mkdir -p $(DESTDIR)/$(LIBDIR) + install -D -m 755 $(OUT)/$(LIBLIBVAFAKEDRIVER) $(DESTDIR)/$(LIBDIR)
diff --git a/OWNERS b/OWNERS new file mode 100644 index 0000000..22cca28 --- /dev/null +++ b/OWNERS
@@ -0,0 +1,4 @@ +set noparent +andrescj@chromium.org +jchinlee@chromium.org +mcasas@chromium.org
diff --git a/README.md b/README.md new file mode 100644 index 0000000..4380862 --- /dev/null +++ b/README.md
@@ -0,0 +1,15 @@ +# VA-API fake backend for libva + +VA-API is an API for video/image decoding/encoding acceleration implemented by +[libva] ([x11-libs/libva]). `libva-fake-driver` provides a fake backend for it +for VMs and other test-related images. + +It can be explicitly exercised by running e.g.: + + LIBVA_DRIVER_NAME="fake" vainfo + +wherever it might be installed. See https://tinyurl.com/libva-fake-driver for +more information. + +[libva]: https://github.com/intel/libva +[x11-libs/libva]: https://chromium.googlesource.com/chromiumos/overlays/chromiumos-overlay/+/refs/heads/master/x11-libs/libva/
diff --git a/common.mk b/common.mk new file mode 100644 index 0000000..486c9e9 --- /dev/null +++ b/common.mk
@@ -0,0 +1,845 @@ +# Copyright 2020 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# If this file is part of another source distribution, it's license may be +# stored in LICENSE.makefile or LICENSE.common.mk. +# +# NOTE NOTE NOTE +# The authoritative common.mk is located in: +# https://chromium.googlesource.com/chromiumos/platform/common-mk.git +# Please make all changes there, then copy into place in other repos. +# NOTE NOTE NOTE +# +# This file provides a common architecture for building C/C++ source trees. +# It uses recursive makefile inclusion to create a single make process which +# can be built in the source tree or with the build artifacts placed elsewhere. +# +# It is fully parallelizable for all targets, including static archives. +# +# To use: +# 1. Place common.mk in your top source level +# 2. In your top-level Makefile, place "include common.mk" at the top +# 3. In all subdirectories, create a 'module.mk' file that starts with: +# include common.mk +# And then contains the remainder of your targets. +# 4. All build targets should look like: +# relative/path/target: relative/path/obj.o +# +# See existing makefiles for rule examples. +# +# Exported macros: +# - cc_binary, cxx_binary provide standard compilation steps for binaries +# - cxx_library, cc_library provide standard compilation steps for +# shared objects. +# All of the above optionally take an argument for extra flags. +# - update_archive creates/updates a given .a target +# +# Instead of using the build macros, most users can just use wrapped targets: +# - CXX_BINARY, CC_BINARY, CC_STATIC_BINARY, CXX_STATIC_BINARY +# - CXX_LIBRARY, CC_LIBRARY, CC_STATIC_LIBRARY, CXX_STATIC_LIBRARY +# - E.g., CXX_BINARY(mahbinary): foo.o +# - object.depends targets may be used when a prerequisite is required for an +# object file. Because object files result in multiple build artifacts to +# handle PIC and PIE weirdness. E.g. +# foo.o.depends: generated/dbus.h +# - TEST(binary) or TEST(CXX_BINARY(binary)) may be used as a prerequisite +# for the tests target to trigger an automated test run. +# - CLEAN(file_or_dir) dependency can be added to 'clean'. +# +# If source code is being generated, rules will need to be registered for +# compiling the objects. This can be done by adding one of the following +# to the Makefile: +# - For C source files +# $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/gen_b.o,CC,c)) +# - For C++ source files +# $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/gen_b.o,CXX,cc)) +# +# Exported targets meant to have prerequisites added to: +# - all - Your desired targets should be given +# - tests - Any TEST(test_binary) targets should be given +# - FORCE - force the given target to run regardless of changes +# In most cases, using .PHONY is preferred. +# +# Possible command line variables: +# - COLOR=[0|1] to set ANSI color output (default: 1) +# - VERBOSE=[0|1] to hide/show commands (default: 0) +# - MODE=[opt|dbg|profiling] (default: opt) +# opt - Enable optimizations for release builds +# dbg - Turn down optimization for debugging +# profiling - Turn off optimization and turn on profiling/coverage +# support. +# - ARCH=[x86|arm|supported qemu name] (default: from portage or uname -m) +# - SPLITDEBUG=[0|1] splits debug info in target.debug (default: 0) +# If NOSTRIP=1, SPLITDEBUG will never strip the final emitted objects. +# - NOSTRIP=[0|1] determines if binaries are stripped. (default: 1) +# NOSTRIP=0 and MODE=opt will also drop -g from the CFLAGS. +# - VALGRIND=[0|1] runs tests under valgrind (default: 0) +# - OUT=/path/to/builddir puts all output in given path (default: $PWD) +# - VALGRIND_ARGS="" supplies extra memcheck arguments +# +# Per-target(-ish) variable: +# - NEEDS_ROOT=[0|1] allows a TEST() target to run with root. +# Default is 0 unless it is running under QEmu. +# - NEEDS_MOUNTS=[0|1] allows a TEST() target running on QEmu to get +# setup mounts in the $(SYSROOT) +# +# Caveats: +# - Directories or files with spaces in them DO NOT get along with GNU Make. +# If you need them, all uses of dir/notdir/etc will need to have magic +# wrappers. Proceed at risk to your own sanity. +# - External CXXFLAGS and CFLAGS should be passed via the environment since +# this file does not use 'override' to control them. +# - Our version of GNU Make doesn't seem to support the 'private' variable +# annotation, so you can't tag a variable private on a wrapping target. + +# Behavior configuration variables +SPLITDEBUG ?= 0 +NOSTRIP ?= 1 +VALGRIND ?= 0 +COLOR ?= 1 +VERBOSE ?= 0 +MODE ?= opt +ARCH ?= $(shell uname -m) +NEEDS_ROOT = 0 +NEEDS_MOUNTS = 0 + +# Put objects in a separate tree based on makefile locations +# This means you can build a tree without touching it: +# make -C $SRCDIR # will create ./build-$(MODE) +# Or +# make -C $SRCDIR OUT=$PWD +# This variable is extended on subdir calls and doesn't need to be re-called. +OUT ?= $(PWD)/ + +# Make OUT now so we can use realpath. +$(shell mkdir -p "$(OUT)") + +# TODO(wad) Relative paths are resolved against SRC and not the calling dir. +# Ensure a command-line supplied OUT has a slash +override OUT := $(realpath $(OUT))/ + +# SRC is not meant to be set by the end user, but during make call relocation. +# $(PWD) != $(CURDIR) all the time. +export SRC ?= $(CURDIR) + +# Re-start in the $(OUT) directory if we're not there. +# We may be invoked using -C or bare and we need to ensure behavior +# is consistent so we check both PWD vs OUT and PWD vs CURDIR. +override RELOCATE_BUILD := 0 +ifneq (${PWD}/,${OUT}) +override RELOCATE_BUILD := 1 +endif +# Make sure we're running with no builtin targets. They cause +# leakage and mayhem! +ifneq (${PWD},${CURDIR}) +override RELOCATE_BUILD := 1 +# If we're run from the build dir, don't let it get cleaned up later. +ifeq (${PWD}/,${OUT}) +$(shell touch "$(PWD)/.dont_delete_on_clean") +endif +endif # ifneq (${PWD},${CURDIR} + +# "Relocate" if we need to restart without implicit rules. +ifeq ($(subst r,,$(MAKEFLAGS)),$(MAKEFLAGS)) +override RELOCATE_BUILD := 1 +endif + +ifeq (${RELOCATE_BUILD},1) +# By default, silence build output. Reused below as well. +QUIET = @ +ifeq ($(VERBOSE),1) + QUIET= +endif + +# This target will override all targets, including prerequisites. To avoid +# calling $(MAKE) once per prereq on the given CMDGOAL, we guard it with a local +# variable. +RUN_ONCE := 0 +MAKECMDGOALS ?= all +# Keep the rules split as newer make does not allow them to be declared +# on the same line. But the way :: rules work, the _all here will also +# invoke the %:: rule while retaining "_all" as the default. +_all:: +%:: + $(if $(filter 0,$(RUN_ONCE)), \ + cd "$(OUT)" && \ + $(MAKE) -r -I "$(SRC)" -f "$(CURDIR)/Makefile" \ + SRC="$(CURDIR)" OUT="$(OUT)" $(foreach g,$(MAKECMDGOALS),"$(g)"),) + $(eval RUN_ONCE := 1) +pass-to-subcall := 1 +endif + +ifeq ($(pass-to-subcall),) + +# Only call MODULE if we're in a submodule +MODULES_LIST := $(filter-out Makefile %.d,$(MAKEFILE_LIST)) +ifeq ($(words $(filter-out Makefile common.mk %.d $(SRC)/Makefile \ + $(SRC)/common.mk,$(MAKEFILE_LIST))),0) + +# All the top-level defines outside of module.mk. + +# +# Helper macros +# + +# Create the directory if it doesn't yet exist. +define auto_mkdir + $(if $(wildcard $(dir $1)),$2,$(QUIET)mkdir -p "$(dir $1)") +endef + +# Creates the actual archive with an index. +# The target $@ must end with .pic.a or .pie.a. +define update_archive + $(call auto_mkdir,$(TARGET_OR_MEMBER)) + $(QUIET)# Create the archive in one step to avoid parallel use accessing it + $(QUIET)# before all the symbols are present. + @$(ECHO) "AR $(subst \ +$(SRC)/,,$(^:.o=$(suffix $(basename $(TARGET_OR_MEMBER))).o)) \ +-> $(subst $(SRC)/,,$(TARGET_OR_MEMBER))" + $(QUIET)$(AR) rcs $(TARGET_OR_MEMBER) \ + $(subst $(SRC)/,,$(^:.o=$(suffix $(basename $(TARGET_OR_MEMBER))).o)) +endef + +# Default compile from objects using pre-requisites but filters out +# subdirs and .d files. +define cc_binary + $(call COMPILE_BINARY_implementation,CC,$(CFLAGS) $(1),$(EXTRA_FLAGS)) +endef + +define cxx_binary + $(call COMPILE_BINARY_implementation,CXX,$(CXXFLAGS) $(1),$(EXTRA_FLAGS)) +endef + +# Default compile from objects using pre-requisites but filters out +# subdirs and .d files. +define cc_library + $(call COMPILE_LIBRARY_implementation,CC,$(CFLAGS) $(1),$(EXTRA_FLAGS)) +endef +define cxx_library + $(call COMPILE_LIBRARY_implementation,CXX,$(CXXFLAGS) $(1),$(EXTRA_FLAGS)) +endef + +# Deletes files silently if they exist. Meant for use in any local +# clean targets. +define silent_rm + $(if $(wildcard $(1)), + $(QUIET)($(ECHO) -n '$(COLOR_RED)CLEANFILE$(COLOR_RESET) ' && \ + $(ECHO) '$(subst $(OUT)/,,$(wildcard $(1)))' && \ + $(RM) $(1) 2>/dev/null) || true,) +endef +define silent_rmdir + $(if $(wildcard $(1)), + $(if $(wildcard $(1)/*), + $(QUIET)# $(1) not empty [$(wildcard $(1)/*)]. Not deleting., + $(QUIET)($(ECHO) -n '$(COLOR_RED)CLEANDIR$(COLOR_RESET) ' && \ + $(ECHO) '$(subst $(OUT)/,,$(wildcard $(1)))' && \ + $(RMDIR) $(1) 2>/dev/null) || true),) +endef + +# +# Default variable values +# + +# Only override toolchain vars if they are from make. +CROSS_COMPILE ?= +define override_var +ifneq ($(filter undefined default,$(origin $1)),) +$1 = $(CROSS_COMPILE)$2 +endif +endef +$(eval $(call override_var,AR,ar)) +$(eval $(call override_var,CC,gcc)) +$(eval $(call override_var,CXX,g++)) +$(eval $(call override_var,OBJCOPY,objcopy)) +$(eval $(call override_var,PKG_CONFIG,pkg-config)) +$(eval $(call override_var,RANLIB,ranlib)) +$(eval $(call override_var,STRIP,strip)) + +RMDIR ?= rmdir +ECHO = /bin/echo -e + +ifeq ($(lastword $(subst /, ,$(CC))),clang) +CDRIVER = clang +else +CDRIVER = gcc +endif + +ifeq ($(lastword $(subst /, ,$(CXX))),clang++) +CXXDRIVER = clang +else +CXXDRIVER = gcc +endif + +# To update these from an including Makefile: +# CXXFLAGS += -mahflag # Append to the list +# CXXFLAGS := -mahflag $(CXXFLAGS) # Prepend to the list +# CXXFLAGS := $(filter-out badflag,$(CXXFLAGS)) # Filter out a value +# The same goes for CFLAGS. +COMMON_CFLAGS-gcc := -fstack-protector-strong -fvisibility=internal -ggdb3 \ + -Wa,--noexecstack +COMMON_CFLAGS-clang := -fstack-protector-all -fvisibility=hidden -ggdb +COMMON_CFLAGS := -Wall -Werror -fno-strict-aliasing -O1 -Wformat=2 +CXXFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CXXDRIVER)) +CFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CDRIVER)) +CPPFLAGS += -D_FORTIFY_SOURCE=2 + + +ifeq ($(MODE),opt) + # Up the optimizations. + CFLAGS := $(filter-out -O1,$(CFLAGS)) -O2 + CXXFLAGS := $(filter-out -O1,$(CXXFLAGS)) -O2 + # Only drop -g* if symbols aren't desired. + ifeq ($(NOSTRIP),0) + # TODO: do we want -fomit-frame-pointer on x86? + CFLAGS := $(filter-out -ggdb3,$(CFLAGS)) + CXXFLAGS := $(filter-out -ggdb3,$(CXXFLAGS)) + endif +endif + +ifeq ($(MODE),profiling) + CFLAGS := $(CFLAGS) -O0 -g --coverage + CXXFLAGS := $(CXXFLAGS) -O0 -g --coverage + LDFLAGS := $(LDFLAGS) --coverage +endif + +LDFLAGS := $(LDFLAGS) -Wl,-z,relro -Wl,-z,noexecstack -Wl,-z,now + +# Fancy helpers for color if a prompt is defined +ifeq ($(COLOR),1) +COLOR_RESET = \x1b[0m +COLOR_GREEN = \x1b[32;01m +COLOR_RED = \x1b[31;01m +COLOR_YELLOW = \x1b[33;01m +endif + +# By default, silence build output. +QUIET = @ +ifeq ($(VERBOSE),1) + QUIET= +endif + +# +# Implementation macros for compile helpers above +# + +# Useful for dealing with pie-broken toolchains. +# Call make with PIE=0 to disable default PIE use. +OBJ_PIE_FLAG = -fPIE +COMPILE_PIE_FLAG = -pie +ifeq ($(PIE),0) + OBJ_PIE_FLAG = + COMPILE_PIE_FLAG = +endif + +# Favor member targets first for CXX_BINARY(%) magic. +# And strip out nested members if possible. +LP := ( +RP := ) +TARGET_OR_MEMBER = $(lastword $(subst $(LP), ,$(subst $(RP),,$(or $%,$@)))) + +# Default compile from objects using pre-requisites but filters out +# all non-.o files. +define COMPILE_BINARY_implementation + @$(ECHO) "LD$(1) $(subst $(PWD)/,,$(TARGET_OR_MEMBER))" + $(call auto_mkdir,$(TARGET_OR_MEMBER)) + $(QUIET)$($(1)) $(COMPILE_PIE_FLAGS) -o $(TARGET_OR_MEMBER) \ + $(2) $(LDFLAGS) \ + $(filter %.o %.a,$(^:.o=.pie.o)) \ + $(foreach so,$(filter %.so,$^),-L$(dir $(so)) \ + -l$(patsubst lib%,%,$(basename $(notdir $(so))))) \ + $(LDLIBS) + $(call conditional_strip) + @$(ECHO) -n "BIN " + @$(ECHO) "$(COLOR_GREEN)$(subst $(PWD)/,,$(TARGET_OR_MEMBER))$(COLOR_RESET)" + @$(ECHO) " $(COLOR_YELLOW)-----$(COLOR_RESET)" +endef + +# TODO: add version support extracted from PV environment variable +#ifeq ($(PV),9999) +#$(warning PV=$(PV). If shared object versions matter, please force PV=.) +#endif +# Then add -Wl,-soname,$@.$(PV) ? + +# Default compile from objects using pre-requisites but filters out +# all non-.o values. (Remember to add -L$(OUT) -llib) +COMMA := , +define COMPILE_LIBRARY_implementation + @$(ECHO) "SHARED$(1) $(subst $(PWD)/,,$(TARGET_OR_MEMBER))" + $(call auto_mkdir,$(TARGET_OR_MEMBER)) + $(QUIET)$($(1)) -shared -Wl,-E -o $(TARGET_OR_MEMBER) \ + $(2) $(LDFLAGS) \ + $(if $(filter %.a,$^),-Wl$(COMMA)--whole-archive,) \ + $(filter %.o ,$(^:.o=.pic.o)) \ + $(foreach a,$(filter %.a,$^),-L$(dir $(a)) \ + -l$(patsubst lib%,%,$(basename $(notdir $(a))))) \ + $(foreach so,$(filter %.so,$^),-L$(dir $(so)) \ + -l$(patsubst lib%,%,$(basename $(notdir $(so))))) \ + $(LDLIBS) + $(call conditional_strip) + @$(ECHO) -n "LIB $(COLOR_GREEN)" + @$(ECHO) "$(subst $(PWD)/,,$(TARGET_OR_MEMBER))$(COLOR_RESET)" + @$(ECHO) " $(COLOR_YELLOW)-----$(COLOR_RESET)" +endef + +define conditional_strip + $(if $(filter 0,$(NOSTRIP)),$(call strip_artifact)) +endef + +define strip_artifact + @$(ECHO) "STRIP $(subst $(OUT)/,,$(TARGET_OR_MEMBER))" + $(if $(filter 1,$(SPLITDEBUG)), @$(ECHO) -n "DEBUG "; \ + $(ECHO) "$(COLOR_YELLOW)\ +$(subst $(OUT)/,,$(TARGET_OR_MEMBER)).debug$(COLOR_RESET)") + $(if $(filter 1,$(SPLITDEBUG)), \ + $(QUIET)$(OBJCOPY) --only-keep-debug "$(TARGET_OR_MEMBER)" \ + "$(TARGET_OR_MEMBER).debug") + $(if $(filter-out dbg,$(MODE)),$(QUIET)$(STRIP) --strip-unneeded \ + "$(TARGET_OR_MEMBER)",) +endef + +# +# Global pattern rules +# + +# Below, the archive member syntax is abused to create fancier +# syntactic sugar for recipe authors that avoids needed to know +# subcall options. The downside is that make attempts to look +# into the phony archives for timestamps. This will cause the final +# target to be rebuilt/linked on _every_ call to make even when nothing +# has changed. Until a better way presents itself, we have helpers that +# do the stat check on make's behalf. Dodgy but simple. +define old_or_no_timestamp + $(if $(realpath $%),,$(1)) + $(if $(shell find $^ -cnewer "$%" 2>/dev/null),$(1)) +endef + +define check_deps + $(if $(filter 0,$(words $^)),\ + $(error Missing dependencies or declaration of $@($%)),) +endef + +# Build a cxx target magically +CXX_BINARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cxx_binary)) +clean: CLEAN(CXX_BINARY*) + +CC_BINARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cc_binary)) +clean: CLEAN(CC_BINARY*) + +CXX_STATIC_BINARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cxx_binary,-static)) +clean: CLEAN(CXX_STATIC_BINARY*) + +CC_STATIC_BINARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cc_binary,-static)) +clean: CLEAN(CC_STATIC_BINARY*) + +CXX_LIBRARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cxx_library)) +clean: CLEAN(CXX_LIBRARY*) + +CXX_LIBARY(%): + $(error Typo alert! LIBARY != LIBRARY) + +CC_LIBRARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cc_library)) +clean: CLEAN(CC_LIBRARY*) + +CC_LIBARY(%): + $(error Typo alert! LIBARY != LIBRARY) + +CXX_STATIC_LIBRARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call update_archive)) +clean: CLEAN(CXX_STATIC_LIBRARY*) + +CXX_STATIC_LIBARY(%): + $(error Typo alert! LIBARY != LIBRARY) + +CC_STATIC_LIBRARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call update_archive)) +clean: CLEAN(CC_STATIC_LIBRARY*) + +CC_STATIC_LIBARY(%): + $(error Typo alert! LIBARY != LIBRARY) + + +TEST(%): % qemu_chroot_install + $(call TEST_implementation) +.PHONY: TEST + +# multiple targets with a wildcard need to share an directory. +# Don't use this directly it just makes sure the directory is removed _after_ +# the files are. +CLEANFILE(%): + $(call silent_rm,$(TARGET_OR_MEMBER)) +.PHONY: CLEANFILE + +CLEAN(%): CLEANFILE(%) + $(QUIET)# CLEAN($%) meta-target called + $(if $(filter-out $(PWD)/,$(dir $(abspath $(TARGET_OR_MEMBER)))), \ + $(call silent_rmdir,$(dir $(abspath $(TARGET_OR_MEMBER)))),\ + $(QUIET)# Not deleting $(dir $(abspath $(TARGET_OR_MEMBER))) yet.) +.PHONY: CLEAN + +# +# Top-level objects and pattern rules +# + +# All objects for .c files at the top level +C_OBJECTS = $(patsubst $(SRC)/%.c,%.o,$(wildcard $(SRC)/*.c)) + + +# All objects for .cxx files at the top level +CXX_OBJECTS = $(patsubst $(SRC)/%.cc,%.o,$(wildcard $(SRC)/*.cc)) + +# Note, the catch-all pattern rules don't work in subdirectories because +# we're building from the $(OUT) directory. At the top-level (here) they will +# work, but we go ahead and match using the module form. Then we can place a +# generic pattern rule to capture leakage from the main Makefile. (Later in the +# file.) +# +# The reason target specific pattern rules work well for modules, +# MODULE_C_OBJECTS, is because it scopes the behavior to the given target which +# ensures we get a relative directory offset from $(OUT) which otherwise would +# not match without further magic on a per-subdirectory basis. + +# Creates object file rules. Call with eval. +# $(1) list of .o files +# $(2) source type (CC or CXX) +# $(3) source suffix (cc or c) +# $(4) compiler flag name (CFLAGS or CXXFLAGS) +# $(5) source dir: _only_ if $(SRC). Leave blank for obj tree. +define add_object_rules +$(patsubst %.o,%.pie.o,$(1)): %.pie.o: $(5)%.$(3) %.o.depends + $$(call auto_mkdir,$$@) + $$(call OBJECT_PATTERN_implementation,$(2),\ + $$(basename $$@),$$($(4)) $$(CPPFLAGS) $$(OBJ_PIE_FLAG)) + +$(patsubst %.o,%.pic.o,$(1)): %.pic.o: $(5)%.$(3) %.o.depends + $$(call auto_mkdir,$$@) + $$(call OBJECT_PATTERN_implementation,$(2),\ + $$(basename $$@),$$($(4)) $$(CPPFLAGS) -fPIC) + +# Placeholder for depends +$(patsubst %.o,%.o.depends,$(1)): + $$(call auto_mkdir,$$@) + $$(QUIET)touch "$$@" + +$(1): %.o: %.pic.o %.pie.o + $$(call auto_mkdir,$$@) + $$(QUIET)touch "$$@" +endef + +define OBJECT_PATTERN_implementation + @$(ECHO) "$(1) $(subst $(SRC)/,,$<) -> $(2).o" + $(call auto_mkdir,$@) + $(QUIET)$($(1)) -c -MD -MF $(2).d $(3) -o $(2).o $< + $(QUIET)# Wrap all the deps in $$(wildcard) so a missing header + $(QUIET)# won't cause weirdness. First we remove newlines and \, + $(QUIET)# then wrap it. + $(QUIET)sed -i -e :j -e '$$!N;s|\\\s*\n| |;tj' \ + -e 's|^\(.*\s*:\s*\)\(.*\)$$|\1 $$\(wildcard \2\)|' $(2).d +endef + +# Now actually register handlers for C(XX)_OBJECTS. +$(eval $(call add_object_rules,$(C_OBJECTS),CC,c,CFLAGS,$(SRC)/)) +$(eval $(call add_object_rules,$(CXX_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/)) + +# Disable default pattern rules to help avoid leakage. +# These may already be handled by '-r', but let's keep it to be safe. +%: %.o ; +%.a: %.o ; +%.o: %.c ; +%.o: %.cc ; + +# NOTE: A specific rule for archive objects is avoided because parallel +# update of the archive causes build flakiness. +# Instead, just make the objects the prerequisites and use update_archive +# To use the foo.a(obj.o) functionality, targets would need to specify the +# explicit object they expect on the prerequisite line. + +# +# Architecture detection and QEMU wrapping +# + +HOST_ARCH ?= $(shell uname -m) +override ARCH := $(strip $(ARCH)) +override HOST_ARCH := $(strip $(HOST_ARCH)) +# emake will supply "x86" or "arm" for ARCH, but +# if uname -m runs and you get x86_64, then this subst +# will break. +ifeq ($(subst x86,i386,$(ARCH)),i386) + QEMU_ARCH := $(subst x86,i386,$(ARCH)) # x86 -> i386 +else ifeq ($(subst amd64,x86_64,$(ARCH)),x86_64) + QEMU_ARCH := $(subst amd64,x86_64,$(ARCH)) # amd64 -> x86_64 +else + QEMU_ARCH = $(ARCH) +endif +override QEMU_ARCH := $(strip $(QEMU_ARCH)) + +# If we're cross-compiling, try to use qemu for running the tests. +ifneq ($(QEMU_ARCH),$(HOST_ARCH)) + ifeq ($(SYSROOT),) + $(info SYSROOT not defined. qemu-based testing disabled) + else + # A SYSROOT is assumed for QEmu use. + USE_QEMU ?= 1 + + # Allow 64-bit hosts to run 32-bit without qemu. + ifeq ($(HOST_ARCH),x86_64) + ifeq ($(QEMU_ARCH),i386) + USE_QEMU = 0 + endif + endif + endif +else + USE_QEMU ?= 0 +endif + +SYSROOT_OUT = $(OUT) +ifneq ($(SYSROOT),) + SYSROOT_OUT = $(subst $(SYSROOT),,$(OUT)) +else + # Default to / when all the empty-sysroot logic is done. + SYSROOT = / +endif + + +# +# Output full configuration at top level +# + +# Don't show on clean +ifneq ($(MAKECMDGOALS),clean) + $(info build configuration:) + $(info - OUT=$(OUT)) + $(info - SRC=$(SRC)) + $(info - MODE=$(MODE)) + $(info - SPLITDEBUG=$(SPLITDEBUG)) + $(info - NOSTRIP=$(NOSTRIP)) + $(info - VALGRIND=$(VALGRIND)) + $(info - COLOR=$(COLOR)) + $(info - ARCH=$(ARCH)) + $(info - QEMU_ARCH=$(QEMU_ARCH)) + $(info - SYSROOT=$(SYSROOT)) + $(info ) +endif + +# +# Standard targets with detection for when they are improperly configured. +# + +# all does not include tests by default +all: + $(QUIET)(test -z "$^" && \ + $(ECHO) "You must add your targets as 'all' prerequisites") || true + $(QUIET)test -n "$^" + +# Builds and runs tests for the target arch +# Run them in parallel +# After the test have completed, if profiling, run coverage analysis +tests: +ifeq ($(MODE),profiling) + @$(ECHO) -n "COVERAGE gcov " + @$(ECHO) "[$(COLOR_YELLOW)STARTED$(COLOR_RESET)]" + $(QUIET)(FILES=""; \ + for GCNO in `find . -name "*.gcno"`; \ + do \ + GCDA="$${GCNO%.gcno}.gcda"; \ + [ -e $${GCDA} ] && FILES="$${FILES} $${GCDA}"; \ + done; \ + gcov -l $${FILES}) + @$(ECHO) -n "COVERAGE gcov " + @$(ECHO) "[$(COLOR_YELLOW)FINISHED$(COLOR_RESET)]" + @$(ECHO) -n "COVERAGE lcov " + @$(ECHO) "[$(COLOR_YELLOW)STARTED$(COLOR_RESET)]" + $(QUIET)lcov --capture --directory . --output-file=lcov-coverage.info + $(QUIET)genhtml lcov-coverage.info --output-directory lcov-html + @$(ECHO) -n "COVERAGE lcov " + @$(ECHO) "[$(COLOR_YELLOW)FINISHED$(COLOR_RESET)]" +endif +.PHONY: tests + +qemu_clean: + $(call if_qemu,$(call silent_rm,$(OUT)/qemu-$(QEMU_ARCH))) + +qemu_chroot_install: +ifeq ($(USE_QEMU),1) + $(QUIET)$(ECHO) "QEMU Preparing qemu-$(QEMU_ARCH)" + $(QUIET)cp -fu /usr/bin/qemu-$(QEMU_ARCH) $(OUT)/qemu-$(QEMU_ARCH) + $(QUIET)chmod a+rx $(OUT)/qemu-$(QEMU_ARCH) +endif +.PHONY: qemu_clean qemu_chroot_install + +# TODO(wad) Move to -L $(SYSROOT) and fakechroot when qemu-user +# doesn't hang traversing /proc from SYSROOT. +QEMU_CMD = +ROOT_CMD = $(if $(filter 1,$(NEEDS_ROOT)),sudo , ) +MOUNT_CMD = $(if $(filter 1,$(NEEDS_MOUNTS)),$(ROOT_CMD) mount, \#) +UMOUNT_CMD = $(if $(filter 1,$(NEEDS_MOUNTS)),$(ROOT_CMD) umount, \#) +QEMU_LDPATH = $(SYSROOT_LDPATH):/lib64:/lib:/usr/lib64:/usr/lib +ROOT_CMD_LDPATH = $(SYSROOT_LDPATH):$(SYSROOT)/lib64: +ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/lib:$(SYSROOT)/usr/lib64: +ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/usr/lib +ifeq ($(USE_QEMU),1) + export QEMU_CMD = \ + sudo chroot $(SYSROOT) $(SYSROOT_OUT)qemu-$(QEMU_ARCH) \ + -drop-ld-preload \ + -E LD_LIBRARY_PATH="$(QEMU_LDPATH):$(patsubst $(OUT),,$(LD_DIRS))" \ + -E HOME="$(HOME)" -- + # USE_QEMU conditional function + define if_qemu + $(1) + endef +else + ROOT_CMD = $(if $(filter 1,$(NEEDS_ROOT)),sudo, ) \ + LD_LIBRARY_PATH="$(ROOT_CMD_LDPATH):$(LD_DIRS)" + define if_qemu + $(2) + endef +endif + +VALGRIND_CMD = +ifeq ($(VALGRIND),1) + VALGRIND_CMD = /usr/bin/valgrind --tool=memcheck $(VALGRIND_ARGS) -- +endif + +define TEST_implementation + $(QUIET)$(call TEST_setup) + $(QUIET)$(call TEST_run) + $(QUIET)$(call TEST_teardown) + $(QUIET)exit $$(cat $(OUT)$(TARGET_OR_MEMBER).status.test) +endef + +define TEST_setup + @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " + @$(ECHO) "[$(COLOR_YELLOW)SETUP$(COLOR_RESET)]" + $(QUIET)# Setup a target-specific results file + $(QUIET)(echo 1 > $(OUT)$(TARGET_OR_MEMBER).status.test) + $(QUIET)(echo > $(OUT)$(TARGET_OR_MEMBER).cleanup.test) + $(QUIET)# No setup if we are not using QEMU + $(QUIET)# TODO(wad) this is racy until we use a vfs namespace + $(call if_qemu,\ + $(QUIET)sudo mkdir -p "$(SYSROOT)/proc" "$(SYSROOT)/dev") + $(call if_qemu,\ + $(QUIET)$(MOUNT_CMD) --bind /proc "$(SYSROOT)/proc") + $(call if_qemu,\ + $(QUIET)$(MOUNT_CMD) --bind /dev "$(SYSROOT)/dev") + $(call if_qemu,\ + $(QUIET)(echo "$(UMOUNT_CMD) -l '$(SYSROOT)/proc'" \ + >> "$(OUT)$(TARGET_OR_MEMBER).cleanup.test")) + $(call if_qemu,\ + $(QUIET)(echo "$(UMOUNT_CMD) -l '$(SYSROOT)/dev'" \ + >> "$(OUT)$(TARGET_OR_MEMBER).cleanup.test")) +endef + +define TEST_teardown + @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " + @$(ECHO) "[$(COLOR_YELLOW)TEARDOWN$(COLOR_RESET)]" + $(call if_qemu, $(QUIET)$(SHELL) "$(OUT)$(TARGET_OR_MEMBER).cleanup.test") +endef + +# Use GTEST_ARGS.[arch] if defined. +override GTEST_ARGS.real = \ + $(call if_qemu,$(GTEST_ARGS.qemu.$(QEMU_ARCH)),$(GTEST_ARGS.host.$(HOST_ARCH))) + +define TEST_run + @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " + @$(ECHO) "[$(COLOR_GREEN)RUN$(COLOR_RESET)]" + $(QUIET)(echo 1 > "$(OUT)$(TARGET_OR_MEMBER).status.test") + -($(ROOT_CMD) $(QEMU_CMD) $(VALGRIND_CMD) \ + "$(strip $(call if_qemu, $(SYSROOT_OUT),$(OUT))$(TARGET_OR_MEMBER))" \ + $(if $(filter-out 0,$(words $(GTEST_ARGS.real))),$(GTEST_ARGS.real),\ + $(GTEST_ARGS)) && \ + echo 0 > "$(OUT)$(TARGET_OR_MEMBER).status.test") +endef + +# Recursive list reversal so that we get RMDIR_ON_CLEAN in reverse order. +define reverse +$(if $(1),$(call reverse,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1)) +endef + +clean: qemu_clean +clean: CLEAN($(OUT)*.d) CLEAN($(OUT)*.o) CLEAN($(OUT)*.debug) +clean: CLEAN($(OUT)*.test) CLEAN($(OUT)*.depends) +clean: CLEAN($(OUT)*.gcno) CLEAN($(OUT)*.gcda) CLEAN($(OUT)*.gcov) +clean: CLEAN($(OUT)lcov-coverage.info) CLEAN($(OUT)lcov-html) + +clean: + $(QUIET)# Always delete the containing directory last. + $(call silent_rmdir,$(OUT)) + +FORCE: ; +# Empty rule for use when no special targets are needed, like large_tests +NONE: + +.PHONY: clean NONE valgrind NONE +.DEFAULT_GOAL := all +# Don't let make blow away "intermediates" +.PRECIOUS: %.pic.o %.pie.o %.a %.pic.a %.pie.a %.test + +# Start accruing build info +OUT_DIRS = $(OUT) +LD_DIRS = $(OUT) +SRC_DIRS = $(SRC) + +include $(wildcard $(OUT)*.d) +SUBMODULE_DIRS = $(wildcard $(SRC)/*/module.mk) +include $(SUBMODULE_DIRS) + + +else ## In duplicate inclusions of common.mk + +# Get the current inclusion directory without a trailing slash +MODULE := $(patsubst %/,%, \ + $(dir $(lastword $(filter-out %common.mk,$(MAKEFILE_LIST))))) +MODULE := $(subst $(SRC)/,,$(MODULE)) +MODULE_NAME := $(subst /,_,$(MODULE)) +#VPATH := $(MODULE):$(VPATH) + + +# Depth first +$(eval OUT_DIRS += $(OUT)$(MODULE)) +$(eval SRC_DIRS += $(OUT)$(MODULE)) +$(eval LD_DIRS := $(LD_DIRS):$(OUT)$(MODULE)) + +# Add the defaults from this dir to rm_clean +clean: CLEAN($(OUT)$(MODULE)/*.d) CLEAN($(OUT)$(MODULE)/*.o) +clean: CLEAN($(OUT)$(MODULE)/*.debug) CLEAN($(OUT)$(MODULE)/*.test) +clean: CLEAN($(OUT)$(MODULE)/*.depends) +clean: CLEAN($(OUT)$(MODULE)/*.gcno) CLEAN($(OUT)$(MODULE)/*.gcda) +clean: CLEAN($(OUT)$(MODULE)/*.gcov) CLEAN($(OUT)lcov-coverage.info) +clean: CLEAN($(OUT)lcov-html) + +$(info + submodule: $(MODULE_NAME)) +# We must eval otherwise they may be dropped. +MODULE_C_OBJECTS = $(patsubst $(SRC)/$(MODULE)/%.c,$(MODULE)/%.o,\ + $(wildcard $(SRC)/$(MODULE)/*.c)) +$(eval $(MODULE_NAME)_C_OBJECTS ?= $(MODULE_C_OBJECTS)) +MODULE_CXX_OBJECTS = $(patsubst $(SRC)/$(MODULE)/%.cc,$(MODULE)/%.o,\ + $(wildcard $(SRC)/$(MODULE)/*.cc)) +$(eval $(MODULE_NAME)_CXX_OBJECTS ?= $(MODULE_CXX_OBJECTS)) + +# Note, $(MODULE) is implicit in the path to the %.c. +# See $(C_OBJECTS) for more details. +# Register rules for the module objects. +$(eval $(call add_object_rules,$(MODULE_C_OBJECTS),CC,c,CFLAGS,$(SRC)/)) +$(eval $(call add_object_rules,$(MODULE_CXX_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/)) + +# Continue recursive inclusion of module.mk files +SUBMODULE_DIRS = $(wildcard $(SRC)/$(MODULE)/*/module.mk) +include $(wildcard $(OUT)$(MODULE)/*.d) +include $(SUBMODULE_DIRS) + +endif +endif ## pass-to-subcall wrapper for relocating the call directory
diff --git a/fake_drv_video.c b/fake_drv_video.c new file mode 100644 index 0000000..aecbc63 --- /dev/null +++ b/fake_drv_video.c
@@ -0,0 +1,677 @@ +// Copyright 2020 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdbool.h> + +#include <va/va.h> +#include <va/va_backend.h> + +VAStatus FakeTerminate(VADriverContextP ctx) { + return VA_STATUS_SUCCESS; +} + +// Needed to be able to instantiate kCapabilities statically. +#define MAX_CAPABILITY_ATTRIBUTES 5 + +struct Capability { + VAProfile profile; + VAEntrypoint entry_point; + int num_attribs; + VAConfigAttrib attrib_list[MAX_CAPABILITY_ATTRIBUTES]; +}; +const struct Capability kCapabilities[] = { + {VAProfileVP8Version0_3, + VAEntrypointVLD, + 1, + { + {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, + }}, + {VAProfileVP8Version0_3, + VAEntrypointEncSlice, + 3, + { + {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, + {VAConfigAttribRateControl, VA_RC_CQP | VA_RC_CBR}, + {VAConfigAttribEncMaxRefFrames, 1}, + }}, + {VAProfileVP9Profile0, + VAEntrypointVLD, + 1, + { + {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, + }}, + {VAProfileVP9Profile0, + VAEntrypointEncSlice, + 3, + { + {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, + {VAConfigAttribRateControl, VA_RC_CQP | VA_RC_CBR}, + {VAConfigAttribEncMaxRefFrames, 1}, + }}, + {VAProfileVP9Profile2, + VAEntrypointVLD, + 1, + { + {VAConfigAttribRTFormat, + VA_RT_FORMAT_YUV420 | VA_RT_FORMAT_YUV420_10BPP}, + }}, + {VAProfileVP9Profile2, + VAEntrypointEncSlice, + 3, + { + {VAConfigAttribRTFormat, + VA_RT_FORMAT_YUV420 | VA_RT_FORMAT_YUV420_10BPP}, + {VAConfigAttribRateControl, VA_RC_CQP | VA_RC_CBR}, + {VAConfigAttribEncMaxRefFrames, 1}, + }}, + // JPEG is an image codec, so the encoding entry point is different. + {VAProfileJPEGBaseline, + VAEntrypointVLD, + 1, + { + {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, + }}, + {VAProfileJPEGBaseline, + VAEntrypointEncPicture, + 1, + { + {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, + }}, + // VideoProc is a special silicon area for video/image post processing, e.g. + // crop, resize, and format conversion. + {VAProfileNone, + VAEntrypointVideoProc, + 1, + { + {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, + }}, + // H264 codec profiles need the VAConfigAttribEncPackedHeaders attribute for + // encoding because Chrome will expect it. + {VAProfileH264ConstrainedBaseline, + VAEntrypointVLD, + 1, + { + {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, + }}, + {VAProfileH264ConstrainedBaseline, + VAEntrypointEncSlice, + 4, + { + {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, + {VAConfigAttribRateControl, VA_RC_CQP | VA_RC_CBR}, + {VAConfigAttribEncPackedHeaders, VA_ENC_PACKED_HEADER_NONE}, + {VAConfigAttribEncMaxRefFrames, 1}, + }}, + {VAProfileH264Main, + VAEntrypointVLD, + 1, + { + {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, + }}, + {VAProfileH264Main, + VAEntrypointEncSlice, + 4, + { + {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, + {VAConfigAttribRateControl, VA_RC_CQP | VA_RC_CBR}, + {VAConfigAttribEncPackedHeaders, VA_ENC_PACKED_HEADER_NONE}, + {VAConfigAttribEncMaxRefFrames, 1}, + }}, + {VAProfileH264High, + VAEntrypointVLD, + 1, + { + {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, + }}, + {VAProfileH264High, + VAEntrypointEncSlice, + 4, + { + {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, + {VAConfigAttribRateControl, VA_RC_CQP | VA_RC_CBR}, + {VAConfigAttribEncPackedHeaders, VA_ENC_PACKED_HEADER_NONE}, + {VAConfigAttribEncMaxRefFrames, 1}, + }}}; + +const size_t kCapabilitiesSize = + sizeof(kCapabilities) / sizeof(struct Capability); + +/** + * Original comment: + * Query supported profiles + * The caller must provide a "profile_list" array that can hold at + * least vaMaxNumProfiles() entries. The actual number of profiles + * returned in "profile_list" is returned in "num_profile". + */ +VAStatus FakeQueryConfigProfiles(VADriverContextP ctx, + VAProfile* profile_list, + int* num_profiles) { + int i = 0; + // TODO(crbug.com/1080871): consider extracting the profiles from + // kCapabilities; would need to remove duplicates. + profile_list[i++] = VAProfileJPEGBaseline; + profile_list[i++] = VAProfileH264ConstrainedBaseline; + profile_list[i++] = VAProfileH264Main; + profile_list[i++] = VAProfileH264High; + profile_list[i++] = VAProfileNone; // For video processing (e.g. cropping). + profile_list[i++] = VAProfileVP8Version0_3; + profile_list[i++] = VAProfileVP9Profile0; + profile_list[i++] = VAProfileVP9Profile2; + *num_profiles = i; + + return VA_STATUS_SUCCESS; +} + +/** + * Original comment: + * Query supported entrypoints for a given profile + * The caller must provide an "entrypoint_list" array that can hold at + * least vaMaxNumEntrypoints() entries. The actual number of entrypoints + * returned in "entrypoint_list" is returned in "num_entrypoints". + */ +VAStatus FakeQueryConfigEntrypoints(VADriverContextP ctx, + VAProfile profile, + VAEntrypoint* entrypoint_list, + int* num_entrypoints) { + *num_entrypoints = 0; + for (size_t i = 0; i < kCapabilitiesSize; ++i) { + if (kCapabilities[i].profile == profile) + entrypoint_list[(*num_entrypoints)++] = kCapabilities[i].entry_point; + } + return VA_STATUS_SUCCESS; +} + +/** + * Original comment: + * Get attributes for a given profile/entrypoint pair + * The caller must provide an "attrib_list" with all attributes to be + * retrieved. Upon return, the attributes in "attrib_list" have been + * updated with their value. Unknown attributes or attributes that are + * not supported for the given profile/entrypoint pair will have their + * value set to VA_ATTRIB_NOT_SUPPORTED + */ +VAStatus FakeGetConfigAttributes(VADriverContextP ctx, + VAProfile profile, + VAEntrypoint entrypoint, + VAConfigAttrib* attrib_list, + int num_attribs) { + + // First, try to find the |profile| and |entrypoint| entry in kCapabilities. + // If found, search for each entry in the input |attrib_list| (usually many) + // in kCapabilities[i]'s |attrib_list| (usually few), and, if found, update + // its |value|. + bool profile_found = false; + for (size_t i = 0; i < kCapabilitiesSize; ++i) { + profile_found = kCapabilities[i].profile == profile; + if (!(profile_found && kCapabilities[i].entry_point == entrypoint)) + continue; + + // Clear the |attrib_list|: sometimes it's not initialized. + for (size_t attrib = 0; attrib < num_attribs; attrib++) + attrib_list[attrib].value = VA_ATTRIB_NOT_SUPPORTED; + + for (size_t j = 0; j < kCapabilities[i].num_attribs; j++) { + for (size_t n = 0; n < num_attribs; n++) { + if (kCapabilities[i].attrib_list[j].type == attrib_list[n].type) { + attrib_list[n].value = kCapabilities[i].attrib_list[j].value; + break; + } + } + } + return VA_STATUS_SUCCESS; + } + return profile_found ? VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT + : VA_STATUS_ERROR_UNSUPPORTED_PROFILE; +} + +VAStatus FakeCreateConfig(VADriverContextP ctx, + VAProfile profile, + VAEntrypoint entrypoint, + VAConfigAttrib* attrib_list, + int num_attribs, + VAConfigID* config_id) { + *config_id = VA_INVALID_ID; + for (size_t i = 0; i < kCapabilitiesSize; ++i) { + if (!(kCapabilities[i].profile == profile && + kCapabilities[i].entry_point == entrypoint)) { + continue; + } + // TODO(crbug.com/1080871): make sure that all entries in |attrib_list| are + // also supported by kCapabilities[i].attrib_list. + + // |config_id| is also the index in kCapabilities, to simplify things. + *config_id = i; + return VA_STATUS_SUCCESS; + } + + // TODO(crbug.com/1080871): return here VA_STATUS_ERROR_UNSUPPORTED_PROFILE / + // VA_STATUS_ERROR_ATTR_NOT_SUPPORTED / VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT + // depending on what has happened in the previous for loop. + return VA_STATUS_ERROR_ALLOCATION_FAILED; +} + +/** + * Original comment: + * Query all attributes for a given configuration + * The profile of the configuration is returned in "profile" + * The entrypoint of the configuration is returned in "entrypoint" + * The caller must provide an "attrib_list" array that can hold at least + * vaMaxNumConfigAttributes() entries. The actual number of attributes + * returned in "attrib_list" is returned in "num_attribs" + */ +// Misleading function name: it queries |profile|, |entrypoint| and attributes +// (|attrib_list|) for the given |config_id|. +VAStatus FakeQueryConfigAttributes(VADriverContextP ctx, + VAConfigID config_id, + VAProfile* profile, + VAEntrypoint* entrypoint, + VAConfigAttrib* attrib_list, + int* num_attribs) { + // |config_id| is also the index in kCapabilities, to simplify things. + if (config_id >= kCapabilitiesSize) + return VA_STATUS_ERROR_INVALID_CONFIG; + + *profile = kCapabilities[config_id].profile; + *entrypoint = kCapabilities[config_id].entry_point; + *num_attribs = kCapabilities[config_id].num_attribs; + for (size_t j = 0; j < kCapabilities[config_id].num_attribs; j++) { + attrib_list[j].type = kCapabilities[config_id].attrib_list[j].type; + attrib_list[j].value = kCapabilities[config_id].attrib_list[j].value; + } + return VA_STATUS_SUCCESS; +} + +VAStatus FakeDestroyConfig(VADriverContextP ctx, VAConfigID config_id) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeCreateSurfaces(VADriverContextP ctx, + int width, + int height, + int format, + int num_surfaces, + VASurfaceID* surfaces) { + for (size_t index = 0; index < num_surfaces; ++index) + surfaces[index] = index; + + return VA_STATUS_SUCCESS; +} + +VAStatus FakeDestroySurfaces(VADriverContextP ctx, + VASurfaceID* surface_list, + int num_surfaces) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeCreateContext(VADriverContextP ctx, + VAConfigID config_id, + int picture_width, + int picture_height, + int flag, + VASurfaceID* render_targets, + int num_render_targets, + VAContextID* context) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeDestroyContext(VADriverContextP ctx, VAContextID context) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeCreateBuffer(VADriverContextP ctx, + VAContextID context, + VABufferType type, + unsigned int size, + unsigned int num_elements, + void* data, + VABufferID* buf_id) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeBufferSetNumElements(VADriverContextP ctx, + VABufferID buf_id, + unsigned int num_elements) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeMapBuffer(VADriverContextP ctx, VABufferID buf_id, void** pbuf) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeUnmapBuffer(VADriverContextP ctx, VABufferID buf_id) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeDestroyBuffer(VADriverContextP ctx, VABufferID buffer_id) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeBeginPicture(VADriverContextP ctx, + VAContextID context, + VASurfaceID render_target) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeRenderPicture(VADriverContextP ctx, + VAContextID context, + VABufferID* buffers, + int num_buffers) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeEndPicture(VADriverContextP ctx, VAContextID context) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeSyncSurface(VADriverContextP ctx, VASurfaceID render_target) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeQuerySurfaceStatus(VADriverContextP ctx, + VASurfaceID render_target, + VASurfaceStatus* status) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakePutSurface(VADriverContextP ctx, + VASurfaceID surface, + void* draw, + short srcx, + short srcy, + unsigned short srcw, + unsigned short srch, + short destx, + short desty, + unsigned short destw, + unsigned short desth, + VARectangle* cliprects, + unsigned int number_cliprects, + unsigned int flags) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeQueryImageFormats(VADriverContextP ctx, + VAImageFormat* format_list, + int* num_formats) { + *num_formats = 0; + return VA_STATUS_SUCCESS; +} + +VAStatus FakeCreateImage(VADriverContextP ctx, + VAImageFormat* format, + int width, + int height, + VAImage* image) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeDestroyImage(VADriverContextP ctx, VAImageID image) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeSetImagePalette(VADriverContextP ctx, + VAImageID image, + unsigned char* palette) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeGetImage(VADriverContextP ctx, + VASurfaceID surface, + int x, + int y, + unsigned int width, + unsigned int height, + VAImageID image) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakePutImage(VADriverContextP ctx, + VASurfaceID surface, + VAImageID image, + int src_x, + int src_y, + unsigned int src_width, + unsigned int src_height, + int dest_x, + int dest_y, + unsigned int dest_width, + unsigned int dest_height) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeDeriveImage(VADriverContextP ctx, + VASurfaceID surface, + VAImage* image) { + return VA_STATUS_SUCCESS; +} + + +VAStatus FakeQuerySubpictureFormats(VADriverContextP ctx, + VAImageFormat* format_list, + unsigned int* flags, + unsigned int* num_formats) { + *num_formats = 0; + return VA_STATUS_SUCCESS; +} + +VAStatus FakeCreateSubpicture(VADriverContextP ctx, + VAImageID image, + VASubpictureID* subpicture) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeDestroySubpicture(VADriverContextP ctx, + VASubpictureID subpicture) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeSetSubpictureImage(VADriverContextP ctx, + VASubpictureID subpicture, + VAImageID image) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeSetSubpictureChromakey(VADriverContextP ctx, + VASubpictureID subpicture, + unsigned int chromakey_min, + unsigned int chromakey_max, + unsigned int chromakey_mask) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeSetSubpictureGlobalAlpha(VADriverContextP ctx, + VASubpictureID subpicture, + float global_alpha) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeAssociateSubpicture(VADriverContextP ctx, + VASubpictureID subpicture, + VASurfaceID* target_surfaces, + int num_surfaces, + int16_t src_x, + int16_t src_y, + uint16_t src_width, + uint16_t src_height, + int16_t dest_x, + int16_t dest_y, + uint16_t dest_width, + uint16_t dest_height, + uint32_t flags) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeDeassociateSubpicture(VADriverContextP ctx, + VASubpictureID subpicture, + VASurfaceID* target_surfaces, + int num_surfaces) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeQueryDisplayAttributes(VADriverContextP ctx, + VADisplayAttribute* attr_list, + int* num_attributes) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeGetDisplayAttributes(VADriverContextP ctx, + VADisplayAttribute* attr_list, + int num_attributes) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeSetDisplayAttributes(VADriverContextP ctx, + VADisplayAttribute* attr_list, + int num_attributes) { + return VA_STATUS_SUCCESS; +} + +VAStatus FakeQuerySurfaceAttributes(VADriverContextP ctx, + VAConfigID config, + VASurfaceAttrib* attribs, + unsigned int* num_attribs) { + // This function is called once with |attribs| NULL to dimension output. The + // second time, |num_attribs| must be larger than kMaxNumSurfaceAttributes. + // See the original documentation: + // "The attrib_list array is allocated by the user and num_attribs shall be + // initialized to the number of allocated elements in that array. Upon + // successful return, the actual number of attributes will be overwritten into + // num_attribs. Otherwise, VA_STATUS_ERROR_MAX_NUM_EXCEEDED is returned and + // num_attribs is adjusted to the number of elements that would be returned if + // enough space was available." + const unsigned int kMaxNumSurfaceAttributes = 32; + if (attribs == NULL) { + *num_attribs = kMaxNumSurfaceAttributes; + return VA_STATUS_SUCCESS; + } + if (*num_attribs < kMaxNumSurfaceAttributes) { + *num_attribs = kMaxNumSurfaceAttributes; + return VA_STATUS_ERROR_MAX_NUM_EXCEEDED; + } + + // |attribs| may have a single VASurfaceAttribPixelFormat set for querying + // support for a given pixel format. Chrome doesn't support it, so we verify + // all input types are zero (VASurfaceAttribNone). + for (size_t i = 0; i < kMaxNumSurfaceAttributes; ++i) { + if (attribs[i].type != VASurfaceAttribNone) { + *num_attribs = 0; + return VA_STATUS_ERROR_ATTR_NOT_SUPPORTED; + } + } + + int i = 0; + attribs[i].type = VASurfaceAttribPixelFormat; + attribs[i].value.type = VAGenericValueTypeInteger; + attribs[i].flags = VA_SURFACE_ATTRIB_GETTABLE | VA_SURFACE_ATTRIB_SETTABLE; + attribs[i].value.value.i = VA_FOURCC_NV12; + i++; + + attribs[i].type = VASurfaceAttribPixelFormat; + attribs[i].value.type = VAGenericValueTypeInteger; + attribs[i].flags = VA_SURFACE_ATTRIB_GETTABLE | VA_SURFACE_ATTRIB_SETTABLE; + attribs[i].value.value.i = VA_FOURCC_YV12; + i++; + + attribs[i].type = VASurfaceAttribMaxWidth; + attribs[i].value.type = VAGenericValueTypeInteger; + attribs[i].flags = VA_SURFACE_ATTRIB_GETTABLE; + attribs[i].value.value.i = 1024; + i++; + + attribs[i].type = VASurfaceAttribMaxHeight; + attribs[i].value.type = VAGenericValueTypeInteger; + attribs[i].flags = VA_SURFACE_ATTRIB_GETTABLE; + attribs[i].value.value.i = 1024; + i++; + + *num_attribs = i; + return VA_STATUS_SUCCESS; +} + +VAStatus FakeCreateSurfaces2(VADriverContextP ctx, + unsigned int format, + unsigned int width, + unsigned int height, + VASurfaceID* surfaces, + unsigned int num_surfaces, + VASurfaceAttrib* attrib_list, + unsigned int num_attribs) { + return VA_STATUS_SUCCESS; +} + +#define MAX_PROFILES 8 +#define MAX_ENTRYPOINTS 8 +#define MAX_CONFIG_ATTRIBUTES 32 +#if MAX_CAPABILITY_ATTRIBUTES >= MAX_CONFIG_ATTRIBUTES +#error "MAX_CAPABILITY_ATTRIBUTES should be smaller than MAX_CONFIG_ATTRIBUTES" +#endif +#define MAX_IMAGE_FORMATS 10 +#define MAX_SUBPIC_FORMATS 6 + +#define DLL_EXPORT __attribute__((visibility("default"))) + +VAStatus DLL_EXPORT __vaDriverInit_1_0(VADriverContextP ctx) { + struct VADriverVTable* const vtable = ctx->vtable; + + ctx->version_major = VA_MAJOR_VERSION; + ctx->version_minor = VA_MINOR_VERSION; + ctx->str_vendor = "fake"; + + ctx->max_profiles = MAX_PROFILES; + ctx->max_entrypoints = MAX_ENTRYPOINTS; + ctx->max_attributes = MAX_CONFIG_ATTRIBUTES; + ctx->max_image_formats = MAX_IMAGE_FORMATS; + ctx->max_subpic_formats = MAX_SUBPIC_FORMATS; + ctx->max_display_attributes = 1; + + vtable->vaTerminate = FakeTerminate; + vtable->vaQueryConfigEntrypoints = FakeQueryConfigEntrypoints; + vtable->vaQueryConfigProfiles = FakeQueryConfigProfiles; + vtable->vaQueryConfigAttributes = FakeQueryConfigAttributes; + vtable->vaCreateConfig = FakeCreateConfig; + vtable->vaDestroyConfig = FakeDestroyConfig; + vtable->vaGetConfigAttributes = FakeGetConfigAttributes; + vtable->vaCreateSurfaces = FakeCreateSurfaces; + vtable->vaDestroySurfaces = FakeDestroySurfaces; + vtable->vaCreateContext = FakeCreateContext; + vtable->vaDestroyContext = FakeDestroyContext; + vtable->vaCreateBuffer = FakeCreateBuffer; + vtable->vaBufferSetNumElements = FakeBufferSetNumElements; + vtable->vaMapBuffer = FakeMapBuffer; + vtable->vaUnmapBuffer = FakeUnmapBuffer; + vtable->vaDestroyBuffer = FakeDestroyBuffer; + vtable->vaBeginPicture = FakeBeginPicture; + vtable->vaRenderPicture = FakeRenderPicture; + vtable->vaEndPicture = FakeEndPicture; + vtable->vaSyncSurface = FakeSyncSurface; + vtable->vaQuerySurfaceStatus = FakeQuerySurfaceStatus; + vtable->vaPutSurface = FakePutSurface; + + vtable->vaQueryImageFormats = FakeQueryImageFormats; + vtable->vaCreateImage = FakeCreateImage; + vtable->vaDeriveImage = FakeDeriveImage; + vtable->vaDestroyImage = FakeDestroyImage; + vtable->vaSetImagePalette = FakeSetImagePalette; + vtable->vaGetImage = FakeGetImage; + vtable->vaPutImage = FakePutImage; + + vtable->vaQuerySubpictureFormats = FakeQuerySubpictureFormats; + vtable->vaCreateSubpicture = FakeCreateSubpicture; + vtable->vaDestroySubpicture = FakeDestroySubpicture; + vtable->vaSetSubpictureImage = FakeSetSubpictureImage; + vtable->vaSetSubpictureChromakey = FakeSetSubpictureChromakey; + vtable->vaSetSubpictureGlobalAlpha = FakeSetSubpictureGlobalAlpha; + vtable->vaAssociateSubpicture = FakeAssociateSubpicture; + vtable->vaDeassociateSubpicture = FakeDeassociateSubpicture; + vtable->vaQueryDisplayAttributes = FakeQueryDisplayAttributes; + vtable->vaGetDisplayAttributes = FakeGetDisplayAttributes; + vtable->vaSetDisplayAttributes = FakeSetDisplayAttributes; + + // Not needed by va_openDriver(), but by Chrome to enumerate profiles and + // other advanced functionality. + vtable->vaQuerySurfaceAttributes = FakeQuerySurfaceAttributes; + vtable->vaCreateSurfaces2 = FakeCreateSurfaces2; + + return VA_STATUS_SUCCESS; +}