| // 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 "GfxStreamBackend.h" |
| |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <fstream> |
| #include <string> |
| |
| #include "FrameBuffer.h" |
| #include "GfxStreamAgents.h" |
| #include "VkCommonOperations.h" |
| #include "VulkanDispatch.h" |
| #include "base/MemStream.h" |
| #include "base/Metrics.h" |
| #include "base/PathUtils.h" |
| #include "base/System.h" |
| #include "host-common/AndroidPipe.h" |
| #include "host-common/FeatureControl.h" |
| #include "host-common/GfxstreamFatalError.h" |
| #include "host-common/HostmemIdMapping.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/android_pipe_device.h" |
| #include "host-common/feature_control.h" |
| #include "host-common/globals.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/vm_operations.h" |
| #include "host-common/window_agent.h" |
| #include "render_api.h" |
| #include "snapshot/interface.h" |
| #include "vk_util.h" |
| |
| using emugl::ABORT_REASON_OTHER; |
| using emugl::FatalError; |
| |
| #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; |
| using android::base::MetricsLogger; |
| |
| #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*); |
| |
| // 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<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 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 |
| } |
| |
| 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, |
| 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->set_annotation) { |
| MetricsLogger::set_crash_annotation_callback = |
| gfxstreamcallbacks->set_annotation; |
| } |
| if (gfxstreamcallbacks->abort) { |
| emugl::setDieFunction(gfxstreamcallbacks->abort); |
| } |
| } |
| |
| 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, /* 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, |
| 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"); |
| |
| 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) { |
| switch (display_type) { |
| case POST_CALLBACK_DISPLAY_TYPE_X: |
| GFXS_LOG("using display type: X11"); |
| 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" 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); |
| } |
| |
| extern "C" const GoldfishPipeServiceOps* goldfish_pipe_get_service_ops() { |
| return &goldfish_pipe_service_ops; |
| } |
| |