TYPE3
[iec.git] / src / type3_AndroidCloud / anbox-master / src / anbox / graphics / emugl / ColorBuffer.cpp
diff --git a/src/type3_AndroidCloud/anbox-master/src/anbox/graphics/emugl/ColorBuffer.cpp b/src/type3_AndroidCloud/anbox-master/src/anbox/graphics/emugl/ColorBuffer.cpp
new file mode 100644 (file)
index 0000000..f336c83
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+* Copyright (C) 2011 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 "anbox/graphics/emugl/ColorBuffer.h"
+#include "anbox/graphics/emugl/DispatchTables.h"
+#include "anbox/graphics/emugl/RenderThreadInfo.h"
+#include "anbox/graphics/emugl/TextureDraw.h"
+#include "anbox/graphics/emugl/TextureResize.h"
+#include "anbox/logger.h"
+
+#include "external/android-emugl/host/include/OpenGLESDispatch/EGLDispatch.h"
+
+#include <stdio.h>
+
+namespace {
+
+// <EGL/egl.h> defines many types as 'void*' while they're really
+// implemented as unsigned integers. These convenience template functions
+// help casting between them safely without generating compiler warnings.
+inline void* SafePointerFromUInt(unsigned int handle) {
+  return reinterpret_cast<void*>(static_cast<uintptr_t>(handle));
+}
+
+// Lazily create and bind a framebuffer object to the current host context.
+// |fbo| is the address of the framebuffer object name.
+// |tex| is the name of a texture that is attached to the framebuffer object
+// on creation only. I.e. all rendering operations will target it.
+// returns true in case of success, false on failure.
+bool bindFbo(GLuint* fbo, GLuint tex) {
+  if (*fbo) {
+    // fbo already exist - just bind
+    s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
+    return true;
+  }
+
+  s_gles2.glGenFramebuffers(1, fbo);
+  s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
+  s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_OES,
+                                 GL_TEXTURE_2D, tex, 0);
+  GLenum status = s_gles2.glCheckFramebufferStatus(GL_FRAMEBUFFER);
+  if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
+    ERROR("FBO not complete: %#x", status);
+    s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    s_gles2.glDeleteFramebuffers(1, fbo);
+    *fbo = 0;
+    return false;
+  }
+  return true;
+}
+
+void unbindFbo() { s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); }
+
+// Helper class to use a ColorBuffer::Helper context.
+// Usage is pretty simple:
+//
+//     {
+//        ScopedHelperContext context(m_helper);
+//        if (!context.isOk()) {
+//            return false;   // something bad happened.
+//        }
+//        .... do something ....
+//     }   // automatically calls m_helper->teardownContext();
+//
+class ScopedHelperContext {
+ public:
+  ScopedHelperContext(ColorBuffer::Helper* helper) : mHelper(helper) {
+    if (!helper->setupContext()) {
+      mHelper = NULL;
+    }
+  }
+
+  bool isOk() const { return mHelper != NULL; }
+
+  ~ScopedHelperContext() { release(); }
+
+  void release() {
+    if (mHelper) {
+      mHelper->teardownContext();
+      mHelper = NULL;
+    }
+  }
+
+ private:
+  ColorBuffer::Helper* mHelper;
+};
+
+}  // namespace
+
+// static
+ColorBuffer* ColorBuffer::create(EGLDisplay p_display, int p_width,
+                                 int p_height, GLenum p_internalFormat,
+                                 bool has_eglimage_texture_2d, Helper* helper) {
+  GLenum texInternalFormat = 0;
+
+  switch (p_internalFormat) {
+    case GL_RGB:
+    case GL_RGB565_OES:
+      texInternalFormat = GL_RGB;
+      break;
+
+    case GL_RGBA:
+    case GL_RGB5_A1_OES:
+    case GL_RGBA4_OES:
+      texInternalFormat = GL_RGBA;
+      break;
+
+    default:
+      return NULL;
+      break;
+  }
+
+  ScopedHelperContext context(helper);
+  if (!context.isOk()) {
+    return NULL;
+  }
+
+  ColorBuffer* cb = new ColorBuffer(p_display, helper);
+
+  s_gles2.glGenTextures(1, &cb->m_tex);
+  s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_tex);
+
+  int nComp = (texInternalFormat == GL_RGB ? 3 : 4);
+
+  char* zBuff = static_cast<char*>(::calloc(nComp * p_width * p_height, 1));
+  s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, texInternalFormat, p_width, p_height,
+                       0, texInternalFormat, GL_UNSIGNED_BYTE, zBuff);
+  ::free(zBuff);
+
+  s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+  //
+  // create another texture for that colorbuffer for blit
+  //
+  s_gles2.glGenTextures(1, &cb->m_blitTex);
+  s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_blitTex);
+  s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, texInternalFormat, p_width, p_height,
+                       0, texInternalFormat, GL_UNSIGNED_BYTE, NULL);
+
+  s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+  s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+  s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+  cb->m_width = p_width;
+  cb->m_height = p_height;
+  cb->m_internalFormat = texInternalFormat;
+
+  if (has_eglimage_texture_2d) {
+    cb->m_eglImage = s_egl.eglCreateImageKHR(
+        p_display, s_egl.eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR,
+        reinterpret_cast<EGLClientBuffer>(SafePointerFromUInt(cb->m_tex)), NULL);
+
+    cb->m_blitEGLImage = s_egl.eglCreateImageKHR(
+        p_display, s_egl.eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR,
+        reinterpret_cast<EGLClientBuffer>(SafePointerFromUInt(cb->m_blitTex)), NULL);
+  }
+
+  cb->m_resizer = new TextureResize(p_width, p_height);
+
+  return cb;
+}
+
+ColorBuffer::ColorBuffer(EGLDisplay display, Helper* helper)
+    : m_tex(0),
+      m_blitTex(0),
+      m_eglImage(NULL),
+      m_blitEGLImage(NULL),
+      m_fbo(0),
+      m_internalFormat(0),
+      m_display(display),
+      m_helper(helper) {}
+
+ColorBuffer::~ColorBuffer() {
+  ScopedHelperContext context(m_helper);
+
+  if (m_blitEGLImage) {
+    s_egl.eglDestroyImageKHR(m_display, m_blitEGLImage);
+  }
+  if (m_eglImage) {
+    s_egl.eglDestroyImageKHR(m_display, m_eglImage);
+  }
+
+  if (m_fbo) {
+    s_gles2.glDeleteFramebuffers(1, &m_fbo);
+  }
+
+  GLuint tex[2] = {m_tex, m_blitTex};
+  s_gles2.glDeleteTextures(2, tex);
+
+  delete m_resizer;
+}
+
+void ColorBuffer::readPixels(int x, int y, int width, int height,
+                             GLenum p_format, GLenum p_type, void* pixels) {
+  ScopedHelperContext context(m_helper);
+  if (!context.isOk()) {
+    return;
+  }
+
+  if (bindFbo(&m_fbo, m_tex)) {
+    s_gles2.glReadPixels(x, y, width, height, p_format, p_type, pixels);
+    unbindFbo();
+  }
+}
+
+void ColorBuffer::subUpdate(int x, int y, int width, int height,
+                            GLenum p_format, GLenum p_type, void* pixels) {
+  ScopedHelperContext context(m_helper);
+  if (!context.isOk()) {
+    return;
+  }
+
+  s_gles2.glBindTexture(GL_TEXTURE_2D, m_tex);
+  s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+  s_gles2.glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, p_format,
+                          p_type, pixels);
+}
+
+bool ColorBuffer::blitFromCurrentReadBuffer() {
+  RenderThreadInfo* tInfo = RenderThreadInfo::get();
+  if (!tInfo->currContext) {
+    // no Current context
+    return false;
+  }
+
+  // Copy the content of the current read surface into m_blitEGLImage.
+  // This is done by creating a temporary texture, bind it to the EGLImage
+  // then call glCopyTexSubImage2D().
+  GLuint tmpTex;
+  GLint currTexBind;
+  if (tInfo->currContext->isGL2()) {
+    s_gles2.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
+    s_gles2.glGenTextures(1, &tmpTex);
+    s_gles2.glBindTexture(GL_TEXTURE_2D, tmpTex);
+    s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
+    s_gles2.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width,
+                                m_height);
+    s_gles2.glDeleteTextures(1, &tmpTex);
+    s_gles2.glBindTexture(GL_TEXTURE_2D, currTexBind);
+  } else {
+    s_gles1.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
+    s_gles1.glGenTextures(1, &tmpTex);
+    s_gles1.glBindTexture(GL_TEXTURE_2D, tmpTex);
+    s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
+    s_gles1.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width,
+                                m_height);
+    s_gles1.glDeleteTextures(1, &tmpTex);
+    s_gles1.glBindTexture(GL_TEXTURE_2D, currTexBind);
+  }
+
+  ScopedHelperContext context(m_helper);
+  if (!context.isOk()) {
+    return false;
+  }
+
+  if (!bindFbo(&m_fbo, m_tex)) {
+    return false;
+  }
+
+  // Save current viewport and match it to the current colorbuffer size.
+  GLint vport[4] = {
+      0,
+  };
+  s_gles2.glGetIntegerv(GL_VIEWPORT, vport);
+  s_gles2.glViewport(0, 0, m_width, m_height);
+
+  // render m_blitTex
+  m_helper->getTextureDraw()->draw(m_blitTex);
+
+  // Restore previous viewport.
+  s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]);
+  unbindFbo();
+
+  return true;
+}
+
+bool ColorBuffer::bindToTexture() {
+  if (!m_eglImage) {
+    return false;
+  }
+  RenderThreadInfo* tInfo = RenderThreadInfo::get();
+  if (!tInfo->currContext) {
+    return false;
+  }
+  if (tInfo->currContext->isGL2()) {
+    s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
+  } else {
+    s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
+  }
+  return true;
+}
+
+bool ColorBuffer::bindToRenderbuffer() {
+  if (!m_eglImage) {
+    return false;
+  }
+  RenderThreadInfo* tInfo = RenderThreadInfo::get();
+  if (!tInfo->currContext) {
+    return false;
+  }
+  if (tInfo->currContext->isGL2()) {
+    s_gles2.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES,
+                                                   m_eglImage);
+  } else {
+    s_gles1.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES,
+                                                   m_eglImage);
+  }
+  return true;
+}
+
+void ColorBuffer::readback(unsigned char* img) {
+  ScopedHelperContext context(m_helper);
+  if (!context.isOk()) {
+    return;
+  }
+  if (bindFbo(&m_fbo, m_tex)) {
+    s_gles2.glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE,
+                         img);
+    unbindFbo();
+  }
+}
+
+void ColorBuffer::bind() {
+  const auto id = m_resizer->update(m_tex);
+  s_gles2.glBindTexture(GL_TEXTURE_2D, id);
+}