Merge remote-tracking branch 'aosp/upstream-main' am: 197d810946

Original change: https://android-review.googlesource.com/c/platform/external/minigbm/+/3204910

Change-Id: I3812b7b1fc5dbc6a842e7a8dead63b895e623775
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Jason Macnak 2024-08-02 15:32:55 +00:00 committed by Automerger Merge Worker
commit 105245d6e3
5 changed files with 320 additions and 33 deletions

View file

@ -1,4 +1,4 @@
# Copyright 2012 The Chromium OS Authors. All rights reserved.
# Copyright 2012 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
@ -63,7 +63,7 @@
#
# Possible command line variables:
# - COLOR=[0|1] to set ANSI color output (default: 1)
# - VERBOSE=[0|1] to hide/show commands (default: 0)
# - VERBOSE=[0|1] V=[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
@ -98,9 +98,11 @@ SPLITDEBUG ?= 0
NOSTRIP ?= 1
VALGRIND ?= 0
COLOR ?= 1
VERBOSE ?= 0
V ?= 0
VERBOSE ?= $(V)
MODE ?= opt
CXXEXCEPTIONS ?= 0
RUN_TESTS ?= 1
ARCH ?= $(shell uname -m)
# Put objects in a separate tree based on makefile locations
@ -258,13 +260,13 @@ $(eval $(call override_var,STRIP,strip))
RMDIR ?= rmdir
ECHO = /bin/echo -e
ifeq ($(lastword $(subst /, ,$(CC))),clang)
ifeq ($(filter clang,$(subst -, ,$(notdir $(CC)))),clang)
CDRIVER = clang
else
CDRIVER = gcc
endif
ifeq ($(lastword $(subst /, ,$(CXX))),clang++)
ifeq ($(filter clang++,$(subst -, ,$(notdir $(CXX)))),clang++)
CXXDRIVER = clang
else
CXXDRIVER = gcc
@ -310,12 +312,29 @@ endif
# CXXFLAGS := $(filter-out badflag,$(CXXFLAGS)) # Filter out a value
# The same goes for CFLAGS.
COMMON_CFLAGS-gcc := -fvisibility=internal -ggdb3 -Wa,--noexecstack
COMMON_CFLAGS-clang := -fvisibility=hidden -ggdb
COMMON_CFLAGS := -Wall -Werror -fno-strict-aliasing $(SSP_CFLAGS) -O1 -Wformat=2
CXXFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CXXDRIVER))
CFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CDRIVER))
# minigbm: Disable -Wimplicit-fallthrough to unbreak compilation.
COMMON_CFLAGS-clang := -fvisibility=hidden -ggdb \
-Wstring-plus-int
# When a class is exported through __attribute__((visibility("default"))), we
# still want to eliminate symbols from inline class member functions to reduce
# symbol resolution overhead. Therefore, pass -fvisibility-inlines-hidden in
# addition to -fvisibility=hidden. (go/cros-symbol-slimming)
# minigbm: Disable -Wunreachable-code to unbreak compilation.
COMMON_CFLAGS := -Wall -Wunused -Wno-unused-parameter \
-Wbool-operation -Wstring-compare -Wxor-used-as-pow \
-Wint-in-bool-context -Wfree-nonheap-object \
-Werror -Wformat=2 -fno-strict-aliasing -fvisibility-inlines-hidden \
$(SSP_CFLAGS) -O1
CXXFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CXXDRIVER)) -std=gnu++20
CFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CDRIVER)) -std=gnu17
# We undefine _FORTIFY_SOURCE because some distros enable it by default in
# their toolchains. This makes the compiler issue warnings about redefines
# and our -Werror usage breaks it all.
CPPFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3
# Enable large file support.
CPPFLAGS += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
# Disable exceptions based on the CXXEXCEPTIONS setting.
ifeq ($(CXXEXCEPTIONS),0)
CXXFLAGS := $(CXXFLAGS) -fno-exceptions -fno-unwind-tables \
@ -340,7 +359,11 @@ ifeq ($(MODE),profiling)
LDFLAGS := $(LDFLAGS) --coverage
endif
LDFLAGS := $(LDFLAGS) -Wl,-z,relro -Wl,-z,noexecstack -Wl,-z,now
# Pass -Bsymbolic-non-weak which pre-binds symbols in the same DSO to improve
# startup performance. We don't support interposing non-weak symbols.
# (go/cros-symbol-slimming)
LDFLAGS := $(LDFLAGS) -Wl,-z,relro -Wl,-z,noexecstack -Wl,-z,now \
-Wl,-Bsymbolic-non-weak
# Fancy helpers for color if a prompt is defined
ifeq ($(COLOR),1)
@ -510,8 +533,11 @@ CC_STATIC_LIBARY(%):
$(error Typo alert! LIBARY != LIBRARY)
TEST(%): % qemu_chroot_install
TEST(%): %
$(call TEST_implementation)
ifneq ($(RUN_TESTS),0)
TEST(%): qemu_chroot_install
endif
.PHONY: TEST
# multiple targets with a wildcard need to share an directory.
@ -577,13 +603,12 @@ $(1): %.o: %.pic.o %.pie.o
$$(QUIET)touch "$$@"
endef
# Wrap all the deps in $$(wildcard) so a missing header won't cause weirdness.
# First we remove newlines and \, then wrap it.
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
@ -722,7 +747,9 @@ ifeq ($(MODE),profiling)
fi
@$(ECHO) "COVERAGE [$(COLOR_YELLOW)FINISHED$(COLOR_RESET)]"
endif
.PHONY: tests
# Standard name everyone else uses.
check: tests
.PHONY: check tests
qemu_chroot_install:
ifeq ($(USE_QEMU),1)
@ -795,12 +822,17 @@ ifeq ($(VALGRIND),1)
VALGRIND_CMD = /usr/bin/valgrind --tool=memcheck $(VALGRIND_ARGS) --
endif
ifneq ($(RUN_TESTS),0)
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
else
define TEST_implementation
endef
endif
define TEST_setup
@$(ECHO) -n "TEST $(TARGET_OR_MEMBER) "

147
external/virtgpu_gfxstream_protocol.h vendored Normal file
View file

@ -0,0 +1,147 @@
// Copyright 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef VIRTGPU_GFXSTREAM_PROTOCOL_H
#define VIRTGPU_GFXSTREAM_PROTOCOL_H
#include <stdint.h>
// See definitions in rutabaga_gfx_ffi.h
#define VIRTGPU_CAPSET_VIRGL 1
#define VIRTGPU_CAPSET_VIRGL2 2
#define VIRTGPU_CAPSET_GFXSTREAM_VULKAN 3
#define VIRTGPU_CAPSET_VENUS 4
#define VIRTGPU_CAPSET_CROSS_DOMAIN 5
#define VIRTGPU_CAPSET_DRM 6
#define VIRTGPU_CAPSET_GFXSTREAM_MAGMA 7
#define VIRTGPU_CAPSET_GFXSTREAM_GLES 8
#define VIRTGPU_CAPSET_GFXSTREAM_COMPOSER 9
// Address Space Graphics contexts
#define GFXSTREAM_CONTEXT_CREATE 0x1001
#define GFXSTREAM_CONTEXT_PING 0x1002
#define GFXSTREAM_CONTEXT_PING_WITH_RESPONSE 0x1003
// Native Sync FD
#define GFXSTREAM_CREATE_EXPORT_SYNC 0x9000
#define GFXSTREAM_CREATE_IMPORT_SYNC 0x9001
// Vulkan Sync
#define GFXSTREAM_CREATE_EXPORT_SYNC_VK 0xa000
#define GFXSTREAM_CREATE_IMPORT_SYNC_VK 0xa001
#define GFXSTREAM_CREATE_QSRI_EXPORT_VK 0xa002
#define GFXSTREAM_RESOURCE_CREATE_3D 0xa003
// clang-format off
// A placeholder command to ensure virtio-gpu completes
#define GFXSTREAM_PLACEHOLDER_COMMAND_VK 0xf002
// clang-format on
struct gfxstreamHeader {
uint32_t opCode;
};
struct gfxstreamContextCreate {
struct gfxstreamHeader hdr;
uint32_t resourceId;
};
struct gfxstreamContextPing {
struct gfxstreamHeader hdr;
uint32_t resourceId;
};
struct gfxstreamCreateExportSync {
struct gfxstreamHeader hdr;
uint32_t syncHandleLo;
uint32_t syncHandleHi;
};
struct gfxstreamCreateExportSyncVK {
struct gfxstreamHeader hdr;
uint32_t deviceHandleLo;
uint32_t deviceHandleHi;
uint32_t fenceHandleLo;
uint32_t fenceHandleHi;
};
struct gfxstreamCreateQSRIExportVK {
struct gfxstreamHeader hdr;
uint32_t imageHandleLo;
uint32_t imageHandleHi;
};
struct gfxstreamPlaceholderCommandVk {
struct gfxstreamHeader hdr;
uint32_t pad;
uint32_t padding;
};
struct gfxstreamResourceCreate3d {
struct gfxstreamHeader hdr;
uint32_t target;
uint32_t format;
uint32_t bind;
uint32_t width;
uint32_t height;
uint32_t depth;
uint32_t arraySize;
uint32_t lastLevel;
uint32_t nrSamples;
uint32_t flags;
uint32_t pad;
uint64_t blobId;
};
struct vulkanCapset {
uint32_t protocolVersion;
// ASG Ring Parameters
uint32_t ringSize;
uint32_t bufferSize;
uint32_t colorBufferMemoryIndex;
uint32_t deferredMapping;
uint32_t blobAlignment;
uint32_t noRenderControlEnc;
uint32_t alwaysBlob;
uint32_t externalSync;
uint32_t virglSupportedFormats[16];
};
struct magmaCapset {
uint32_t protocolVersion;
// ASG Ring Parameters
uint32_t ringSize;
uint32_t bufferSize;
uint32_t blobAlignment;
};
struct glesCapset {
uint32_t protocolVersion;
// ASG Ring Parameters
uint32_t ringSize;
uint32_t bufferSize;
uint32_t blobAlignment;
};
struct composerCapset {
uint32_t protocolVersion;
// ASG Ring Parameters
uint32_t ringSize;
uint32_t bufferSize;
uint32_t blobAlignment;
};
#endif

View file

@ -10,9 +10,13 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#if !defined(ANDROID) || (ANDROID_API_LEVEL >= 31 && defined(HAS_DMABUF_SYSTEM_HEAP))
#include <linux/dma-heap.h>
#endif
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <xf86drm.h>
@ -67,6 +71,10 @@
#define USE_EXTRA_PADDING_FOR_YVU420
#endif
struct mediatek_private_drv_data {
int dma_heap_fd;
};
struct mediatek_private_map_data {
void *cached_addr;
void *gem_addr;
@ -120,6 +128,16 @@ static bool is_video_yuv_format(uint32_t format)
static int mediatek_init(struct driver *drv)
{
struct format_metadata metadata;
struct mediatek_private_drv_data *priv;
priv = calloc(1, sizeof(*priv));
if (!priv) {
drv_loge("Failed calloc private data, errno=%d\n", -errno);
return -errno;
}
priv->dma_heap_fd = -1;
drv->priv = priv;
drv_add_combinations(drv, render_target_formats, ARRAY_SIZE(render_target_formats),
&LINEAR_METADATA,
@ -199,6 +217,17 @@ static int mediatek_init(struct driver *drv)
return drv_modify_linear_combinations(drv);
}
static void mediatek_close(struct driver *drv)
{
struct mediatek_private_drv_data *priv = (struct mediatek_private_drv_data *)drv->priv;
if (priv->dma_heap_fd >= 0)
close(priv->dma_heap_fd);
free(priv);
drv->priv = NULL;
}
static int mediatek_bo_create_with_modifiers(struct bo *bo, uint32_t width, uint32_t height,
uint32_t format, const uint64_t *modifiers,
uint32_t count)
@ -207,13 +236,17 @@ static int mediatek_bo_create_with_modifiers(struct bo *bo, uint32_t width, uint
size_t plane;
uint32_t stride;
struct drm_mtk_gem_create gem_create = { 0 };
const bool is_camera_write = bo->meta.use_flags & BO_USE_CAMERA_WRITE;
const bool is_hw_video_encoder = bo->meta.use_flags & BO_USE_HW_VIDEO_ENCODER;
const bool is_linear = bo->meta.use_flags & BO_USE_LINEAR;
const bool is_protected = bo->meta.use_flags & BO_USE_PROTECTED;
const bool is_scanout = bo->meta.use_flags & BO_USE_SCANOUT;
/*
* We identify the ChromeOS Camera App buffers via these two USE flags. Those buffers need
* the same alignment as the video hardware encoding.
*/
const bool is_camera_preview =
(bo->meta.use_flags & BO_USE_SCANOUT) && (bo->meta.use_flags & BO_USE_CAMERA_WRITE);
const bool is_hw_video_encoder = bo->meta.use_flags & BO_USE_HW_VIDEO_ENCODER;
const bool is_camera_preview = is_scanout && is_camera_write;
#ifdef MTK_MT8173
const bool is_mt8173_video_decoder = bo->meta.use_flags & BO_USE_HW_VIDEO_DECODER;
#else
@ -325,8 +358,16 @@ static int mediatek_bo_create_with_modifiers(struct bo *bo, uint32_t width, uint
#endif
}
/* For protected data buffer needs to be allocated from GEM */
if (bo->meta.use_flags & BO_USE_PROTECTED) {
/* For protected data buffer needs to allocate from DMA_HEAP directly */
if (is_protected) {
#if !defined(ANDROID) || (ANDROID_API_LEVEL >= 31 && defined(HAS_DMABUF_SYSTEM_HEAP))
int ret;
struct mediatek_private_drv_data *priv = (struct mediatek_private_drv_data *)bo->drv->priv;
struct dma_heap_allocation_data heap_data = {
.len = bo->meta.total_size,
.fd_flags = O_RDWR | O_CLOEXEC,
};
if (format == DRM_FORMAT_P010) {
/*
* Adjust the size so we don't waste tons of space. This was allocated
@ -340,9 +381,51 @@ static int mediatek_bo_create_with_modifiers(struct bo *bo, uint32_t width, uint
bo->meta.offsets[1] = bo->meta.sizes[0];
bo->meta.total_size = bo->meta.total_size * 10 / 16;
}
gem_create.flags |= DRM_MTK_GEM_CREATE_ENCRYPTED;
if (priv->dma_heap_fd < 0) {
priv->dma_heap_fd = open("/dev/dma_heap/restricted_mtk_cma", O_RDWR | O_CLOEXEC);
if (priv->dma_heap_fd < 0) {
drv_loge("Failed opening secure CMA heap errno=%d\n", -errno);
return -errno;
}
}
ret = ioctl(priv->dma_heap_fd, DMA_HEAP_IOCTL_ALLOC, &heap_data);
if (ret < 0) {
drv_loge("Failed allocating CMA buffer ret=%d\n", ret);
return ret;
}
/* Create GEM handle for secure CMA and close FD here */
ret = drmPrimeFDToHandle(bo->drv->fd, heap_data.fd, &bo->handle.u32);
close(heap_data.fd);
if (ret) {
drv_loge("Failed drmPrimeFDToHandle(fd:%d) ret=%d\n", heap_data.fd, ret);
return ret;
}
#else
drv_loge("Protected allocation not supported\n");
return -1;
#endif
return 0;
}
/*
* For linear scanout buffers, the read/write pattern is usually linear i.e. each address is
* accessed sequentially, and there are fewer chances that an address will be repeatedly
* accessed.
* This behavior leads to less TLB dependency and cache misses i.e. no need to translate the
* same virtual address to a physical address multiple times.
*
* With that premise, it's safe to allow the DMA framework to fulfill such allocation
* requests with non-continuous smaller chunks of memory (e.g., 4KiB single pages) which
* are generally easier to allocate compared to large continuous chunks of memory, improving
* memory allocation efficiency and reduce the risk of allocation failures, especially when
* available memory budget is low or on memory-constrained devices.
*/
if (is_linear && is_scanout)
gem_create.flags |= DRM_MTK_GEM_CREATE_FLAG_ALLOC_SINGLE_PAGES;
gem_create.size = bo->meta.total_size;
ret = drmIoctl(bo->drv->fd, DRM_IOCTL_MTK_GEM_CREATE, &gem_create);
@ -539,6 +622,7 @@ static void mediatek_resolve_format_and_use_flags(struct driver *drv, uint32_t f
const struct backend backend_mediatek = {
.name = "mediatek",
.init = mediatek_init,
.close = mediatek_close,
.bo_create = mediatek_bo_create,
.bo_create_with_modifiers = mediatek_bo_create_with_modifiers,
.bo_destroy = drv_gem_bo_destroy,

View file

@ -26,6 +26,6 @@ enum virtgpu_param_id {
#define VIRTIO_GPU_CAPSET_VIRGL 1
#define VIRTIO_GPU_CAPSET_VIRGL2 2
#define VIRTIO_GPU_CAPSET_GFXSTREAM 3
#define VIRTIO_GPU_CAPSET_GFXSTREAM_VULKAN 3
#define VIRTIO_GPU_CAPSET_VENUS 4
#define VIRTIO_GPU_CAPSET_CROSS_DOMAIN 5

View file

@ -21,6 +21,7 @@
#include "external/virgl_hw.h"
#include "external/virgl_protocol.h"
#include "external/virtgpu_drm.h"
#include "external/virtgpu_gfxstream_protocol.h"
#include "util.h"
#include "virtgpu.h"
@ -71,6 +72,8 @@ struct virgl_blob_metadata_cache {
struct virgl_priv {
int caps_is_v2;
union virgl_caps caps;
int caps_is_gfxstream;
struct vulkanCapset gfxstream_vulkan_caps;
int host_gbm_enabled;
atomic_int next_blob_id;
@ -330,6 +333,17 @@ static bool virgl_supports_combination_natively(struct driver *drv, uint32_t drm
{
struct virgl_priv *priv = (struct virgl_priv *)drv->priv;
if (priv->caps_is_gfxstream) {
// If the data is invalid or an older version just accept all formats as previously
if (priv->gfxstream_vulkan_caps.protocolVersion == 0 ||
priv->gfxstream_vulkan_caps.virglSupportedFormats[0] == 0)
return true;
bool supported_format = virgl_bitmask_supports_format(
(struct virgl_supported_format_mask *)&priv->gfxstream_vulkan_caps
.virglSupportedFormats[0],
drm_format);
return supported_format;
}
if (priv->caps.max_version == 0)
return true;
@ -559,34 +573,42 @@ static uint32_t virgl_3d_get_max_texture_2d_size(struct driver *drv)
return UINT32_MAX;
}
static int virgl_get_caps(struct driver *drv, union virgl_caps *caps, int *caps_is_v2)
static int virgl_get_caps(struct driver *drv, struct virgl_priv *priv)
{
int ret;
struct drm_virtgpu_get_caps cap_args = { 0 };
memset(caps, 0, sizeof(union virgl_caps));
*caps_is_v2 = 0;
memset(&priv->caps, 0, sizeof(union virgl_caps));
priv->caps_is_v2 = 0;
memset(&priv->gfxstream_vulkan_caps, 0, sizeof(struct vulkanCapset));
if (params[param_supported_capset_ids].value) {
drv_logi("Supported CAPSET IDs: %u.", params[param_supported_capset_ids].value);
if (params[param_supported_capset_ids].value & (1 << VIRTIO_GPU_CAPSET_VIRGL2)) {
*caps_is_v2 = 1;
priv->caps_is_v2 = 1;
} else if (params[param_supported_capset_ids].value &
(1 << VIRTIO_GPU_CAPSET_VIRGL)) {
*caps_is_v2 = 0;
priv->caps_is_v2 = 0;
} else if (params[param_supported_capset_ids].value &
(1 << VIRTIO_GPU_CAPSET_GFXSTREAM_VULKAN)) {
priv->caps_is_gfxstream = 1;
} else {
drv_logi("Unrecognized CAPSET IDs: %u. Assuming all zero caps.",
params[param_supported_capset_ids].value);
return 0;
}
} else if (params[param_capset_fix].value) {
*caps_is_v2 = 1;
priv->caps_is_v2 = 1;
}
cap_args.addr = (unsigned long long)caps;
if (*caps_is_v2) {
cap_args.addr = (unsigned long long)&priv->caps;
if (priv->caps_is_v2) {
cap_args.cap_set_id = VIRTIO_GPU_CAPSET_VIRGL2;
cap_args.size = sizeof(union virgl_caps);
} else if (priv->caps_is_gfxstream) {
cap_args.addr = (unsigned long long)&priv->gfxstream_vulkan_caps;
cap_args.cap_set_id = VIRTIO_GPU_CAPSET_GFXSTREAM_VULKAN;
cap_args.size = sizeof(struct vulkanCapset);
} else {
cap_args.cap_set_id = VIRTIO_GPU_CAPSET_VIRGL;
cap_args.size = sizeof(struct virgl_caps_v1);
@ -595,7 +617,9 @@ static int virgl_get_caps(struct driver *drv, union virgl_caps *caps, int *caps_
ret = drmIoctl(drv->fd, DRM_IOCTL_VIRTGPU_GET_CAPS, &cap_args);
if (ret) {
drv_loge("DRM_IOCTL_VIRTGPU_GET_CAPS failed with %s\n", strerror(errno));
*caps_is_v2 = 0;
priv->caps_is_v2 = 0;
priv->caps_is_gfxstream = 0;
cap_args.addr = (unsigned long long)&priv->caps;
// Fallback to v1
cap_args.cap_set_id = VIRTIO_GPU_CAPSET_VIRGL;
@ -613,7 +637,7 @@ static void virgl_init_params_and_caps(struct driver *drv)
{
struct virgl_priv *priv = (struct virgl_priv *)drv->priv;
if (params[param_3d].value) {
virgl_get_caps(drv, &priv->caps, &priv->caps_is_v2);
virgl_get_caps(drv, priv);
// We use two criteria to determine whether host minigbm is used on the host for
// swapchain allocations.
@ -656,7 +680,7 @@ static int virgl_init(struct driver *drv)
BO_USE_TEXTURE_MASK);
virgl_add_combinations(drv, depth_stencil_formats,
ARRAY_SIZE(depth_stencil_formats), &LINEAR_METADATA,
BO_USE_RENDER_MASK | BO_USE_TEXTURE_MASK);
BO_USE_GPU_HW);
/* NV12 with scanout must flow through virgl_add_combination, so that the native
* support is checked and scanout use_flag can be conditionally stripped. */
virgl_add_combination(drv, DRM_FORMAT_NV12, &LINEAR_METADATA,