| /* |
| * 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_; |
| } |