TYPE3
[iec.git] / src / type3_AndroidCloud / anbox-master / src / anbox / graphics / emugl / ColorBuffer.cpp
1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "anbox/graphics/emugl/ColorBuffer.h"
18 #include "anbox/graphics/emugl/DispatchTables.h"
19 #include "anbox/graphics/emugl/RenderThreadInfo.h"
20 #include "anbox/graphics/emugl/TextureDraw.h"
21 #include "anbox/graphics/emugl/TextureResize.h"
22 #include "anbox/logger.h"
23
24 #include "external/android-emugl/host/include/OpenGLESDispatch/EGLDispatch.h"
25
26 #include <stdio.h>
27
28 namespace {
29
30 // <EGL/egl.h> defines many types as 'void*' while they're really
31 // implemented as unsigned integers. These convenience template functions
32 // help casting between them safely without generating compiler warnings.
33 inline void* SafePointerFromUInt(unsigned int handle) {
34   return reinterpret_cast<void*>(static_cast<uintptr_t>(handle));
35 }
36
37 // Lazily create and bind a framebuffer object to the current host context.
38 // |fbo| is the address of the framebuffer object name.
39 // |tex| is the name of a texture that is attached to the framebuffer object
40 // on creation only. I.e. all rendering operations will target it.
41 // returns true in case of success, false on failure.
42 bool bindFbo(GLuint* fbo, GLuint tex) {
43   if (*fbo) {
44     // fbo already exist - just bind
45     s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
46     return true;
47   }
48
49   s_gles2.glGenFramebuffers(1, fbo);
50   s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
51   s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_OES,
52                                  GL_TEXTURE_2D, tex, 0);
53   GLenum status = s_gles2.glCheckFramebufferStatus(GL_FRAMEBUFFER);
54   if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
55     ERROR("FBO not complete: %#x", status);
56     s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
57     s_gles2.glDeleteFramebuffers(1, fbo);
58     *fbo = 0;
59     return false;
60   }
61   return true;
62 }
63
64 void unbindFbo() { s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); }
65
66 // Helper class to use a ColorBuffer::Helper context.
67 // Usage is pretty simple:
68 //
69 //     {
70 //        ScopedHelperContext context(m_helper);
71 //        if (!context.isOk()) {
72 //            return false;   // something bad happened.
73 //        }
74 //        .... do something ....
75 //     }   // automatically calls m_helper->teardownContext();
76 //
77 class ScopedHelperContext {
78  public:
79   ScopedHelperContext(ColorBuffer::Helper* helper) : mHelper(helper) {
80     if (!helper->setupContext()) {
81       mHelper = NULL;
82     }
83   }
84
85   bool isOk() const { return mHelper != NULL; }
86
87   ~ScopedHelperContext() { release(); }
88
89   void release() {
90     if (mHelper) {
91       mHelper->teardownContext();
92       mHelper = NULL;
93     }
94   }
95
96  private:
97   ColorBuffer::Helper* mHelper;
98 };
99
100 }  // namespace
101
102 // static
103 ColorBuffer* ColorBuffer::create(EGLDisplay p_display, int p_width,
104                                  int p_height, GLenum p_internalFormat,
105                                  bool has_eglimage_texture_2d, Helper* helper) {
106   GLenum texInternalFormat = 0;
107
108   switch (p_internalFormat) {
109     case GL_RGB:
110     case GL_RGB565_OES:
111       texInternalFormat = GL_RGB;
112       break;
113
114     case GL_RGBA:
115     case GL_RGB5_A1_OES:
116     case GL_RGBA4_OES:
117       texInternalFormat = GL_RGBA;
118       break;
119
120     default:
121       return NULL;
122       break;
123   }
124
125   ScopedHelperContext context(helper);
126   if (!context.isOk()) {
127     return NULL;
128   }
129
130   ColorBuffer* cb = new ColorBuffer(p_display, helper);
131
132   s_gles2.glGenTextures(1, &cb->m_tex);
133   s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_tex);
134
135   int nComp = (texInternalFormat == GL_RGB ? 3 : 4);
136
137   char* zBuff = static_cast<char*>(::calloc(nComp * p_width * p_height, 1));
138   s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, texInternalFormat, p_width, p_height,
139                        0, texInternalFormat, GL_UNSIGNED_BYTE, zBuff);
140   ::free(zBuff);
141
142   s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
143   s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
144   s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
145   s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
146
147   //
148   // create another texture for that colorbuffer for blit
149   //
150   s_gles2.glGenTextures(1, &cb->m_blitTex);
151   s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_blitTex);
152   s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, texInternalFormat, p_width, p_height,
153                        0, texInternalFormat, GL_UNSIGNED_BYTE, NULL);
154
155   s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
156   s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
157   s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
158   s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
159
160   cb->m_width = p_width;
161   cb->m_height = p_height;
162   cb->m_internalFormat = texInternalFormat;
163
164   if (has_eglimage_texture_2d) {
165     cb->m_eglImage = s_egl.eglCreateImageKHR(
166         p_display, s_egl.eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR,
167         reinterpret_cast<EGLClientBuffer>(SafePointerFromUInt(cb->m_tex)), NULL);
168
169     cb->m_blitEGLImage = s_egl.eglCreateImageKHR(
170         p_display, s_egl.eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR,
171         reinterpret_cast<EGLClientBuffer>(SafePointerFromUInt(cb->m_blitTex)), NULL);
172   }
173
174   cb->m_resizer = new TextureResize(p_width, p_height);
175
176   return cb;
177 }
178
179 ColorBuffer::ColorBuffer(EGLDisplay display, Helper* helper)
180     : m_tex(0),
181       m_blitTex(0),
182       m_eglImage(NULL),
183       m_blitEGLImage(NULL),
184       m_fbo(0),
185       m_internalFormat(0),
186       m_display(display),
187       m_helper(helper) {}
188
189 ColorBuffer::~ColorBuffer() {
190   ScopedHelperContext context(m_helper);
191
192   if (m_blitEGLImage) {
193     s_egl.eglDestroyImageKHR(m_display, m_blitEGLImage);
194   }
195   if (m_eglImage) {
196     s_egl.eglDestroyImageKHR(m_display, m_eglImage);
197   }
198
199   if (m_fbo) {
200     s_gles2.glDeleteFramebuffers(1, &m_fbo);
201   }
202
203   GLuint tex[2] = {m_tex, m_blitTex};
204   s_gles2.glDeleteTextures(2, tex);
205
206   delete m_resizer;
207 }
208
209 void ColorBuffer::readPixels(int x, int y, int width, int height,
210                              GLenum p_format, GLenum p_type, void* pixels) {
211   ScopedHelperContext context(m_helper);
212   if (!context.isOk()) {
213     return;
214   }
215
216   if (bindFbo(&m_fbo, m_tex)) {
217     s_gles2.glReadPixels(x, y, width, height, p_format, p_type, pixels);
218     unbindFbo();
219   }
220 }
221
222 void ColorBuffer::subUpdate(int x, int y, int width, int height,
223                             GLenum p_format, GLenum p_type, void* pixels) {
224   ScopedHelperContext context(m_helper);
225   if (!context.isOk()) {
226     return;
227   }
228
229   s_gles2.glBindTexture(GL_TEXTURE_2D, m_tex);
230   s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
231   s_gles2.glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, p_format,
232                           p_type, pixels);
233 }
234
235 bool ColorBuffer::blitFromCurrentReadBuffer() {
236   RenderThreadInfo* tInfo = RenderThreadInfo::get();
237   if (!tInfo->currContext) {
238     // no Current context
239     return false;
240   }
241
242   // Copy the content of the current read surface into m_blitEGLImage.
243   // This is done by creating a temporary texture, bind it to the EGLImage
244   // then call glCopyTexSubImage2D().
245   GLuint tmpTex;
246   GLint currTexBind;
247   if (tInfo->currContext->isGL2()) {
248     s_gles2.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
249     s_gles2.glGenTextures(1, &tmpTex);
250     s_gles2.glBindTexture(GL_TEXTURE_2D, tmpTex);
251     s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
252     s_gles2.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width,
253                                 m_height);
254     s_gles2.glDeleteTextures(1, &tmpTex);
255     s_gles2.glBindTexture(GL_TEXTURE_2D, currTexBind);
256   } else {
257     s_gles1.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
258     s_gles1.glGenTextures(1, &tmpTex);
259     s_gles1.glBindTexture(GL_TEXTURE_2D, tmpTex);
260     s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
261     s_gles1.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width,
262                                 m_height);
263     s_gles1.glDeleteTextures(1, &tmpTex);
264     s_gles1.glBindTexture(GL_TEXTURE_2D, currTexBind);
265   }
266
267   ScopedHelperContext context(m_helper);
268   if (!context.isOk()) {
269     return false;
270   }
271
272   if (!bindFbo(&m_fbo, m_tex)) {
273     return false;
274   }
275
276   // Save current viewport and match it to the current colorbuffer size.
277   GLint vport[4] = {
278       0,
279   };
280   s_gles2.glGetIntegerv(GL_VIEWPORT, vport);
281   s_gles2.glViewport(0, 0, m_width, m_height);
282
283   // render m_blitTex
284   m_helper->getTextureDraw()->draw(m_blitTex);
285
286   // Restore previous viewport.
287   s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]);
288   unbindFbo();
289
290   return true;
291 }
292
293 bool ColorBuffer::bindToTexture() {
294   if (!m_eglImage) {
295     return false;
296   }
297   RenderThreadInfo* tInfo = RenderThreadInfo::get();
298   if (!tInfo->currContext) {
299     return false;
300   }
301   if (tInfo->currContext->isGL2()) {
302     s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
303   } else {
304     s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
305   }
306   return true;
307 }
308
309 bool ColorBuffer::bindToRenderbuffer() {
310   if (!m_eglImage) {
311     return false;
312   }
313   RenderThreadInfo* tInfo = RenderThreadInfo::get();
314   if (!tInfo->currContext) {
315     return false;
316   }
317   if (tInfo->currContext->isGL2()) {
318     s_gles2.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES,
319                                                    m_eglImage);
320   } else {
321     s_gles1.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES,
322                                                    m_eglImage);
323   }
324   return true;
325 }
326
327 void ColorBuffer::readback(unsigned char* img) {
328   ScopedHelperContext context(m_helper);
329   if (!context.isOk()) {
330     return;
331   }
332   if (bindFbo(&m_fbo, m_tex)) {
333     s_gles2.glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE,
334                          img);
335     unbindFbo();
336   }
337 }
338
339 void ColorBuffer::bind() {
340   const auto id = m_resizer->update(m_tex);
341   s_gles2.glBindTexture(GL_TEXTURE_2D, id);
342 }