blob: 2612053b35f7378ee573f05f9f0a80c1ccdcf5b3 [file] [log] [blame]
// 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);
}
}
// 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");
android::base::setEnvironmentVariable("ANDROID_EMUGL_FIXED_BACKEND_LIST", "1");
bool vkDisabledByEnv = android::base::getEnvironmentVariable("ANDROID_EMU_DISABLE_VULKAN") == "1";
bool vkDisabledByFlag =
(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_RgtcTextureSupport, true);
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);
// 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,
!syncFdDisabledByFlag &&
(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;
}