You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
694 lines
21 KiB
694 lines
21 KiB
// Copyright 2020 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 "opengles.h"
|
|
|
|
#include "base/GLObjectCounter.h"
|
|
#include "base/PathUtils.h"
|
|
#include "base/Stream.h"
|
|
#include "base/MemoryTracker.h"
|
|
#include "base/SharedLibrary.h"
|
|
#include "base/System.h"
|
|
#include "host-common/address_space_device.h"
|
|
#include "host-common/address_space_graphics.h"
|
|
#include "host-common/address_space_graphics_types.h"
|
|
#include "host-common/GoldfishDma.h"
|
|
#include "host-common/RefcountPipe.h"
|
|
#include "host-common/FeatureControl.h"
|
|
#include "host-common/globals.h"
|
|
#include "host-common/opengl/emugl_config.h"
|
|
#include "host-common/opengl/GLProcessPipe.h"
|
|
#include "host-common/opengl/logger.h"
|
|
#include "host-common/opengl/gpuinfo.h"
|
|
|
|
#include "../stream-servers/render_api_functions.h"
|
|
#include "../stream-servers/OpenGLESDispatch/EGLDispatch.h"
|
|
#include "../stream-servers/OpenGLESDispatch/GLESv2Dispatch.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#define D(...)
|
|
#define DD(...)
|
|
#define E(...)
|
|
|
|
// #define D(...) do { \
|
|
// VERBOSE_PRINT(init,__VA_ARGS__); \
|
|
// android_opengl_logger_write(__VA_ARGS__); \
|
|
// } while(0);
|
|
//
|
|
// #define DD(...) do { \
|
|
// VERBOSE_PRINT(gles,__VA_ARGS__); \
|
|
// android_opengl_logger_write(__VA_ARGS__); \
|
|
// } while(0);
|
|
//
|
|
// #define E(fmt,...) do { \
|
|
// derror(fmt, ##__VA_ARGS__); \
|
|
// android_opengl_logger_write(fmt "\n", ##__VA_ARGS__); \
|
|
// } while(0);
|
|
|
|
using android::base::pj;
|
|
using android::base::SharedLibrary;
|
|
using android::emulation::asg::AddressSpaceGraphicsContext;
|
|
using android::emulation::asg::ConsumerInterface;
|
|
using android::emulation::asg::ConsumerCallbacks;
|
|
|
|
/* Name of the GLES rendering library we're going to use */
|
|
#define RENDERER_LIB_NAME "libOpenglRender"
|
|
|
|
/* Declared in "android/globals.h" */
|
|
int android_gles_fast_pipes = 1;
|
|
|
|
// Define the Render API function pointers.
|
|
#define FUNCTION_(ret, name, sig, params) \
|
|
inline ret (*name) sig = NULL;
|
|
LIST_RENDER_API_FUNCTIONS(FUNCTION_)
|
|
#undef FUNCTION_
|
|
|
|
static bool sOpenglLoggerInitialized = false;
|
|
static bool sRendererUsesSubWindow = false;
|
|
static bool sEgl2egl = false;
|
|
static emugl::RenderLib* sRenderLib = nullptr;
|
|
static emugl::RendererPtr sRenderer = nullptr;
|
|
|
|
static const EGLDispatch* sEgl = nullptr;
|
|
static const GLESv2Dispatch* sGlesv2 = nullptr;
|
|
|
|
int android_prepareOpenglesEmulation() {
|
|
android_init_opengl_logger();
|
|
|
|
bool glFineLogging = android::base::getEnvironmentVariable("ANDROID_EMUGL_FINE_LOG") == "1";
|
|
bool glLogPrinting = android::base::getEnvironmentVariable("ANDROID_EMUGL_LOG_PRINT") == "1";
|
|
|
|
AndroidOpenglLoggerFlags loggerFlags =
|
|
static_cast<AndroidOpenglLoggerFlags>(
|
|
(glFineLogging ? OPENGL_LOGGER_DO_FINE_LOGGING : 0) |
|
|
(glLogPrinting ? OPENGL_LOGGER_PRINT_TO_STDOUT : 0));
|
|
|
|
android_opengl_logger_set_flags(loggerFlags);
|
|
|
|
sOpenglLoggerInitialized = true;
|
|
sRendererUsesSubWindow = true;
|
|
|
|
sEgl2egl = false;
|
|
if (android::base::getEnvironmentVariable("ANDROID_EGL_ON_EGL") == "1") {
|
|
sEgl2egl = true;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
//
|
|
// char* error = NULL;
|
|
//
|
|
// if (sRenderLib != NULL)
|
|
// return 0;
|
|
//
|
|
// D("Initializing hardware OpenGLES emulation support");
|
|
//
|
|
// SharedLibrary* rendererSo =
|
|
// SharedLibrary::open(RENDERER_LIB_NAME);
|
|
// if (rendererSo == NULL) {
|
|
// E("Could not load OpenGLES emulation library [%s]: %s",
|
|
// RENDERER_LIB_NAME, error);
|
|
//
|
|
// E("Retrying in program directory/lib64...");
|
|
//
|
|
// auto progDir = System::get()->getProgramDirectory();
|
|
//
|
|
// auto retryLibPath =
|
|
// pj(progDir, "lib64", RENDERER_LIB_NAME);
|
|
//
|
|
// rendererSo = adynamicLibrary_open(retryLibPath.c_str(), &error);
|
|
//
|
|
// if (rendererSo == nullptr) {
|
|
// E("Could not load OpenGLES emulation library [%s]: %s (2nd try)",
|
|
// retryLibPath.c_str(), error);
|
|
// return -1;
|
|
// }
|
|
// }
|
|
//
|
|
// /* Resolve the functions */
|
|
// if (initOpenglesEmulationFuncs(rendererSo) < 0) {
|
|
// E("OpenGLES emulation library mismatch. Be sure to use the correct version!");
|
|
// crashhandler_append_message_format(
|
|
// "OpenGLES emulation library mismatch. Be sure to use the correct version!");
|
|
// goto BAD_EXIT;
|
|
// }
|
|
//
|
|
// sRenderLib = initLibrary();
|
|
// if (!sRenderLib) {
|
|
// E("OpenGLES initialization failed!");
|
|
// crashhandler_append_message_format("OpenGLES initialization failed!");
|
|
// goto BAD_EXIT;
|
|
// }
|
|
//
|
|
// sRendererUsesSubWindow = true;
|
|
// if (const char* env = getenv("ANDROID_GL_SOFTWARE_RENDERER")) {
|
|
// if (env[0] != '\0' && env[0] != '0') {
|
|
// sRendererUsesSubWindow = false;
|
|
// }
|
|
// }
|
|
//
|
|
// sEgl2egl = false;
|
|
// if (const char* env = getenv("ANDROID_EGL_ON_EGL")) {
|
|
// if (env[0] != '\0' && env[0] == '1') {
|
|
// sEgl2egl = true;
|
|
// }
|
|
// }
|
|
//
|
|
// sEgl = (const EGLDispatch *)sRenderLib->getEGLDispatch();
|
|
// sGlesv2 = (const GLESv2Dispatch *)sRenderLib->getGLESv2Dispatch();
|
|
//
|
|
// return 0;
|
|
//
|
|
// BAD_EXIT:
|
|
// E("OpenGLES emulation library could not be initialized!");
|
|
// adynamicLibrary_close(rendererSo);
|
|
// return -1;
|
|
//
|
|
|
|
int android_setOpenglesEmulation(void* renderLib, void* eglDispatch, void* glesv2Dispatch) {
|
|
sRenderLib = (emugl::RenderLib*)renderLib;
|
|
sEgl = (EGLDispatch*)eglDispatch;
|
|
sGlesv2 = (GLESv2Dispatch*)glesv2Dispatch;
|
|
sEgl2egl = false;
|
|
if (android::base::getEnvironmentVariable("ANDROID_EGL_ON_EGL") == "1") {
|
|
sEgl2egl = true;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int android_initOpenglesEmulation() {
|
|
fprintf(stderr, "%s: Not meant to call android_initOpenglesEmulation in the new build\n", __func__);
|
|
abort();
|
|
}
|
|
|
|
int
|
|
android_startOpenglesRenderer(int width, int height, bool guestPhoneApi, int guestApiLevel,
|
|
const QAndroidVmOperations *vm_operations,
|
|
const QAndroidEmulatorWindowAgent *window_agent,
|
|
const QAndroidMultiDisplayAgent *multi_display_agent,
|
|
int* glesMajorVersion_out,
|
|
int* glesMinorVersion_out)
|
|
{
|
|
if (!sRenderLib) {
|
|
D("Can't start OpenGLES renderer without support libraries");
|
|
return -1;
|
|
}
|
|
|
|
if (!sEgl) {
|
|
D("Can't start OpenGLES renderer without EGL libraries");
|
|
return -1;
|
|
}
|
|
|
|
if (!sGlesv2) {
|
|
D("Can't start OpenGLES renderer without GLES libraries");
|
|
return -1;
|
|
}
|
|
|
|
if (sRenderer) {
|
|
return 0;
|
|
}
|
|
|
|
const GpuInfoList& gpuList = globalGpuInfoList();
|
|
std::string gpuInfoAsString = gpuList.dump();
|
|
android_opengl_logger_write("%s: gpu info", __func__);
|
|
android_opengl_logger_write("%s", gpuInfoAsString.c_str());
|
|
|
|
sRenderLib->setRenderer(emuglConfig_get_current_renderer());
|
|
sRenderLib->setAvdInfo(guestPhoneApi, guestApiLevel);
|
|
// sRenderLib->setCrashReporter(&crashhandler_die_format);
|
|
// sRenderLib->setFeatureController(&android::featurecontrol::isEnabled);
|
|
sRenderLib->setSyncDevice(goldfish_sync_create_timeline,
|
|
goldfish_sync_create_fence,
|
|
goldfish_sync_timeline_inc,
|
|
goldfish_sync_destroy_timeline,
|
|
goldfish_sync_register_trigger_wait,
|
|
goldfish_sync_device_exists);
|
|
|
|
emugl_logger_struct logfuncs;
|
|
logfuncs.coarse = android_opengl_logger_write;
|
|
logfuncs.fine = android_opengl_cxt_logger_write;
|
|
sRenderLib->setLogger(logfuncs);
|
|
sRenderLib->setGLObjectCounter(android::base::GLObjectCounter::get());
|
|
emugl_dma_ops dma_ops;
|
|
dma_ops.get_host_addr = android_goldfish_dma_ops.get_host_addr;
|
|
dma_ops.unlock = android_goldfish_dma_ops.unlock;
|
|
sRenderLib->setDmaOps(dma_ops);
|
|
sRenderLib->setVmOps(*vm_operations);
|
|
sRenderLib->setAddressSpaceDeviceControlOps(get_address_space_device_control_ops());
|
|
sRenderLib->setWindowOps(*window_agent, *multi_display_agent);
|
|
// sRenderLib->setUsageTracker(android::base::CpuUsage::get(),
|
|
// android::base::MemoryTracker::get());
|
|
|
|
sRenderer = sRenderLib->initRenderer(width, height, sRendererUsesSubWindow, sEgl2egl);
|
|
|
|
// android::snapshot::Snapshotter::get().addOperationCallback(
|
|
// [](android::snapshot::Snapshotter::Operation op,
|
|
// android::snapshot::Snapshotter::Stage stage) {
|
|
// sRenderer->snapshotOperationCallback(op, stage);
|
|
// });
|
|
|
|
android::emulation::registerOnLastRefCallback(
|
|
sRenderLib->getOnLastColorBufferRef());
|
|
|
|
ConsumerInterface iface = {
|
|
// create
|
|
[](struct asg_context context,
|
|
android::base::Stream* loadStream,
|
|
ConsumerCallbacks callbacks) {
|
|
return sRenderer->addressSpaceGraphicsConsumerCreate(
|
|
context, loadStream, callbacks);
|
|
},
|
|
// destroy
|
|
[](void* consumer) {
|
|
sRenderer->addressSpaceGraphicsConsumerDestroy(consumer);
|
|
},
|
|
// pre save
|
|
[](void* consumer) {
|
|
sRenderer->addressSpaceGraphicsConsumerPreSave(consumer);
|
|
},
|
|
// global presave
|
|
[]() {
|
|
sRenderer->pauseAllPreSave();
|
|
},
|
|
// save
|
|
[](void* consumer, android::base::Stream* stream) {
|
|
sRenderer->addressSpaceGraphicsConsumerSave(consumer, stream);
|
|
},
|
|
// global postsave
|
|
[]() {
|
|
sRenderer->resumeAll();
|
|
},
|
|
// postSave
|
|
[](void* consumer) {
|
|
sRenderer->addressSpaceGraphicsConsumerPostSave(consumer);
|
|
},
|
|
// postLoad
|
|
[](void* consumer) {
|
|
sRenderer->addressSpaceGraphicsConsumerRegisterPostLoadRenderThread(consumer);
|
|
},
|
|
// global preload
|
|
[]() {
|
|
// This wants to address that when using asg, pipe wants to clean
|
|
// up all render threads and wait for gl objects, but framebuffer
|
|
// notices that there is a render thread info that is still not
|
|
// cleaned up because these render threads come from asg.
|
|
android::opengl::forEachProcessPipeIdRunAndErase([](uint64_t id) {
|
|
android_cleanupProcGLObjects(id);
|
|
});
|
|
android_waitForOpenglesProcessCleanup();
|
|
},
|
|
};
|
|
AddressSpaceGraphicsContext::setConsumer(iface);
|
|
|
|
if (!sRenderer) {
|
|
D("Can't start OpenGLES renderer?");
|
|
return -1;
|
|
}
|
|
|
|
// after initRenderer is a success, the maximum GLES API is calculated depending
|
|
// on feature control and host GPU support. Set the obtained GLES version here.
|
|
if (glesMajorVersion_out && glesMinorVersion_out)
|
|
sRenderLib->getGlesVersion(glesMajorVersion_out, glesMinorVersion_out);
|
|
return 0;
|
|
}
|
|
|
|
bool
|
|
android_asyncReadbackSupported() {
|
|
if (sRenderer) {
|
|
return sRenderer->asyncReadbackSupported();
|
|
} else {
|
|
D("tried to query async readback support "
|
|
"before renderer initialized. Likely guest rendering");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void
|
|
android_setPostCallback(OnPostFunc onPost, void* onPostContext, bool useBgraReadback, uint32_t displayId)
|
|
{
|
|
if (sRenderer) {
|
|
sRenderer->setPostCallback(onPost, onPostContext, useBgraReadback, displayId);
|
|
}
|
|
}
|
|
|
|
ReadPixelsFunc android_getReadPixelsFunc() {
|
|
if (sRenderer) {
|
|
return sRenderer->getReadPixelsCallback();
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
FlushReadPixelPipeline android_getFlushReadPixelPipeline() {
|
|
if (sRenderer) {
|
|
return sRenderer->getFlushReadPixelPipeline();
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
static char* strdupBaseString(const char* src) {
|
|
const char* begin = strchr(src, '(');
|
|
if (!begin) {
|
|
return strdup(src);
|
|
}
|
|
|
|
const char* end = strrchr(begin + 1, ')');
|
|
if (!end) {
|
|
return strdup(src);
|
|
}
|
|
|
|
// src is of the form:
|
|
// "foo (barzzzzzzzzzz)"
|
|
// ^ ^
|
|
// (b+1) e
|
|
// = 5 18
|
|
int len;
|
|
begin += 1;
|
|
len = end - begin;
|
|
|
|
char* result;
|
|
result = (char*)malloc(len + 1);
|
|
memcpy(result, begin, len);
|
|
result[len] = '\0';
|
|
return result;
|
|
}
|
|
|
|
void android_getOpenglesHardwareStrings(char** vendor,
|
|
char** renderer,
|
|
char** version) {
|
|
assert(vendor != NULL && renderer != NULL && version != NULL);
|
|
assert(*vendor == NULL && *renderer == NULL && *version == NULL);
|
|
if (!sRenderer) {
|
|
D("Can't get OpenGL ES hardware strings when renderer not started");
|
|
return;
|
|
}
|
|
|
|
const emugl::Renderer::HardwareStrings strings =
|
|
sRenderer->getHardwareStrings();
|
|
D("OpenGL Vendor=[%s]", strings.vendor.c_str());
|
|
D("OpenGL Renderer=[%s]", strings.renderer.c_str());
|
|
D("OpenGL Version=[%s]", strings.version.c_str());
|
|
|
|
/* Special case for the default ES to GL translators: extract the strings
|
|
* of the underlying OpenGL implementation. */
|
|
if (strncmp(strings.vendor.c_str(), "Google", 6) == 0 &&
|
|
strncmp(strings.renderer.c_str(), "Android Emulator OpenGL ES Translator", 37) == 0) {
|
|
*vendor = strdupBaseString(strings.vendor.c_str());
|
|
*renderer = strdupBaseString(strings.renderer.c_str());
|
|
*version = strdupBaseString(strings.version.c_str());
|
|
} else {
|
|
*vendor = strdup(strings.vendor.c_str());
|
|
*renderer = strdup(strings.renderer.c_str());
|
|
*version = strdup(strings.version.c_str());
|
|
}
|
|
}
|
|
|
|
void android_getOpenglesVersion(int* maj, int* min) {
|
|
sRenderLib->getGlesVersion(maj, min);
|
|
fprintf(stderr, "%s: maj min %d %d\n", __func__, *maj, *min);
|
|
}
|
|
|
|
void
|
|
android_stopOpenglesRenderer(bool wait)
|
|
{
|
|
if (sRenderer) {
|
|
sRenderer->stop(wait);
|
|
if (wait) {
|
|
sRenderer.reset();
|
|
android_stop_opengl_logger();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
android_finishOpenglesRenderer()
|
|
{
|
|
if (sRenderer) {
|
|
sRenderer->finish();
|
|
}
|
|
}
|
|
|
|
static emugl::RenderOpt sOpt;
|
|
static int sWidth, sHeight;
|
|
static int sNewWidth, sNewHeight;
|
|
|
|
int android_showOpenglesWindow(void* window,
|
|
int wx,
|
|
int wy,
|
|
int ww,
|
|
int wh,
|
|
int fbw,
|
|
int fbh,
|
|
float dpr,
|
|
float rotation,
|
|
bool deleteExisting,
|
|
bool hideWindow) {
|
|
if (!sRenderer) {
|
|
return -1;
|
|
}
|
|
FBNativeWindowType win = (FBNativeWindowType)(uintptr_t)window;
|
|
bool success = sRenderer->showOpenGLSubwindow(win, wx, wy, ww, wh, fbw, fbh,
|
|
dpr, rotation, deleteExisting,
|
|
hideWindow);
|
|
sNewWidth = ww * dpr;
|
|
sNewHeight = wh * dpr;
|
|
return success ? 0 : -1;
|
|
}
|
|
|
|
void
|
|
android_setOpenglesTranslation(float px, float py)
|
|
{
|
|
if (sRenderer) {
|
|
sRenderer->setOpenGLDisplayTranslation(px, py);
|
|
}
|
|
}
|
|
|
|
void
|
|
android_setOpenglesScreenMask(int width, int height, const unsigned char* rgbaData)
|
|
{
|
|
if (sRenderer) {
|
|
sRenderer->setScreenMask(width, height, rgbaData);
|
|
}
|
|
}
|
|
|
|
int
|
|
android_hideOpenglesWindow(void)
|
|
{
|
|
if (!sRenderer) {
|
|
return -1;
|
|
}
|
|
bool success = sRenderer->destroyOpenGLSubwindow();
|
|
return success ? 0 : -1;
|
|
}
|
|
|
|
void
|
|
android_redrawOpenglesWindow(void)
|
|
{
|
|
if (sRenderer) {
|
|
sRenderer->repaintOpenGLDisplay();
|
|
}
|
|
}
|
|
|
|
bool
|
|
android_hasGuestPostedAFrame(void)
|
|
{
|
|
if (sRenderer) {
|
|
return sRenderer->hasGuestPostedAFrame();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
android_resetGuestPostedAFrame(void)
|
|
{
|
|
if (sRenderer) {
|
|
sRenderer->resetGuestPostedAFrame();
|
|
}
|
|
}
|
|
|
|
static ScreenshotFunc sScreenshotFunc = nullptr;
|
|
|
|
void android_registerScreenshotFunc(ScreenshotFunc f)
|
|
{
|
|
sScreenshotFunc = f;
|
|
}
|
|
|
|
bool android_screenShot(const char* dirname, uint32_t displayId)
|
|
{
|
|
if (sScreenshotFunc) {
|
|
return sScreenshotFunc(dirname, displayId);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const emugl::RendererPtr& android_getOpenglesRenderer() {
|
|
return sRenderer;
|
|
}
|
|
|
|
void android_cleanupProcGLObjects(uint64_t puid) {
|
|
if (sRenderer) {
|
|
sRenderer->cleanupProcGLObjects(puid);
|
|
}
|
|
}
|
|
|
|
void android_cleanupProcGLObjectsAndWaitFinished(uint64_t puid) {
|
|
if (sRenderer) {
|
|
sRenderer->cleanupProcGLObjects(puid);
|
|
}
|
|
}
|
|
|
|
void android_waitForOpenglesProcessCleanup() {
|
|
if (sRenderer) {
|
|
sRenderer->waitForProcessCleanup();
|
|
}
|
|
}
|
|
|
|
static void* sContext, * sRenderContext, * sSurface;
|
|
static EGLint s_gles_attr[5];
|
|
|
|
extern void tinyepoxy_init(const GLESv2Dispatch* gles, int version);
|
|
|
|
// static bool prepare_epoxy(void) {
|
|
// if (!sRenderLib->getOpt(&sOpt)) {
|
|
// return false;
|
|
// }
|
|
// int major, minor;
|
|
// sRenderLib->getGlesVersion(&major, &minor);
|
|
// EGLint attr[] = {
|
|
// EGL_CONTEXT_CLIENT_VERSION, major,
|
|
// EGL_CONTEXT_MINOR_VERSION_KHR, minor,
|
|
// EGL_NONE
|
|
// };
|
|
// sContext = sEgl->eglCreateContext(sOpt.display, sOpt.config, EGL_NO_CONTEXT,
|
|
// attr);
|
|
// if (sContext == nullptr) {
|
|
// return false;
|
|
// }
|
|
// sRenderContext = sEgl->eglCreateContext(sOpt.display, sOpt.config,
|
|
// sContext, attr);
|
|
// if (sRenderContext == nullptr) {
|
|
// return false;
|
|
// }
|
|
// static constexpr EGLint surface_attr[] = {
|
|
// EGL_WIDTH, 1,
|
|
// EGL_HEIGHT, 1,
|
|
// EGL_NONE
|
|
// };
|
|
// sSurface = sEgl->eglCreatePbufferSurface(sOpt.display, sOpt.config,
|
|
// surface_attr);
|
|
// if (sSurface == EGL_NO_SURFACE) {
|
|
// return false;
|
|
// }
|
|
// static_assert(sizeof(attr) == sizeof(s_gles_attr), "Mismatch");
|
|
// memcpy(s_gles_attr, attr, sizeof(s_gles_attr));
|
|
// tinyepoxy_init(sGlesv2, major * 10 + minor);
|
|
// return true;
|
|
// }
|
|
|
|
struct DisplayChangeListener;
|
|
struct QEMUGLParams;
|
|
|
|
void * android_gl_create_context(DisplayChangeListener * unuse1,
|
|
QEMUGLParams* unuse2) {
|
|
// static bool ok = prepare_epoxy();
|
|
// if (!ok) {
|
|
// return nullptr;
|
|
// }
|
|
sEgl->eglMakeCurrent(sOpt.display, sSurface, sSurface, sContext);
|
|
return sEgl->eglCreateContext(sOpt.display, sOpt.config, sContext, s_gles_attr);
|
|
}
|
|
|
|
void android_gl_destroy_context(DisplayChangeListener* unused, void * ctx) {
|
|
sEgl->eglDestroyContext(sOpt.display, ctx);
|
|
}
|
|
|
|
int android_gl_make_context_current(DisplayChangeListener* unused, void * ctx) {
|
|
return sEgl->eglMakeCurrent(sOpt.display, sSurface, sSurface, ctx);
|
|
}
|
|
|
|
static GLuint s_tex_id, s_fbo_id;
|
|
static uint32_t s_gfx_h, s_gfx_w;
|
|
static bool s_y0_top;
|
|
|
|
// ui/gtk-egl.c:gd_egl_scanout_texture as reference.
|
|
void android_gl_scanout_texture(DisplayChangeListener* unuse,
|
|
uint32_t backing_id,
|
|
bool backing_y_0_top,
|
|
uint32_t backing_width,
|
|
uint32_t backing_height,
|
|
uint32_t x, uint32_t y,
|
|
uint32_t w, uint32_t h) {
|
|
s_tex_id = backing_id;
|
|
s_gfx_h = h;
|
|
s_gfx_w = w;
|
|
s_y0_top = backing_y_0_top;
|
|
if (sNewWidth != sWidth || sNewHeight != sHeight) {
|
|
sRenderLib->getOpt(&sOpt);
|
|
sWidth = sNewWidth;
|
|
sHeight = sNewHeight;
|
|
}
|
|
sEgl->eglMakeCurrent(sOpt.display, sOpt.surface, sOpt.surface,
|
|
sRenderContext);
|
|
if (!s_fbo_id) {
|
|
sGlesv2->glGenFramebuffers(1, &s_fbo_id);
|
|
}
|
|
sGlesv2->glBindFramebuffer(GL_FRAMEBUFFER_EXT, s_fbo_id);
|
|
sGlesv2->glViewport(0, 0, h, w);
|
|
sGlesv2->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
|
|
GL_TEXTURE_2D, backing_id, 0);
|
|
}
|
|
|
|
// ui/gtk-egl.c:gd_egl_scanout_flush as reference.
|
|
void android_gl_scanout_flush(DisplayChangeListener* unuse,
|
|
uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
|
|
if (!s_fbo_id) {
|
|
return;
|
|
}
|
|
sEgl->eglMakeCurrent(sOpt.display, sOpt.surface, sOpt.surface,
|
|
sRenderContext);
|
|
|
|
sGlesv2->glBindFramebuffer(GL_READ_FRAMEBUFFER, s_fbo_id);
|
|
sGlesv2->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
int y1 = s_y0_top ? 0 : s_gfx_h;
|
|
int y2 = s_y0_top ? s_gfx_h : 0;
|
|
|
|
sGlesv2->glViewport(0, 0, sWidth, sHeight);
|
|
sGlesv2->glBlitFramebuffer(0, y1, s_gfx_w, y2,
|
|
0, 0, sWidth, sHeight,
|
|
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
sEgl->eglSwapBuffers(sOpt.display, sOpt.surface);
|
|
sGlesv2->glBindFramebuffer(GL_FRAMEBUFFER_EXT, s_fbo_id);
|
|
}
|
|
|
|
struct AndroidVirtioGpuOps* android_getVirtioGpuOps() {
|
|
if (sRenderer) {
|
|
return sRenderer->getVirtioGpuOps();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const void* android_getEGLDispatch() {
|
|
return sEgl;
|
|
}
|
|
|
|
const void* android_getGLESv2Dispatch() {
|
|
return sGlesv2;
|
|
}
|