869 lines
28 KiB
C++
869 lines
28 KiB
C++
/*
|
|
* 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_;
|
|
}
|