2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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"
24 #include "external/android-emugl/host/include/OpenGLESDispatch/EGLDispatch.h"
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));
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) {
44 // fbo already exist - just bind
45 s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
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);
64 void unbindFbo() { s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); }
66 // Helper class to use a ColorBuffer::Helper context.
67 // Usage is pretty simple:
70 // ScopedHelperContext context(m_helper);
71 // if (!context.isOk()) {
72 // return false; // something bad happened.
74 // .... do something ....
75 // } // automatically calls m_helper->teardownContext();
77 class ScopedHelperContext {
79 ScopedHelperContext(ColorBuffer::Helper* helper) : mHelper(helper) {
80 if (!helper->setupContext()) {
85 bool isOk() const { return mHelper != NULL; }
87 ~ScopedHelperContext() { release(); }
91 mHelper->teardownContext();
97 ColorBuffer::Helper* mHelper;
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;
108 switch (p_internalFormat) {
111 texInternalFormat = GL_RGB;
117 texInternalFormat = GL_RGBA;
125 ScopedHelperContext context(helper);
126 if (!context.isOk()) {
130 ColorBuffer* cb = new ColorBuffer(p_display, helper);
132 s_gles2.glGenTextures(1, &cb->m_tex);
133 s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_tex);
135 int nComp = (texInternalFormat == GL_RGB ? 3 : 4);
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);
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);
148 // create another texture for that colorbuffer for blit
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);
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);
160 cb->m_width = p_width;
161 cb->m_height = p_height;
162 cb->m_internalFormat = texInternalFormat;
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);
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);
174 cb->m_resizer = new TextureResize(p_width, p_height);
179 ColorBuffer::ColorBuffer(EGLDisplay display, Helper* helper)
183 m_blitEGLImage(NULL),
189 ColorBuffer::~ColorBuffer() {
190 ScopedHelperContext context(m_helper);
192 if (m_blitEGLImage) {
193 s_egl.eglDestroyImageKHR(m_display, m_blitEGLImage);
196 s_egl.eglDestroyImageKHR(m_display, m_eglImage);
200 s_gles2.glDeleteFramebuffers(1, &m_fbo);
203 GLuint tex[2] = {m_tex, m_blitTex};
204 s_gles2.glDeleteTextures(2, tex);
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()) {
216 if (bindFbo(&m_fbo, m_tex)) {
217 s_gles2.glReadPixels(x, y, width, height, p_format, p_type, pixels);
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()) {
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,
235 bool ColorBuffer::blitFromCurrentReadBuffer() {
236 RenderThreadInfo* tInfo = RenderThreadInfo::get();
237 if (!tInfo->currContext) {
238 // no Current context
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().
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,
254 s_gles2.glDeleteTextures(1, &tmpTex);
255 s_gles2.glBindTexture(GL_TEXTURE_2D, currTexBind);
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,
263 s_gles1.glDeleteTextures(1, &tmpTex);
264 s_gles1.glBindTexture(GL_TEXTURE_2D, currTexBind);
267 ScopedHelperContext context(m_helper);
268 if (!context.isOk()) {
272 if (!bindFbo(&m_fbo, m_tex)) {
276 // Save current viewport and match it to the current colorbuffer size.
280 s_gles2.glGetIntegerv(GL_VIEWPORT, vport);
281 s_gles2.glViewport(0, 0, m_width, m_height);
284 m_helper->getTextureDraw()->draw(m_blitTex);
286 // Restore previous viewport.
287 s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]);
293 bool ColorBuffer::bindToTexture() {
297 RenderThreadInfo* tInfo = RenderThreadInfo::get();
298 if (!tInfo->currContext) {
301 if (tInfo->currContext->isGL2()) {
302 s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
304 s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
309 bool ColorBuffer::bindToRenderbuffer() {
313 RenderThreadInfo* tInfo = RenderThreadInfo::get();
314 if (!tInfo->currContext) {
317 if (tInfo->currContext->isGL2()) {
318 s_gles2.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES,
321 s_gles1.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES,
327 void ColorBuffer::readback(unsigned char* img) {
328 ScopedHelperContext context(m_helper);
329 if (!context.isOk()) {
332 if (bindFbo(&m_fbo, m_tex)) {
333 s_gles2.glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE,
339 void ColorBuffer::bind() {
340 const auto id = m_resizer->update(m_tex);
341 s_gles2.glBindTexture(GL_TEXTURE_2D, id);