summaryrefslogtreecommitdiffstats
path: root/host/libs/virglrenderer/RenderControl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'host/libs/virglrenderer/RenderControl.cpp')
-rw-r--r--host/libs/virglrenderer/RenderControl.cpp868
1 files changed, 868 insertions, 0 deletions
diff --git a/host/libs/virglrenderer/RenderControl.cpp b/host/libs/virglrenderer/RenderControl.cpp
new file mode 100644
index 000000000..fd84eb548
--- /dev/null
+++ b/host/libs/virglrenderer/RenderControl.cpp
@@ -0,0 +1,868 @@
+/*
+ * 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.
+ */
+#undef NDEBUG
+
+#include "Context.h"
+#include "EglConfig.h"
+#include "EglContext.h"
+#include "EglImage.h"
+#include "EglSurface.h"
+#include "EglSync.h"
+
+#include <cassert>
+#include <cerrno>
+#include <cstring>
+#include <string>
+
+#include <unistd.h>
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include <OpenGLESDispatch/EGLDispatch.h>
+#include <OpenGLESDispatch/GLESv1Dispatch.h>
+#include <OpenGLESDispatch/GLESv3Dispatch.h>
+
+#include "virgl_hw.h"
+
+#include "RenderControl.h"
+
+#include <hardware/gralloc.h>
+#include <hardware/gralloc1.h>
+#include <nativebase/nativebase.h>
+#include <system/window.h>
+
+static void incRefANWB(android_native_base_t* base) {
+ ANativeWindowBuffer* anwb = reinterpret_cast<ANativeWindowBuffer*>(base);
+ anwb->layerCount++;
+}
+
+static void decRefANWB(android_native_base_t* base) {
+ ANativeWindowBuffer* anwb = reinterpret_cast<ANativeWindowBuffer*>(base);
+ if (anwb->layerCount > 0) {
+ anwb->layerCount--;
+ if (anwb->layerCount == 0)
+ delete anwb;
+ }
+}
+struct FakeANativeWindowBuffer : public ANativeWindowBuffer {
+ FakeANativeWindowBuffer() {
+ ANativeWindowBuffer();
+
+ common.incRef = incRefANWB;
+ common.decRef = decRefANWB;
+ layerCount = 0U;
+ }
+};
+
+static void incRefANW(android_native_base_t* base) {
+ ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(base);
+ anw->oem[0]++;
+}
+
+static void decRefANW(android_native_base_t* base) {
+ ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(base);
+ if (anw->oem[0] > 0) {
+ anw->oem[0]--;
+ if (anw->oem[0] == 0)
+ delete anw;
+ }
+}
+
+static int setSwapInterval(ANativeWindow*, int) {
+ printf("%s: not implemented\n", __func__);
+ return 0;
+}
+
+static int dequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer) {
+ if (!window->oem[1])
+ return -EINVAL;
+ *buffer = reinterpret_cast<ANativeWindowBuffer*>(window->oem[1]);
+ window->oem[1] = 0;
+ return 0;
+}
+
+static int lockBuffer_DEPRECATED(ANativeWindow*, ANativeWindowBuffer*) {
+ printf("%s: not implemented\n", __func__);
+ return 0;
+}
+
+static int queueBuffer_DEPRECATED(ANativeWindow*, ANativeWindowBuffer*) {
+ printf("%s: not implemented\n", __func__);
+ return 0;
+}
+
+static int query(const ANativeWindow* window, int what, int* value) {
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ return static_cast<int>(window->oem[2]);
+ case NATIVE_WINDOW_HEIGHT:
+ return static_cast<int>(window->oem[3]);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int perform(ANativeWindow*, int, ...) {
+ printf("%s: not implemented\n", __func__);
+ return 0;
+}
+
+static int cancelBuffer_DEPRECATED(ANativeWindow*, ANativeWindowBuffer*) {
+ printf("%s: not implemented\n", __func__);
+ return 0;
+}
+
+static int dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd) {
+ *fenceFd = -1;
+ return dequeueBuffer_DEPRECATED(window, buffer);
+}
+
+static int queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+ if (fenceFd >= 0)
+ close(fenceFd);
+ return queueBuffer_DEPRECATED(window, buffer);
+}
+
+static int cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+ if (fenceFd >= 0)
+ close(fenceFd);
+ return cancelBuffer_DEPRECATED(window, buffer);
+}
+
+struct FakeANativeWindow : public ANativeWindow {
+ FakeANativeWindow(uint32_t width, uint32_t height) {
+ ANativeWindow();
+
+ common.incRef = incRefANW;
+ common.decRef = decRefANW;
+ oem[0] = 0;
+ oem[2] = static_cast<intptr_t>(width);
+ oem[3] = static_cast<intptr_t>(height);
+
+ this->setSwapInterval = ::setSwapInterval;
+ this->dequeueBuffer_DEPRECATED = ::dequeueBuffer_DEPRECATED;
+ this->lockBuffer_DEPRECATED = ::lockBuffer_DEPRECATED;
+ this->queueBuffer_DEPRECATED = ::queueBuffer_DEPRECATED;
+ this->query = ::query;
+ this->perform = ::perform;
+ this->cancelBuffer_DEPRECATED = ::cancelBuffer_DEPRECATED;
+ this->dequeueBuffer = ::dequeueBuffer;
+ this->queueBuffer = ::queueBuffer;
+ this->cancelBuffer = ::cancelBuffer;
+ }
+};
+
+// Helpers
+
+static ANativeWindowBuffer* resourceToANWB(Resource* res) {
+ ANativeWindowBuffer* buffer = new (std::nothrow) FakeANativeWindowBuffer();
+ if (!buffer)
+ return nullptr;
+
+ buffer->width = res->args.width;
+ buffer->height = res->args.height;
+ buffer->stride = res->args.width;
+ buffer->handle = reinterpret_cast<const native_handle_t*>(res->args.handle);
+ buffer->usage_deprecated = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_RENDER;
+ buffer->usage =
+ GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN | GRALLOC1_CONSUMER_USAGE_CPU_WRITE_OFTEN |
+ GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE | GRALLOC1_PRODUCER_USAGE_CPU_READ_OFTEN |
+ GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN | GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET;
+
+ switch (res->args.format) {
+ case VIRGL_FORMAT_B8G8R8A8_UNORM:
+ buffer->format = HAL_PIXEL_FORMAT_BGRA_8888;
+ break;
+ case VIRGL_FORMAT_B5G6R5_UNORM:
+ buffer->format = HAL_PIXEL_FORMAT_RGB_565;
+ break;
+ case VIRGL_FORMAT_R8G8B8A8_UNORM:
+ buffer->format = HAL_PIXEL_FORMAT_RGBA_8888;
+ break;
+ case VIRGL_FORMAT_R8G8B8X8_UNORM:
+ buffer->format = HAL_PIXEL_FORMAT_RGBX_8888;
+ break;
+ default:
+ delete buffer;
+ return nullptr;
+ }
+
+ return buffer;
+}
+
+// RenderControl
+
+static GLint rcGetRendererVersion() {
+ return 1; // seems to be hard-coded
+}
+
+static EGLint rcGetEGLVersion(void* ctx_, EGLint* major, EGLint* minor) {
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ return s_egl.eglInitialize(rc->dpy, major, minor);
+}
+
+static EGLint rcQueryEGLString(void* ctx_, EGLenum name, void* buffer, EGLint bufferSize) {
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ const char* str = s_egl.eglQueryString(rc->dpy, name);
+ if (!str)
+ str = "";
+
+ if (strlen(str) > (size_t)bufferSize) {
+ memset(buffer, 0, bufferSize);
+ return -strlen(str);
+ }
+
+ char* strOut = static_cast<char*>(buffer);
+ strncpy(strOut, str, bufferSize - 1);
+ strOut[bufferSize - 1] = 0;
+
+ return strlen(strOut) + 1U;
+}
+
+static std::string replaceESVersionString(const std::string& prev, const char* const newver) {
+ // Do not touch ES 1.x contexts (they will all be 1.1 anyway)
+ if (prev.find("ES-CM") != std::string::npos)
+ return prev;
+
+ size_t esStart = prev.find("ES ");
+ size_t esEnd = prev.find(" ", esStart + 3);
+
+ // Do not change out-of-spec version strings.
+ if (esStart == std::string::npos || esEnd == std::string::npos)
+ return prev;
+
+ std::string res = prev.substr(0, esStart + 3);
+ res += newver;
+ res += prev.substr(esEnd);
+ return res;
+}
+
+static EGLint rcGetGLString(void* ctx_, EGLenum name, void* buffer, EGLint bufferSize) {
+ std::string glStr;
+
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ if (rc->ctx->ctx) {
+ const char* str = nullptr;
+ switch (rc->ctx->ctx->api) {
+ case EglContext::GLESApi::GLESApi_CM:
+ str = reinterpret_cast<const char*>(s_gles1.glGetString(name));
+ break;
+ default:
+ str = reinterpret_cast<const char*>(s_gles3.glGetString(name));
+ break;
+ }
+ if (str)
+ glStr += str;
+ }
+
+ // FIXME: Should probably filter the extensions list like the emulator
+ // does. We need to handle ES2 on ES3 compatibility for older
+ // Android versions, as well as filter out unsupported features.
+
+ if (name == GL_EXTENSIONS) {
+ glStr += ChecksumCalculator::getMaxVersionStr();
+ glStr += " ";
+
+ // FIXME: Hard-coded to 3.0 for now. We should attempt to detect 3.1.
+ glStr += "ANDROID_EMU_gles_max_version_3_0";
+ glStr += " ";
+ }
+
+ // FIXME: Add support for async swap and the fence_sync extensions
+
+ // We don't support GLDMA; use VIRTGPU_RESOURCE_CREATE and a combination of
+ // VIRTGPU_TRANSFER_TO_HOST and VIRTGPU_TRANSFER_FROM_HOST.
+
+ // FIXME: Add support for 'no host error'
+
+ if (name == GL_VERSION)
+ glStr = replaceESVersionString(glStr, "3.0");
+
+ int nextBufferSize = glStr.size() + 1;
+
+ if (!buffer || nextBufferSize > bufferSize)
+ return -nextBufferSize;
+
+ snprintf((char*)buffer, nextBufferSize, "%s", glStr.c_str());
+ return nextBufferSize;
+}
+
+static EGLint rcGetNumConfigs(uint32_t* numAttribs) {
+ *numAttribs = EglConfig::kNumAttribs;
+ return EglConfig::vec.size();
+}
+
+static EGLint rcGetConfigs(uint32_t bufSize, GLuint* buffer) {
+ size_t configAttribBytes = sizeof(EglConfig::kAttribs);
+ size_t nConfigs = EglConfig::vec.size();
+ size_t sizeNeeded = configAttribBytes + nConfigs * configAttribBytes;
+
+ if (bufSize < sizeNeeded)
+ return -sizeNeeded;
+
+ memcpy(buffer, &EglConfig::kAttribs, configAttribBytes);
+ size_t offset = EglConfig::kNumAttribs;
+ for (auto const& config : EglConfig::vec) {
+ memcpy(&buffer[offset], config->attribs, configAttribBytes);
+ offset += EglConfig::kNumAttribs;
+ }
+
+ return nConfigs;
+}
+
+static EGLint rcChooseConfig(void* ctx_, EGLint* attribs, uint32_t, uint32_t* config_ints,
+ uint32_t configs_size) {
+ EGLint num_config;
+ EGLConfig configs[configs_size];
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ EGLBoolean ret = s_egl.eglChooseConfig(rc->dpy, attribs, configs, configs_size, &num_config);
+ if (!ret)
+ num_config = 0;
+
+ if (configs_size) {
+ for (EGLint i = 0; i < num_config; i++) {
+ config_ints[i] = ~0U;
+ EGLint config_id;
+ if (s_egl.eglGetConfigAttrib(rc->dpy, configs[i], EGL_CONFIG_ID, &config_id)) {
+ for (size_t i = 0; i < EglConfig::vec.size(); i++) {
+ if (EglConfig::vec[i]->attribs[4] == config_id)
+ config_ints[i] = i;
+ }
+ }
+ if (config_ints[i] == ~0U) {
+ num_config = 0;
+ break;
+ }
+ }
+ if (!num_config)
+ memset(config_ints, 0, configs_size * sizeof(uint32_t));
+ }
+
+ return num_config;
+}
+
+static EGLint rcGetFBParam(EGLint) {
+ printf("%s: not implemented\n", __func__);
+ return 0;
+}
+
+static uint32_t rcCreateContext(void* ctx_, uint32_t config_, uint32_t share_, uint32_t glVersion) {
+ // clang-format off
+ EGLint attrib_list[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 0,
+ EGL_CONTEXT_MINOR_VERSION_KHR, 0,
+ EGL_NONE
+ };
+ // clang-format on
+ switch (glVersion) {
+ case EglContext::GLESApi::GLESApi_CM:
+ attrib_list[1] = 1;
+ attrib_list[3] = 1;
+ break;
+ case EglContext::GLESApi::GLESApi_2:
+ attrib_list[1] = 2;
+ break;
+ case EglContext::GLESApi::GLESApi_3_0:
+ attrib_list[1] = 3;
+ break;
+ case EglContext::GLESApi::GLESApi_3_1:
+ attrib_list[1] = 3;
+ attrib_list[3] = 1;
+ break;
+ }
+ if (!attrib_list[1])
+ return 0U;
+
+ if (config_ > EglConfig::vec.size())
+ return 0U;
+ EglConfig const* config = EglConfig::vec[config_];
+
+ EGLContext share_context = EGL_NO_CONTEXT;
+ if (share_ > 0) {
+ std::map<uint32_t, EglContext*>::iterator context_it;
+ context_it = EglContext::map.find(share_);
+ if (context_it == EglContext::map.end())
+ return 0U;
+
+ EglContext const* share = context_it->second;
+ share_context = share->context;
+ }
+
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ EGLContext context_ =
+ s_egl.eglCreateContext(rc->dpy, config->config, share_context, attrib_list);
+ if (context_ == EGL_NO_CONTEXT)
+ return 0U;
+
+ EglContext* context = new (std::nothrow)
+ EglContext(context_, rc->ctx->handle, (enum EglContext::GLESApi)glVersion);
+ if (!context) {
+ s_egl.eglDestroyContext(rc->dpy, context_);
+ return 0U;
+ }
+
+ return context->id;
+}
+
+static void rcDestroyContext(void* ctx_, uint32_t ctx) {
+ std::map<uint32_t, EglContext*>::iterator it;
+ it = EglContext::map.find(ctx);
+ if (it == EglContext::map.end())
+ return;
+
+ EglContext* context = it->second;
+
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ s_egl.eglDestroyContext(rc->dpy, context->context);
+ context->context = EGL_NO_CONTEXT;
+ if (context->disposable())
+ delete context;
+}
+
+static uint32_t rcCreateWindowSurface(void* ctx_, uint32_t config_, uint32_t width,
+ uint32_t height) {
+ if (config_ > EglConfig::vec.size())
+ return 0U;
+
+ EglConfig const* config = EglConfig::vec[config_];
+
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ EglSurface* surface =
+ new (std::nothrow) EglSurface(config->config, rc->ctx->handle, width, height);
+ if (!surface)
+ return 0U;
+
+ return surface->id;
+}
+
+static void rcDestroyWindowSurface(void* ctx_, uint32_t surface_) {
+ std::map<uint32_t, EglSurface*>::iterator it;
+ it = EglSurface::map.find(surface_);
+ if (it == EglSurface::map.end())
+ return;
+
+ EglSurface* surface = it->second;
+
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ s_egl.eglDestroySurface(rc->dpy, surface->surface);
+ surface->surface = EGL_NO_SURFACE;
+ if (surface->disposable()) {
+ delete surface->window;
+ delete surface;
+ }
+}
+
+static uint32_t rcCreateColorBuffer(uint32_t, uint32_t, GLenum) {
+ // NOTE: This CreateColorBuffer implementation is a no-op which returns a
+ // special surface ID to indicate that a pbuffer surface should be
+ // created. This is necessary because the emulator does not create a
+ // true pbuffer, it always creates a fake one. We don't want this.
+ return ~1U;
+}
+
+static void rcOpenColorBuffer(uint32_t) {
+ printf("%s: not implemented\n", __func__);
+}
+
+static void rcCloseColorBuffer(uint32_t) {
+ printf("%s: not implemented\n", __func__);
+}
+
+static void rcSetWindowColorBuffer(void* ctx_, uint32_t windowSurface, uint32_t colorBuffer) {
+ std::map<uint32_t, EglSurface*>::iterator surface_it;
+ surface_it = EglSurface::map.find(windowSurface);
+ if (surface_it == EglSurface::map.end())
+ return;
+
+ EglSurface* surface = surface_it->second;
+
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+
+ if (colorBuffer == ~1U) {
+ EGLint const attrib_list[] = { EGL_WIDTH, (EGLint)surface->width, EGL_HEIGHT,
+ (EGLint)surface->height, EGL_NONE };
+ assert(surface->surface == EGL_NO_SURFACE && "Pbuffer set twice");
+ surface->surface = s_egl.eglCreatePbufferSurface(rc->dpy, surface->config, attrib_list);
+ } else {
+ std::map<uint32_t, Resource*>::iterator resource_it;
+ resource_it = Resource::map.find(colorBuffer);
+ if (resource_it == Resource::map.end())
+ return;
+
+ Resource* res = resource_it->second;
+ ANativeWindowBuffer* buffer = resourceToANWB(res);
+ if (!buffer)
+ return;
+
+ if (surface->surface == EGL_NO_SURFACE) {
+ surface->window =
+ new (std::nothrow) FakeANativeWindow(res->args.width, res->args.height);
+ if (!surface->window)
+ return;
+
+ NativeWindowType native_window = reinterpret_cast<NativeWindowType>(surface->window);
+ surface->window->oem[1] = (intptr_t)buffer;
+ surface->surface =
+ s_egl.eglCreateWindowSurface(rc->dpy, surface->config, native_window, nullptr);
+ } else {
+ surface->window->oem[1] = (intptr_t)buffer;
+ s_egl.eglSwapBuffers(rc->dpy, surface->surface);
+ }
+ }
+}
+
+static int rcFlushWindowColorBuffer(uint32_t windowSurface) {
+ std::map<uint32_t, EglSurface*>::iterator it;
+ it = EglSurface::map.find(windowSurface);
+ return it == EglSurface::map.end() ? -1 : 0;
+}
+
+static EGLint rcMakeCurrent(void* ctx_, uint32_t context_, uint32_t drawSurf, uint32_t readSurf) {
+ std::map<uint32_t, EglContext*>::iterator context_it;
+ context_it = EglContext::map.find(context_);
+ if (context_it == EglContext::map.end())
+ return EGL_FALSE;
+
+ EglContext* context = context_it->second;
+
+ std::map<uint32_t, EglSurface*>::iterator surface_it;
+ surface_it = EglSurface::map.find(drawSurf);
+ if (surface_it == EglSurface::map.end())
+ return EGL_FALSE;
+
+ EglSurface* draw_surface = surface_it->second;
+
+ surface_it = EglSurface::map.find(readSurf);
+ if (surface_it == EglSurface::map.end())
+ return EGL_FALSE;
+
+ EglSurface* read_surface = surface_it->second;
+
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+
+ EglSurface* old_draw_surface = draw_surface->bind(rc->ctx->handle, false);
+ if (old_draw_surface)
+ old_draw_surface->unbind(false);
+
+ EglSurface* old_read_surface = read_surface->bind(rc->ctx->handle, true);
+ if (old_read_surface)
+ old_read_surface->unbind(true);
+
+ EglContext* old_context = context->bind(rc->ctx->handle);
+ if (old_context)
+ old_context->unbind();
+
+ EGLBoolean ret = s_egl.eglMakeCurrent(rc->dpy, draw_surface->surface, read_surface->surface,
+ context->context);
+ if (!ret) {
+ // If eglMakeCurrent fails, it's specified *not* to have unbound the
+ // previous contexts or surfaces, but many implementations do. This bug
+ // isn't worked around here, and we just assume the implementations obey
+ // the spec.
+ context->unbind();
+ if (old_context)
+ old_context->bind(rc->ctx->handle);
+ read_surface->unbind(true);
+ if (old_read_surface)
+ old_read_surface->bind(rc->ctx->handle, true);
+ draw_surface->unbind(false);
+ if (old_draw_surface)
+ old_draw_surface->bind(rc->ctx->handle, false);
+ } else {
+ if (old_context && old_context->disposable())
+ delete old_context;
+ if (old_read_surface && old_read_surface->disposable())
+ delete old_read_surface;
+ if (old_draw_surface && old_draw_surface->disposable())
+ delete old_draw_surface;
+ rc->ctx->unbind();
+ rc->ctx->bind(context);
+ }
+
+ return (EGLint)ret;
+}
+
+static void rcFBPost(uint32_t) {
+ printf("%s: not implemented\n", __func__);
+}
+
+static void rcFBSetSwapInterval(void* ctx_, EGLint interval) {
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ s_egl.eglSwapInterval(rc->dpy, interval);
+}
+
+static void rcBindTexture(void* ctx_, uint32_t colorBuffer) {
+ std::map<uint32_t, Resource*>::iterator it;
+ it = Resource::map.find(colorBuffer);
+ if (it == Resource::map.end())
+ return;
+
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ Resource* res = it->second;
+ if (!res->image) {
+ ANativeWindowBuffer* buffer = resourceToANWB(res);
+ if (!buffer)
+ return;
+
+ EGLClientBuffer client_buffer = static_cast<EGLClientBuffer>(buffer);
+ EGLImageKHR image = s_egl.eglCreateImageKHR(
+ rc->dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, client_buffer, nullptr);
+ if (image == EGL_NO_IMAGE_KHR)
+ return;
+
+ EglImage* img = new (std::nothrow) EglImage(rc->dpy, image, s_egl.eglDestroyImageKHR);
+ if (!img) {
+ s_egl.eglDestroyImageKHR(rc->dpy, image);
+ return;
+ }
+
+ // FIXME: House keeping, because we won't get asked to delete the image
+ // object otherwise, so we need to keep a reference to it..
+ res->image = img;
+ }
+
+ if (rc->ctx->ctx->api == EglContext::GLESApi::GLESApi_CM) {
+ // FIXME: Unconditional use of GL_TEXTURE_2D here is wrong
+ s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, res->image->image);
+ } else {
+ // FIXME: Unconditional use of GL_TEXTURE_2D here is wrong
+ s_gles3.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, res->image->image);
+ }
+}
+
+static void rcBindRenderbuffer(void* ctx_, uint32_t colorBuffer) {
+ std::map<uint32_t, Resource*>::iterator it;
+ it = Resource::map.find(colorBuffer);
+ if (it == Resource::map.end())
+ return;
+
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ Resource* res = it->second;
+ if (!res->image) {
+ ANativeWindowBuffer* buffer = resourceToANWB(res);
+ if (!buffer)
+ return;
+
+ EGLClientBuffer client_buffer = static_cast<EGLClientBuffer>(buffer);
+ EGLImageKHR image = s_egl.eglCreateImageKHR(
+ rc->dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, client_buffer, nullptr);
+ if (image == EGL_NO_IMAGE_KHR)
+ return;
+
+ EglImage* img = new (std::nothrow) EglImage(rc->dpy, image, s_egl.eglDestroyImageKHR);
+ if (!img) {
+ s_egl.eglDestroyImageKHR(rc->dpy, image);
+ return;
+ }
+
+ // FIXME: House keeping, because we won't get asked to delete the image
+ // object otherwise, so we need to keep a reference to it..
+ res->image = img;
+ }
+
+ if (rc->ctx->ctx->api == EglContext::GLESApi::GLESApi_CM) {
+ s_gles1.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, res->image->image);
+ } else {
+ s_gles3.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, res->image->image);
+ }
+}
+
+static EGLint rcColorBufferCacheFlush(uint32_t, EGLint, int) {
+ printf("%s: not implemented\n", __func__);
+ return 0;
+}
+
+static void rcReadColorBuffer(uint32_t, GLint, GLint, GLint, GLint, GLenum, GLenum, void*) {
+ printf("%s: not implemented\n", __func__);
+}
+
+static int rcUpdateColorBuffer(uint32_t, GLint, GLint, GLint, GLint, GLenum, GLenum, void*) {
+ printf("%s: not implemented\n", __func__);
+ return 0;
+}
+
+static int rcOpenColorBuffer2(uint32_t) {
+ printf("%s: not implemented\n", __func__);
+ return 0;
+}
+
+static uint32_t rcCreateClientImage(void* ctx_, uint32_t context_, EGLenum target, GLuint buffer_) {
+ std::map<uint32_t, EglContext*>::iterator it;
+ it = EglContext::map.find(context_);
+ if (it == EglContext::map.end())
+ return 0U;
+
+ EglContext* context = it->second;
+
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ EGLClientBuffer buffer = reinterpret_cast<EGLClientBuffer>(buffer_);
+ EGLImageKHR image = s_egl.eglCreateImageKHR(rc->dpy, context, target, buffer, nullptr);
+ EglImage* img = new (std::nothrow) EglImage(rc->dpy, image, s_egl.eglDestroyImageKHR);
+ if (!img) {
+ s_egl.eglDestroyImageKHR(rc->dpy, image);
+ return 0U;
+ }
+
+ return img->id;
+}
+
+static int rcDestroyClientImage(uint32_t image_) {
+ std::map<uint32_t, EglImage*>::iterator it;
+ it = EglImage::map.find(image_);
+ if (it == EglImage::map.end())
+ return EGL_FALSE;
+
+ EglImage* image = it->second;
+
+ delete image;
+ return EGL_TRUE;
+}
+
+static void rcSelectChecksumHelper(void* ctx_, uint32_t protocol, uint32_t) {
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ rc->ctx->checksum_calc.setVersion(protocol);
+}
+
+static void rcCreateSyncKHR(void* ctx_, EGLenum type, EGLint* attribs, uint32_t, int,
+ uint64_t* glsync_out, uint64_t* syncthread_out) {
+ *syncthread_out = 0ULL;
+
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ EGLSyncKHR sync = s_egl.eglCreateSyncKHR(rc->dpy, type, attribs);
+ if (sync == EGL_NO_SYNC_KHR) {
+ *glsync_out = 0ULL;
+ return;
+ }
+
+ EglSync* syn = new (std::nothrow) EglSync(sync);
+ if (!syn) {
+ s_egl.eglDestroySyncKHR(rc->dpy, sync);
+ *glsync_out = 0ULL;
+ return;
+ }
+
+ *glsync_out = syn->id;
+}
+
+static EGLint rcClientWaitSyncKHR(void* ctx_, uint64_t sync_, EGLint flags, uint64_t timeout) {
+ std::map<uint64_t, EglSync*>::iterator it;
+ it = EglSync::map.find(sync_);
+ if (it == EglSync::map.end())
+ return EGL_CONDITION_SATISFIED_KHR;
+
+ EglSync* sync = it->second;
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ return s_egl.eglClientWaitSyncKHR(rc->dpy, sync->sync, flags, timeout);
+}
+
+static void rcFlushWindowColorBufferAsync(uint32_t windowSurface) {
+ // No-op
+}
+
+static int rcDestroySyncKHR(void* ctx_, uint64_t sync_) {
+ std::map<uint64_t, EglSync*>::iterator it;
+ it = EglSync::map.find(sync_);
+ if (it == EglSync::map.end())
+ return EGL_FALSE;
+
+ EglSync* sync = it->second;
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ return s_egl.eglDestroySyncKHR(rc->dpy, sync->sync);
+}
+
+static void rcSetPuid(void* ctx_, uint64_t proto) {
+ union {
+ uint64_t proto;
+ struct {
+ int pid;
+ int tid;
+ } id;
+ } puid;
+
+ puid.proto = proto;
+
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ rc->ctx->setPidTid(puid.id.pid, puid.id.tid);
+}
+
+static int rcUpdateColorBufferDMA(uint32_t, GLint, GLint, GLint, GLint, GLenum, GLenum, void*,
+ uint32_t) {
+ printf("%s: not implemented\n", __func__);
+ return 0;
+}
+
+static uint32_t rcCreateColorBufferDMA(uint32_t, uint32_t, GLenum, int) {
+ printf("%s: not implemented\n", __func__);
+ return 0U;
+}
+
+static void rcWaitSyncKHR(void* ctx_, uint64_t sync_, EGLint flags) {
+ std::map<uint64_t, EglSync*>::iterator it;
+ it = EglSync::map.find(sync_);
+ if (it == EglSync::map.end())
+ return;
+
+ EglSync* sync = it->second;
+ RenderControl* rc = static_cast<RenderControl*>(ctx_);
+ // FIXME: No eglWaitSyncKHR support in SwiftShader
+ // This call will BLOCK when it should be asynchronous!
+ s_egl.eglClientWaitSyncKHR(rc->dpy, sync->sync, flags, EGL_FOREVER_KHR);
+}
+
+RenderControl::RenderControl(Context* ctx_, EGLDisplay dpy_) {
+ rcGetRendererVersion = ::rcGetRendererVersion;
+ rcGetEGLVersion_dec = ::rcGetEGLVersion;
+ rcQueryEGLString_dec = ::rcQueryEGLString;
+ rcGetGLString_dec = ::rcGetGLString;
+ rcGetNumConfigs = ::rcGetNumConfigs;
+ rcGetConfigs = ::rcGetConfigs;
+ rcChooseConfig_dec = ::rcChooseConfig;
+ rcGetFBParam = ::rcGetFBParam;
+ rcCreateContext_dec = ::rcCreateContext;
+ rcDestroyContext_dec = ::rcDestroyContext;
+ rcCreateWindowSurface_dec = ::rcCreateWindowSurface;
+ rcDestroyWindowSurface_dec = ::rcDestroyWindowSurface;
+ rcCreateColorBuffer = ::rcCreateColorBuffer;
+ rcOpenColorBuffer = ::rcOpenColorBuffer;
+ rcCloseColorBuffer = ::rcCloseColorBuffer;
+ rcSetWindowColorBuffer_dec = ::rcSetWindowColorBuffer;
+ rcFlushWindowColorBuffer = ::rcFlushWindowColorBuffer;
+ rcMakeCurrent_dec = ::rcMakeCurrent;
+ rcFBPost = ::rcFBPost;
+ rcFBSetSwapInterval_dec = ::rcFBSetSwapInterval;
+ rcBindTexture_dec = ::rcBindTexture;
+ rcBindRenderbuffer_dec = ::rcBindRenderbuffer;
+ rcColorBufferCacheFlush = ::rcColorBufferCacheFlush;
+ rcReadColorBuffer = ::rcReadColorBuffer;
+ rcUpdateColorBuffer = ::rcUpdateColorBuffer;
+ rcOpenColorBuffer2 = ::rcOpenColorBuffer2;
+ rcCreateClientImage_dec = ::rcCreateClientImage;
+ rcDestroyClientImage = ::rcDestroyClientImage;
+ rcSelectChecksumHelper_dec = ::rcSelectChecksumHelper;
+ rcCreateSyncKHR_dec = ::rcCreateSyncKHR;
+ rcClientWaitSyncKHR_dec = ::rcClientWaitSyncKHR;
+ rcFlushWindowColorBufferAsync = ::rcFlushWindowColorBufferAsync;
+ rcDestroySyncKHR_dec = ::rcDestroySyncKHR;
+ rcSetPuid_dec = ::rcSetPuid;
+ rcUpdateColorBufferDMA = ::rcUpdateColorBufferDMA;
+ rcCreateColorBufferDMA = ::rcCreateColorBufferDMA;
+ rcWaitSyncKHR_dec = ::rcWaitSyncKHR;
+
+ dpy = dpy_;
+ ctx = ctx_;
+}