// Copyright (C) 2018 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. #include "base/MemStream.h" #include "base/PathUtils.h" #include "base/System.h" #include "host-common/address_space_device.h" #include "host-common/address_space_device.hpp" #include "host-common/address_space_graphics.h" #include "host-common/address_space_graphics_types.h" #include "host-common/AndroidPipe.h" #include "host-common/android_pipe_device.h" #include "host-common/vm_operations.h" #include "host-common/window_agent.h" #include "host-common/HostmemIdMapping.h" #include "host-common/FeatureControl.h" #include "host-common/feature_control.h" #include "host-common/opengl/emugl_config.h" #include "host-common/opengles-pipe.h" #include "host-common/opengles.h" #include "host-common/refcount-pipe.h" #include "host-common/globals.h" #include "snapshot/interface.h" #include #include #include #include "VulkanDispatch.h" #include "GfxStreamAgents.h" #include "render_api.h" #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 extern "C" { #include "host-common/goldfish_pipe.h" #include "virtio-gpu-gfxstream-renderer.h" } // extern "C" using android::AndroidPipe; using android::base::pj; #ifdef _WIN32 #define VG_EXPORT __declspec(dllexport) #else #define VG_EXPORT __attribute__((visibility("default"))) #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 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*); extern "C" 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); extern "C" 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); // For reading back rendered contents to display extern "C" 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( android_pipe_guest_open(hwPipe)); }, // guest_open_with_flags() [](GoldfishHwPipe* hwPipe, uint32_t flags) -> GoldfishHostPipe* { return static_cast( 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(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( 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(buffers), numBuffers); }, // guest_send() [](GoldfishHostPipe** hostPipe, const GoldfishPipeBuffer* buffers, int numBuffers) -> int { return android_pipe_guest_send( reinterpret_cast(hostPipe), reinterpret_cast(buffers), numBuffers); }, // guest_wake_on() [](GoldfishHostPipe* hostPipe, GoldfishPipeWakeFlags wakeFlags) { android_pipe_guest_wake_on(hostPipe, static_cast(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 set_post_callback(struct renderer_display_info* r, post_callback_t func, uint32_t display_type); 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 } uint32_t sBackendFlags = 0; enum BackendFlags { GFXSTREAM_BACKEND_FLAGS_NO_VK_BIT = 1 << 0, GFXSTREAM_BACKEND_FLAGS_EGL2EGL_BIT = 1 << 1, }; // based on VIRGL_RENDERER_USE* and friends enum RendererFlags { GFXSTREAM_RENDERER_FLAGS_USE_EGL_BIT = 1 << 0, GFXSTREAM_RENDERER_FLAGS_THREAD_SYNC = 1 << 1, GFXSTREAM_RENDERER_FLAGS_USE_GLX_BIT = 1 << 2, GFXSTREAM_RENDERER_FLAGS_USE_SURFACELESS_BIT = 1 << 3, GFXSTREAM_RENDERER_FLAGS_USE_GLES_BIT = 1 << 4, GFXSTREAM_RENDERER_FLAGS_NO_VK_BIT = 1 << 5, // for disabling vk GFXSTREAM_RENDERER_FLAGS_IGNORE_HOST_GL_ERRORS_BIT = 1 << 6, // control IgnoreHostOpenGLErrors flag GFXSTREAM_RENDERER_FLAGS_NATIVE_TEXTURE_DECOMPRESSION_BIT = 1 << 7, // Attempt GPU texture decompression GFXSTREAM_RENDERER_FLAGS_ENABLE_BPTC_TEXTURES_BIT = 1 << 8, // enable BPTC texture support if available GFXSTREAM_RENDERER_FLAGS_ENABLE_GLES31_BIT = 1 << 9, // disables the PlayStoreImage flag GFXSTREAM_RENDERER_FLAGS_ENABLE_S3TC_TEXTURES_BIT = 1 << 10, // enable S3TC texture support if available GFXSTREAM_RENDERER_FLAGS_NO_SYNCFD_BIT = 1 << 20, // for disabling syncfd GFXSTREAM_RENDERER_FLAGS_GUEST_USES_ANGLE = 1 << 21, GFXSTREAM_RENDERER_FLAGS_VULKAN_NATIVE_SWAPCHAIN_BIT = 1 << 22, }; // Sets backend flags for different kinds of initialization. // Default (and default if not called): flags == 0 // Needs to be called before |gfxstream_backend_init|. extern "C" VG_EXPORT void gfxstream_backend_set_flags(uint32_t flags) { sBackendFlags = flags; } extern "C" 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) { // First we make some agents available. GFXS_LOG("start. display dimensions: width %u height %u. backend flags: 0x%x renderer flags: 0x%x", display_width, display_height, sBackendFlags, renderer_flags); AvdInfo** avdInfoPtr = aemu_get_android_avdInfoPtr(); (*avdInfoPtr) = avdInfo_newCustom( "goldfish_opengl_test", 28, "x86_64", "x86_64", true /* is google APIs */, AVD_PHONE); // 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"); android::base::setEnvironmentVariable("ANDROID_EMUGL_FIXED_BACKEND_LIST", "1"); bool vkDisabledByEnv = android::base::getEnvironmentVariable("ANDROID_EMU_DISABLE_VULKAN") == "1"; bool vkDisabledByFlag = (sBackendFlags & GFXSTREAM_BACKEND_FLAGS_NO_VK_BIT) || (renderer_flags & GFXSTREAM_RENDERER_FLAGS_NO_VK_BIT); bool enableVk = !vkDisabledByEnv && !vkDisabledByFlag; 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 ignoreHostGlErrorsFlag = renderer_flags & GFXSTREAM_RENDERER_FLAGS_IGNORE_HOST_GL_ERRORS_BIT; bool nativeTextureDecompression = renderer_flags & GFXSTREAM_RENDERER_FLAGS_NATIVE_TEXTURE_DECOMPRESSION_BIT; bool bptcTextureSupport = renderer_flags & GFXSTREAM_RENDERER_FLAGS_ENABLE_BPTC_TEXTURES_BIT; bool s3tcTextureSupport = renderer_flags & GFXSTREAM_RENDERER_FLAGS_ENABLE_S3TC_TEXTURES_BIT; bool syncFdDisabledByFlag = renderer_flags & GFXSTREAM_RENDERER_FLAGS_NO_SYNCFD_BIT; 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("ignore host gl errors enabled? %d", ignoreHostGlErrorsFlag); GFXS_LOG("syncfd enabled? %d", !syncFdDisabledByFlag); GFXS_LOG("use native texture decompression if available? %d", nativeTextureDecompression); GFXS_LOG("enable BPTC support if available? %d", bptcTextureSupport); GFXS_LOG("enable S3TC support if available? %d", s3tcTextureSupport); 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_IgnoreHostOpenGLErrors, ignoreHostGlErrorsFlag); feature_set_enabled_override( kFeature_NativeTextureDecompression, nativeTextureDecompression); feature_set_enabled_override( kFeature_BptcTextureSupport, bptcTextureSupport); feature_set_enabled_override( kFeature_S3tcTextureSupport, s3tcTextureSupport); 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, !syncFdDisabledByFlag); 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); if (useVulkanNativeSwapchain && !enableVk) { fprintf(stderr, "%s: can't enable vulkan native swapchain, Vulkan is disabled, " "fatal\n", __func__); abort(); } 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::injectConsoleAgents(android::emulation::GfxStreamAndroidConsoleFactory()); emuglConfig_init(&config, true /* gpu enabled */, "auto", enable_egl2egl ? "swiftshader_indirect" : "host", 64, /* bitness */ surfaceless, /* no window */ false, /* blacklisted */ 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, getConsoleAgents()->vm, getConsoleAgents()->emu, getConsoleAgents()->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) { fprintf(stderr, "%s: no renderer started, fatal\n", __func__); abort(); } address_space_set_vm_operations(getConsoleAgents()->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"); if (surfaceless) { set_post_callback(nullptr, default_post_callback, display_type); } } extern "C" 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); } static void set_post_callback(struct renderer_display_info* r, post_callback_t func, uint32_t display_type) { // crosvm needs bgra readback depending on the display type bool use_bgra_readback = false; switch (display_type) { case POST_CALLBACK_DISPLAY_TYPE_X: GFXS_LOG("using display type: X11"); use_bgra_readback = true; break; case POST_CALLBACK_DISPLAY_TYPE_WAYLAND_SHARED_MEM: GFXS_LOG("using display type: wayland shared mem"); break; case POST_CALLBACK_DISPLAY_TYPE_WINDOWS_HWND: GFXS_LOG("using display type: windows hwnd"); break; default: break; } android_setPostCallback(func, r, false, 0); } extern "C" VG_EXPORT void gfxstream_backend_teardown() { android_finishOpenglesRenderer(); android_hideOpenglesWindow(); android_stopOpenglesRenderer(true); } extern "C" VG_EXPORT void gfxstream_backend_set_screen_mask(int width, int height, const unsigned char* rgbaData) { android_setOpenglesScreenMask(width, height, rgbaData); } extern "C" VG_EXPORT void get_pixels(void* pixels, uint32_t bytes) { //TODO: support display > 0 sGetPixelsFunc(pixels, bytes, 0); } extern "C" const GoldfishPipeServiceOps* goldfish_pipe_get_service_ops() { return &goldfish_pipe_service_ops; }