blob: 6348e81035f4a30ee11d09a3e9ea3d7748b90be0 [file] [log] [blame]
// Copyright 2017 The Cobalt Authors. All Rights Reserved.
//
// 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 "starboard/configuration.h"
#include "cobalt/renderer/backend/egl/framebuffer.h"
#include "base/logging.h"
#include "cobalt/renderer/backend/egl/graphics_context.h"
#include "cobalt/renderer/backend/egl/texture.h"
#include "cobalt/renderer/backend/egl/utils.h"
#include "cobalt/renderer/egl_and_gles.h"
namespace cobalt {
namespace renderer {
namespace backend {
FramebufferEGL::FramebufferEGL(GraphicsContextEGL* graphics_context,
const math::Size& size, GLenum color_format,
GLenum depth_format)
: graphics_context_(graphics_context), size_(size), error_(false) {
GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(graphics_context_);
SbGlEnum gl_error;
// Create the framebuffer object.
GL_CALL_SIMPLE(glGenFramebuffers(1, &framebuffer_handle_));
gl_error = GL_CALL_SIMPLE(glGetError());
if (gl_error != GL_NO_ERROR) {
LOG(ERROR) << "Error creating new framebuffer. Error = " << gl_error;
error_ = true;
return;
}
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_handle_));
// Create and attach a texture for color.
GLuint color_handle = 0;
GL_CALL_SIMPLE(glGenTextures(1, &color_handle));
gl_error = GL_CALL_SIMPLE(glGetError());
if (gl_error != GL_NO_ERROR) {
LOG(ERROR) << "Error creating new texture. Error = " << gl_error;
error_ = true;
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
GL_CALL(glDeleteFramebuffers(1, &framebuffer_handle_));
return;
}
GL_CALL(glBindTexture(GL_TEXTURE_2D, color_handle));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
GL_CALL_SIMPLE(glTexImage2D(GL_TEXTURE_2D, 0, color_format, size_.width(),
size_.height(), 0, color_format, GL_UNSIGNED_BYTE,
0));
if (GL_CALL_SIMPLE(glGetError()) != GL_NO_ERROR) {
LOG(ERROR) << "Error allocating new texture backing for a framebuffer.";
error_ = true;
GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
GL_CALL(glDeleteTextures(1, &color_handle));
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
GL_CALL(glDeleteFramebuffers(1, &framebuffer_handle_));
return;
}
GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, color_handle, 0));
GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
color_texture_.reset(new TextureEGL(graphics_context_, color_handle, size_,
color_format, GL_TEXTURE_2D,
base::Closure()));
// Create and attach a depth buffer if requested.
depthbuffer_handle_ = 0;
if (depth_format != GL_NONE) {
if (!CreateDepthAttachment(depth_format)) {
LOG(ERROR) << "Error creating depth attachment for framebuffer.";
error_ = true;
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
GL_CALL(glDeleteFramebuffers(1, &framebuffer_handle_));
color_texture_.reset();
return;
}
}
// Verify the framebuffer object is valid.
DCHECK_EQ(GL_CALL_SIMPLE(glCheckFramebufferStatus(GL_FRAMEBUFFER)),
GL_FRAMEBUFFER_COMPLETE);
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
}
void FramebufferEGL::EnsureDepthBufferAttached(GLenum depth_format) {
if (depthbuffer_handle_ == 0) {
GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
graphics_context_);
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_handle_));
CreateDepthAttachment(depth_format);
// Verify the framebuffer object is valid.
DCHECK_EQ(GL_CALL_SIMPLE(glCheckFramebufferStatus(GL_FRAMEBUFFER)),
GL_FRAMEBUFFER_COMPLETE);
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
}
}
FramebufferEGL::~FramebufferEGL() {
if (!error_) {
GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
graphics_context_);
GL_CALL(glDeleteFramebuffers(1, &framebuffer_handle_));
if (depthbuffer_handle_ != 0) {
GL_CALL(glDeleteRenderbuffers(1, &depthbuffer_handle_));
}
}
}
bool FramebufferEGL::CreateDepthAttachment(GLenum depth_format) {
GL_CALL_SIMPLE(glGenRenderbuffers(1, &depthbuffer_handle_));
if (GL_CALL_SIMPLE(glGetError()) != GL_NO_ERROR) {
LOG(ERROR) << "Error creating depth buffer object.";
return false;
}
GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer_handle_));
GL_CALL_SIMPLE(glRenderbufferStorage(GL_RENDERBUFFER, depth_format,
size_.width(), size_.height()));
if (GL_CALL_SIMPLE(glGetError()) != GL_NO_ERROR) {
LOG(ERROR) << "Error allocating memory for depth buffer.";
GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
GL_CALL(glDeleteRenderbuffers(1, &depthbuffer_handle_));
return false;
}
GL_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, depthbuffer_handle_));
GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
return true;
}
} // namespace backend
} // namespace renderer
} // namespace cobalt