Merge GfxStreamBackend into virtio-gpu-gfxstream-renderer.h
These two both expose the C API of the library, and we want to move to a
stream_renderer_init instead of gfxstream_backend_init.
Keeps around GfxStreamBackend.h temporarily until downstreams have
replaced their references.
Bug: b/249823013
Test: presubmit & downstream
Change-Id: If919766d2d61694cea3b8cf5291fb5823f26b355
diff --git a/stream-servers/virtio-gpu-gfxstream-renderer.cpp b/stream-servers/virtio-gpu-gfxstream-renderer.cpp
index 0f00ece..3e0fe3f 100644
--- a/stream-servers/virtio-gpu-gfxstream-renderer.cpp
+++ b/stream-servers/virtio-gpu-gfxstream-renderer.cpp
@@ -17,8 +17,11 @@
#include <type_traits>
#include <unordered_map>
+#include "FrameBuffer.h"
+#include "GfxStreamAgents.h"
#include "VirtioGpuTimelines.h"
#include "aemu/base/AlignedBuf.h"
+#include "aemu/base/Metrics.h"
#include "aemu/base/synchronization/Lock.h"
#include "aemu/base/memory/SharedMemory.h"
#include "aemu/base/ManagedDescriptor.hpp"
@@ -28,11 +31,16 @@
#include "host-common/HostmemIdMapping.h"
#include "host-common/address_space_device.h"
#include "host-common/android_pipe_common.h"
+#include "host-common/android_pipe_device.h"
#include "host-common/feature_control.h"
+#include "host-common/globals.h"
#include "host-common/linux_types.h"
#include "host-common/opengles.h"
+#include "host-common/opengles-pipe.h"
+#include "host-common/refcount-pipe.h"
#include "host-common/vm_operations.h"
#include "virtgpu_gfxstream_protocol.h"
+#include "vk_util.h"
extern "C" {
#include "virtio-gpu-gfxstream-renderer.h"
@@ -70,6 +78,23 @@
#endif // !VIRTIO_GOLDFISH_EXPORT_API
+#define GFXSTREAM_DEBUG_LEVEL 1
+
+#if GFXSTREAM_DEBUG_LEVEL >= 1
+#define GFXS_LOG(fmt, ...) \
+ do { \
+ fprintf(stdout, "%s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__); \
+ fflush(stdout); \
+ } while (0)
+
+#else
+#define GFXS_LOG(fmt,...)
+#endif
+
+#define POST_CALLBACK_DISPLAY_TYPE_X 0
+#define POST_CALLBACK_DISPLAY_TYPE_WAYLAND_SHARED_MEM 1
+#define POST_CALLBACK_DISPLAY_TYPE_WINDOWS_HWND 2
+
// Virtio Goldfish Pipe: Overview-----------------------------------------------
//
// Virtio Goldfish Pipe is meant for running goldfish pipe services with a
@@ -148,10 +173,12 @@
// linear buffer if necessary, and then perform a corresponding pip operation
// based on the box parameter's x and width values.
+using android::AndroidPipe;
using android::base::AutoLock;
using android::base::DescriptorType;
using android::base::Lock;
using android::base::ManagedDescriptor;
+using android::base::MetricsLogger;
using android::base::SharedMemory;
using android::emulation::HostmemIdMapping;
@@ -1928,6 +1955,453 @@
return sRenderer()->vulkanInfo(res_handle, vulkan_info);
}
+struct renderer_display_info;
+typedef void (*get_pixels_t)(void*, uint32_t, uint32_t);
+static get_pixels_t sGetPixelsFunc = 0;
+typedef void (*post_callback_t)(void*, uint32_t, int, int, int, int, int, unsigned char*);
+
+// For reading back rendered contents to display
+VG_EXPORT void get_pixels(void* pixels, uint32_t bytes);
+
+static const GoldfishPipeServiceOps goldfish_pipe_service_ops = {
+ // guest_open()
+ [](GoldfishHwPipe* hwPipe) -> GoldfishHostPipe* {
+ return static_cast<GoldfishHostPipe*>(
+ android_pipe_guest_open(hwPipe));
+ },
+ // guest_open_with_flags()
+ [](GoldfishHwPipe* hwPipe, uint32_t flags) -> GoldfishHostPipe* {
+ return static_cast<GoldfishHostPipe*>(
+ android_pipe_guest_open_with_flags(hwPipe, flags));
+ },
+ // guest_close()
+ [](GoldfishHostPipe* hostPipe, GoldfishPipeCloseReason reason) {
+ static_assert((int)GOLDFISH_PIPE_CLOSE_GRACEFUL ==
+ (int)PIPE_CLOSE_GRACEFUL,
+ "Invalid PIPE_CLOSE_GRACEFUL value");
+ static_assert(
+ (int)GOLDFISH_PIPE_CLOSE_REBOOT == (int)PIPE_CLOSE_REBOOT,
+ "Invalid PIPE_CLOSE_REBOOT value");
+ static_assert((int)GOLDFISH_PIPE_CLOSE_LOAD_SNAPSHOT ==
+ (int)PIPE_CLOSE_LOAD_SNAPSHOT,
+ "Invalid PIPE_CLOSE_LOAD_SNAPSHOT value");
+ static_assert(
+ (int)GOLDFISH_PIPE_CLOSE_ERROR == (int)PIPE_CLOSE_ERROR,
+ "Invalid PIPE_CLOSE_ERROR value");
+
+ android_pipe_guest_close(hostPipe,
+ static_cast<PipeCloseReason>(reason));
+ },
+ // guest_pre_load()
+ [](QEMUFile* file) { (void)file; },
+ // guest_post_load()
+ [](QEMUFile* file) { (void)file; },
+ // guest_pre_save()
+ [](QEMUFile* file) { (void)file; },
+ // guest_post_save()
+ [](QEMUFile* file) { (void)file; },
+ // guest_load()
+ [](QEMUFile* file,
+ GoldfishHwPipe* hwPipe,
+ char* force_close) -> GoldfishHostPipe* {
+ (void)file;
+ (void)hwPipe;
+ (void)force_close;
+ return nullptr;
+ },
+ // guest_save()
+ [](GoldfishHostPipe* hostPipe, QEMUFile* file) {
+ (void)hostPipe;
+ (void)file;
+ },
+ // guest_poll()
+ [](GoldfishHostPipe* hostPipe) {
+ static_assert((int)GOLDFISH_PIPE_POLL_IN == (int)PIPE_POLL_IN,
+ "invalid POLL_IN values");
+ static_assert((int)GOLDFISH_PIPE_POLL_OUT == (int)PIPE_POLL_OUT,
+ "invalid POLL_OUT values");
+ static_assert((int)GOLDFISH_PIPE_POLL_HUP == (int)PIPE_POLL_HUP,
+ "invalid POLL_HUP values");
+
+ return static_cast<GoldfishPipePollFlags>(
+ android_pipe_guest_poll(hostPipe));
+ },
+ // guest_recv()
+ [](GoldfishHostPipe* hostPipe,
+ GoldfishPipeBuffer* buffers,
+ int numBuffers) -> int {
+ // NOTE: Assumes that AndroidPipeBuffer and GoldfishPipeBuffer
+ // have exactly the same layout.
+ static_assert(
+ sizeof(AndroidPipeBuffer) == sizeof(GoldfishPipeBuffer),
+ "Invalid PipeBuffer sizes");
+ // We can't use a static_assert with offsetof() because in msvc, it uses
+ // reinterpret_cast.
+ // TODO: Add runtime assertion instead?
+ // https://developercommunity.visualstudio.com/content/problem/22196/static-assert-cannot-compile-constexprs-method-tha.html
+#ifndef _MSC_VER
+ static_assert(offsetof(AndroidPipeBuffer, data) ==
+ offsetof(GoldfishPipeBuffer, data),
+ "Invalid PipeBuffer::data offsets");
+ static_assert(offsetof(AndroidPipeBuffer, size) ==
+ offsetof(GoldfishPipeBuffer, size),
+ "Invalid PipeBuffer::size offsets");
+#endif
+ return android_pipe_guest_recv(
+ hostPipe, reinterpret_cast<AndroidPipeBuffer*>(buffers),
+ numBuffers);
+ },
+ // guest_send()
+ [](GoldfishHostPipe** hostPipe,
+ const GoldfishPipeBuffer* buffers,
+ int numBuffers) -> int {
+ return android_pipe_guest_send(
+ reinterpret_cast<void**>(hostPipe),
+ reinterpret_cast<const AndroidPipeBuffer*>(buffers),
+ numBuffers);
+ },
+ // guest_wake_on()
+ [](GoldfishHostPipe* hostPipe, GoldfishPipeWakeFlags wakeFlags) {
+ android_pipe_guest_wake_on(hostPipe, static_cast<int>(wakeFlags));
+ },
+ // dma_add_buffer()
+ [](void* pipe, uint64_t paddr, uint64_t sz) {
+ // not considered for virtio
+ },
+ // dma_remove_buffer()
+ [](uint64_t paddr) {
+ // not considered for virtio
+ },
+ // dma_invalidate_host_mappings()
+ []() {
+ // not considered for virtio
+ },
+ // dma_reset_host_mappings()
+ []() {
+ // not considered for virtio
+ },
+ // dma_save_mappings()
+ [](QEMUFile* file) {
+ (void)file;
+ },
+ // dma_load_mappings()
+ [](QEMUFile* file) {
+ (void)file;
+ },
+};
+
+extern const QAndroidVmOperations* const gQAndroidVmOperations;
+
+static void default_post_callback(
+ void* context, uint32_t displayId, int width, int height, int ydir, int format, int frame_type, unsigned char* pixels) {
+ (void)context;
+ (void)width;
+ (void)height;
+ (void)ydir;
+ (void)format;
+ (void)frame_type;
+ (void)pixels;
+ // no-op
+}
+
+VG_EXPORT void gfxstream_backend_init(
+ uint32_t display_width,
+ uint32_t display_height,
+ uint32_t display_type,
+ void* renderer_cookie,
+ int renderer_flags,
+ struct virgl_renderer_callbacks* virglrenderer_callbacks,
+ struct gfxstream_callbacks* gfxstreamcallbacks) {
+
+ // Set metrics callbacks
+ if (gfxstreamcallbacks) {
+ if (gfxstreamcallbacks->add_instant_event) {
+ MetricsLogger::add_instant_event_callback =
+ gfxstreamcallbacks->add_instant_event;
+ }
+ if (gfxstreamcallbacks->add_instant_event_with_metric) {
+ MetricsLogger::add_instant_event_with_metric_callback =
+ gfxstreamcallbacks->add_instant_event_with_metric;
+ }
+ if (gfxstreamcallbacks->add_instant_event_with_descriptor) {
+ MetricsLogger::add_instant_event_with_descriptor_callback =
+ gfxstreamcallbacks->add_instant_event_with_descriptor;
+ }
+ if (gfxstreamcallbacks->add_vulkan_out_of_memory_event) {
+ MetricsLogger::add_vulkan_out_of_memory_event =
+ gfxstreamcallbacks->add_vulkan_out_of_memory_event;
+ }
+ if (gfxstreamcallbacks->set_annotation) {
+ MetricsLogger::set_crash_annotation_callback =
+ gfxstreamcallbacks->set_annotation;
+ }
+ if (gfxstreamcallbacks->abort) {
+ emugl::setDieFunction(gfxstreamcallbacks->abort);
+ }
+ }
+
+ // Set non product-specific callbacks
+ vk_util::setVkCheckCallbacks(
+ std::make_unique<vk_util::VkCheckCallbacks>(vk_util::VkCheckCallbacks{
+ .onVkErrorOutOfMemory =
+ [](VkResult result, const char* function, int line) {
+ auto fb = FrameBuffer::getFB();
+ if (!fb) {
+ ERR("FrameBuffer not yet initialized. Dropping out of memory event");
+ return;
+ }
+ fb->logVulkanOutOfMemory(result, function, line);
+ },
+ .onVkErrorOutOfMemoryOnAllocation =
+ [](VkResult result, const char* function, int line,
+ std::optional<uint64_t> allocationSize) {
+ auto fb = FrameBuffer::getFB();
+ if (!fb) {
+ ERR("FrameBuffer not yet initialized. Dropping out of memory event");
+ return;
+ }
+ fb->logVulkanOutOfMemory(result, function, line, allocationSize);
+ }}));
+
+ gfxstream_backend_init_product_override();
+ // First we make some agents available.
+
+ GFXS_LOG("start. display dimensions: width %u height %u, renderer flags: 0x%x", display_width,
+ display_height, renderer_flags);
+
+ // Flags processing
+
+ // TODO: hook up "gfxstream egl" to the renderer flags
+ // GFXSTREAM_RENDERER_FLAGS_USE_EGL_BIT in crosvm
+ // as it's specified from launch_cvd.
+ // At the moment, use ANDROID_GFXSTREAM_EGL=1
+ // For test on GCE
+ if (android::base::getEnvironmentVariable("ANDROID_GFXSTREAM_EGL") == "1") {
+ android::base::setEnvironmentVariable("ANDROID_EGL_ON_EGL", "1");
+ android::base::setEnvironmentVariable("ANDROID_EMUGL_LOG_PRINT", "1");
+ android::base::setEnvironmentVariable("ANDROID_EMUGL_VERBOSE", "1");
+ }
+ // end for test on GCE
+
+ android::base::setEnvironmentVariable("ANDROID_EMU_HEADLESS", "1");
+ android::base::setEnvironmentVariable("ANDROID_EMU_SANDBOX", "1");
+ bool enableVk =
+ !(renderer_flags & GFXSTREAM_RENDERER_FLAGS_NO_VK_BIT);
+
+ bool egl2eglByEnv = android::base::getEnvironmentVariable("ANDROID_EGL_ON_EGL") == "1";
+ bool egl2eglByFlag = renderer_flags & GFXSTREAM_RENDERER_FLAGS_USE_EGL_BIT;
+ bool enable_egl2egl = egl2eglByFlag || egl2eglByEnv;
+ if (enable_egl2egl) {
+ android::base::setEnvironmentVariable("ANDROID_GFXSTREAM_EGL", "1");
+ android::base::setEnvironmentVariable("ANDROID_EGL_ON_EGL", "1");
+ }
+
+ bool surfaceless =
+ renderer_flags & GFXSTREAM_RENDERER_FLAGS_USE_SURFACELESS_BIT;
+ bool enableGlEs31Flag = renderer_flags & GFXSTREAM_RENDERER_FLAGS_ENABLE_GLES31_BIT;
+ bool guestUsesAngle = renderer_flags & GFXSTREAM_RENDERER_FLAGS_GUEST_USES_ANGLE;
+ bool useVulkanNativeSwapchain =
+ renderer_flags & GFXSTREAM_RENDERER_FLAGS_VULKAN_NATIVE_SWAPCHAIN_BIT;
+
+ GFXS_LOG("Vulkan enabled? %d", enableVk);
+ GFXS_LOG("egl2egl enabled? %d", enable_egl2egl);
+ GFXS_LOG("surfaceless? %d", surfaceless);
+ GFXS_LOG("OpenGL ES 3.1 enabled? %d", enableGlEs31Flag);
+ GFXS_LOG("guest using ANGLE? %d", guestUsesAngle);
+ GFXS_LOG("use Vulkan native swapchain on the host? %d",
+ useVulkanNativeSwapchain);
+
+ // Need to manually set the GLES backend paths in gfxstream environment
+ // because the library search paths are not automatically set to include
+ // the directory in whioch the GLES backend resides.
+#if defined(__linux__)
+#define GFXSTREAM_LIB_SUFFIX ".so"
+#elif defined(__APPLE__)
+#define GFXSTREAM_LIB_SUFFIX ".dylib"
+#else // Windows
+#define GFXSTREAM_LIB_SUFFIX ".dll"
+#endif
+
+ feature_set_enabled_override(
+ kFeature_GLPipeChecksum, false);
+ feature_set_enabled_override(
+ kFeature_GLESDynamicVersion, true);
+ feature_set_enabled_override(
+ kFeature_PlayStoreImage, !enableGlEs31Flag);
+ feature_set_enabled_override(
+ kFeature_GLDMA, false);
+ feature_set_enabled_override(
+ kFeature_GLAsyncSwap, false);
+ feature_set_enabled_override(
+ kFeature_RefCountPipe, false);
+ feature_set_enabled_override(
+ kFeature_NoDelayCloseColorBuffer, true);
+ feature_set_enabled_override(
+ kFeature_NativeTextureDecompression, false);
+ feature_set_enabled_override(
+ kFeature_GLDirectMem, false);
+ feature_set_enabled_override(
+ kFeature_Vulkan, enableVk);
+ feature_set_enabled_override(
+ kFeature_VulkanSnapshots, false);
+ feature_set_enabled_override(
+ kFeature_VulkanNullOptionalStrings, true);
+ feature_set_enabled_override(
+ kFeature_VulkanShaderFloat16Int8, true);
+ feature_set_enabled_override(
+ kFeature_HostComposition, true);
+ feature_set_enabled_override(
+ kFeature_VulkanIgnoredHandles, true);
+ feature_set_enabled_override(
+ kFeature_VirtioGpuNext, true);
+ feature_set_enabled_override(
+ kFeature_VirtioGpuNativeSync, true);
+ feature_set_enabled_override(
+ kFeature_GuestUsesAngle, guestUsesAngle);
+ feature_set_enabled_override(
+ kFeature_VulkanQueueSubmitWithCommands, true);
+ feature_set_enabled_override(kFeature_VulkanNativeSwapchain,
+ useVulkanNativeSwapchain);
+ feature_set_enabled_override(
+ kFeature_VulkanBatchedDescriptorSetUpdate, true);
+ // TODO: Strictly speaking, renderer_flags check is insufficient because
+ // fence contexts require us to be running a new-enough guest kernel.
+ feature_set_enabled_override(
+ kFeature_VirtioGpuFenceContexts,
+ (renderer_flags & GFXSTREAM_RENDERER_FLAGS_ASYNC_FENCE_CB));
+ feature_set_enabled_override(kFeature_VulkanAstcLdrEmulation, true);
+ feature_set_enabled_override(kFeature_VulkanEtc2Emulation, true);
+ feature_set_enabled_override(kFeature_VulkanYcbcrEmulation, false);
+ feature_set_enabled_override(kFeature_ExternalBlob, false);
+
+ android::featurecontrol::productFeatureOverride();
+
+ if (useVulkanNativeSwapchain && !enableVk) {
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) <<
+ "can't enable vulkan native swapchain, Vulkan is disabled";
+ }
+
+ emugl::vkDispatch(false /* don't use test ICD */);
+
+ auto androidHw = aemu_get_android_hw();
+
+ androidHw->hw_gltransport_asg_writeBufferSize = 1048576;
+ androidHw->hw_gltransport_asg_writeStepSize = 262144;
+ androidHw->hw_gltransport_asg_dataRingSize = 524288;
+ androidHw->hw_gltransport_drawFlushInterval = 10000;
+
+ EmuglConfig config;
+
+ // Make all the console agents available.
+ android::emulation::injectGraphicsAgents(android::emulation::GfxStreamGraphicsAgentFactory());
+
+ emuglConfig_init(&config, true /* gpu enabled */, "auto",
+ enable_egl2egl ? "swiftshader_indirect" : "host",
+ 64, /* bitness */
+ surfaceless, /* no window */
+ false, /* blocklisted */
+ false, /* has guest renderer */
+ WINSYS_GLESBACKEND_PREFERENCE_AUTO,
+ true /* force host gpu vulkan */);
+
+ emuglConfig_setupEnv(&config);
+
+ android_prepareOpenglesEmulation();
+
+ {
+ static emugl::RenderLibPtr renderLibPtr = initLibrary();
+ void* egldispatch = renderLibPtr->getEGLDispatch();
+ void* glesv2Dispatch = renderLibPtr->getGLESv2Dispatch();
+ android_setOpenglesEmulation(
+ renderLibPtr.get(), egldispatch, glesv2Dispatch);
+ }
+
+ int maj;
+ int min;
+ android_startOpenglesRenderer(
+ display_width, display_height, 1, 28,
+ getGraphicsAgents()->vm,
+ getGraphicsAgents()->emu,
+ getGraphicsAgents()->multi_display,
+ &maj, &min);
+
+ char* vendor = nullptr;
+ char* renderer = nullptr;
+ char* version = nullptr;
+
+ android_getOpenglesHardwareStrings(
+ &vendor, &renderer, &version);
+
+ GFXS_LOG("GL strings; [%s] [%s] [%s].\n",
+ vendor, renderer, version);
+
+ auto openglesRenderer = android_getOpenglesRenderer();
+
+ if (!openglesRenderer) {
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "No renderer started, fatal";
+ }
+
+ address_space_set_vm_operations(getGraphicsAgents()->vm);
+ android_init_opengles_pipe();
+ android_opengles_pipe_set_recv_mode(2 /* virtio-gpu */);
+ android_init_refcount_pipe();
+
+ sGetPixelsFunc = android_getReadPixelsFunc();
+
+ pipe_virgl_renderer_init(renderer_cookie, renderer_flags, virglrenderer_callbacks);
+
+ GFXS_LOG("Started renderer");
+}
+
+VG_EXPORT void gfxstream_backend_setup_window(
+ void* native_window_handle,
+ int32_t window_x,
+ int32_t window_y,
+ int32_t window_width,
+ int32_t window_height,
+ int32_t fb_width,
+ int32_t fb_height) {
+ android_showOpenglesWindow(native_window_handle, window_x, window_y,
+ window_width, window_height, fb_width, fb_height,
+ 1.0f, 0, false, false);
+}
+
+VG_EXPORT void gfxstream_backend_teardown() {
+ android_finishOpenglesRenderer();
+ android_hideOpenglesWindow();
+ android_stopOpenglesRenderer(true);
+}
+
+VG_EXPORT void gfxstream_backend_set_screen_mask(int width, int height, const unsigned char* rgbaData) {
+ android_setOpenglesScreenMask(width, height, rgbaData);
+}
+
+VG_EXPORT void get_pixels(void* pixels, uint32_t bytes) {
+ //TODO: support display > 0
+ sGetPixelsFunc(pixels, bytes, 0);
+}
+
+VG_EXPORT void gfxstream_backend_getrender(char* buf, size_t bufSize, size_t* size) {
+ const char* render = "";
+ FrameBuffer* pFB = FrameBuffer::getFB();
+ if (pFB) {
+ const char* vendor = nullptr;
+ const char* version = nullptr;
+ pFB->getGLStrings(&vendor, &render, &version);
+ }
+ if (!buf || bufSize==0) {
+ if (size) *size = strlen(render);
+ return;
+ }
+ *buf = '\0';
+ strncat(buf, render, bufSize - 1);
+ if (size) *size = strlen(buf);
+}
+
+const GoldfishPipeServiceOps* goldfish_pipe_get_service_ops() {
+ return &goldfish_pipe_service_ops;
+}
+
#define VIRGLRENDERER_API_PIPE_STRUCT_DEF(api) pipe_##api,
static struct virgl_renderer_virtio_interface s_virtio_interface = {