blob: 34ed5366e33175cd82afcfec1c8c93d38bd8d49c [file] [log] [blame]
// Copyright 2016 The Android Open Source Project
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "android/skin/qt/gl-canvas.h"
#include "android/skin/qt/gl-common.h"
#include "GLES2/gl2.h"
#include <cassert>
GLCanvas::GLCanvas(int w, int h, const GLESv2Dispatch* gl_dispatch) :
// Note that width and height must be powers of 2 to comply with
// GLES 2.0
mWidth(w * 2),
mHeight(h * 2),
mGLES2(gl_dispatch),
mFramebuffer(0),
mTargetTexture(0),
mDepthBuffer(0) {
assert(((mWidth & (mWidth - 1)) == 0) && ((mHeight & (mHeight - 1)) == 0));
// Create and bind framebuffer object.
mGLES2->glGenFramebuffers(1, &mFramebuffer);
CHECK_GL_ERROR("Failed to create framebuffer object");
mGLES2->glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
CHECK_GL_ERROR("Failed to bind framebuffer object");
// Create a texture to render to.
mGLES2->glGenTextures(1, &mTargetTexture);
CHECK_GL_ERROR("Failed to create target texture for FBO");
mGLES2->glBindTexture(GL_TEXTURE_2D, mTargetTexture);
mGLES2->glTexImage2D(GL_TEXTURE_2D,
0, // mipmap level 0
GL_RGB,
width(),
height(),
0, // border, must be 0
GL_RGB,
GL_UNSIGNED_BYTE,
0); // don't upload any data, just allocate space
CHECK_GL_ERROR("Failed to populate target texture");
mGLES2->glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
mGLES2->glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
mGLES2->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
mGLES2->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
mGLES2->glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
mTargetTexture,
0); // mipmap level, must be 0
CHECK_GL_ERROR("Failed to set FBO color attachment");
// Create a depth buffer for the FBO.
mGLES2->glGenRenderbuffers(1, &mDepthBuffer);
CHECK_GL_ERROR("Failed to create depth buffer");
mGLES2->glBindRenderbuffer(GL_RENDERBUFFER, mDepthBuffer);
mGLES2->glRenderbufferStorage(GL_RENDERBUFFER,
GL_DEPTH_COMPONENT16,
width(),
height());
CHECK_GL_ERROR("Failed to initialize depth buffer");
mGLES2->glFramebufferRenderbuffer(GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER,
mDepthBuffer);
CHECK_GL_ERROR("Failed to attach depth buffer to FBO");
mGLES2->glBindRenderbuffer(GL_RENDERBUFFER, 0);
// Verify that FBO is valid.
GLenum fb_status = mGLES2->glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
fprintf(stderr, "FBO not complete");
}
// Restore the default frame-buffer.
mGLES2->glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
GLCanvas::~GLCanvas() {
if (mFramebuffer) {
mGLES2->glDeleteFramebuffers(1, &mFramebuffer);
mFramebuffer = 0;
}
if (mTargetTexture) {
mGLES2->glDeleteTextures(1, &mTargetTexture);
mTargetTexture = 0;
}
if (mDepthBuffer) {
mGLES2->glDeleteRenderbuffers(1, &mDepthBuffer);
mDepthBuffer = 0;
}
}
void GLCanvas::bind() {
mGLES2->glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
mGLES2->glViewport(0, 0, width(), height());
}
void GLCanvas::unbind() {
mGLES2->glBindFramebuffer(GL_FRAMEBUFFER, 0);
}