This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/gpu/opengl/gl_context.cc
Clément Foucault 0f372f3966 GPUContext: Update internal framebuffer size when activating context
This is to ensure the FrameBuffer extents are always up to date.
2020-08-30 13:11:03 +02:00

336 lines
8.6 KiB
C++

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2020, Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup gpu
*/
#include "BLI_assert.h"
#include "BLI_system.h"
#include "BLI_utildefines.h"
#include "GPU_framebuffer.h"
#include "GHOST_C-api.h"
#include "gpu_context_private.hh"
#include "gl_state.hh"
#include "gl_backend.hh" /* TODO remove */
#include "gl_context.hh"
using namespace blender;
using namespace blender::gpu;
/* -------------------------------------------------------------------- */
/** \name Constructor / Destructor
* \{ */
GLContext::GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list)
: shared_orphan_list_(shared_orphan_list)
{
glGenVertexArrays(1, &default_vao_);
float data[4] = {0.0f, 0.0f, 0.0f, 1.0f};
glGenBuffers(1, &default_attr_vbo_);
glBindBuffer(GL_ARRAY_BUFFER, default_attr_vbo_);
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
state_manager = new GLStateManager();
ghost_window_ = ghost_window;
if (ghost_window) {
GLuint default_fbo = GHOST_GetDefaultOpenGLFramebuffer((GHOST_WindowHandle)ghost_window);
GHOST_RectangleHandle bounds = GHOST_GetClientBounds((GHOST_WindowHandle)ghost_window);
int w = GHOST_GetWidthRectangle(bounds);
int h = GHOST_GetHeightRectangle(bounds);
GHOST_DisposeRectangle(bounds);
if (default_fbo != 0) {
front_left = new GLFrameBuffer("front_left", this, GL_COLOR_ATTACHMENT0, default_fbo, w, h);
back_left = new GLFrameBuffer("back_left", this, GL_COLOR_ATTACHMENT0, default_fbo, w, h);
}
else {
front_left = new GLFrameBuffer("front_left", this, GL_FRONT_LEFT, 0, w, h);
back_left = new GLFrameBuffer("back_left", this, GL_BACK_LEFT, 0, w, h);
}
/* TODO(fclem) enable is supported. */
const bool supports_stereo_quad_buffer = false;
if (supports_stereo_quad_buffer) {
front_right = new GLFrameBuffer("front_right", this, GL_FRONT_RIGHT, 0, w, h);
back_right = new GLFrameBuffer("back_right", this, GL_BACK_RIGHT, 0, w, h);
}
}
else {
/* For offscreen contexts. Default framebuffer is NULL. */
back_left = new GLFrameBuffer("back_left", this, GL_NONE, 0, 0, 0);
}
active_fb = back_left;
}
GLContext::~GLContext()
{
BLI_assert(orphaned_framebuffers_.is_empty());
BLI_assert(orphaned_vertarrays_.is_empty());
/* For now don't allow GPUFrameBuffers to be reuse in another context. */
BLI_assert(framebuffers_.is_empty());
/* Delete vaos so the batch can be reused in another context. */
for (GLVaoCache *cache : vao_caches_) {
cache->clear();
}
glDeleteVertexArrays(1, &default_vao_);
glDeleteBuffers(1, &default_attr_vbo_);
if (front_left) {
delete front_left;
}
if (back_left) {
delete back_left;
}
if (front_right) {
delete front_right;
}
if (back_right) {
delete back_right;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Activate / Deactivate context
* \{ */
void GLContext::activate(void)
{
/* Make sure no other context is already bound to this thread. */
BLI_assert(is_active_ == false);
is_active_ = true;
thread_ = pthread_self();
/* Clear accumulated orphans. */
orphans_clear();
if (ghost_window_) {
/* Get the correct framebuffer size for the internal framebuffers. */
GHOST_RectangleHandle bounds = GHOST_GetClientBounds((GHOST_WindowHandle)ghost_window_);
int w = GHOST_GetWidthRectangle(bounds);
int h = GHOST_GetHeightRectangle(bounds);
GHOST_DisposeRectangle(bounds);
if (front_left) {
front_left->size_set(w, h);
}
if (back_left) {
back_left->size_set(w, h);
}
if (front_right) {
front_right->size_set(w, h);
}
if (back_right) {
back_right->size_set(w, h);
}
}
}
void GLContext::deactivate(void)
{
is_active_ = false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Safe object deletion
*
* GPU objects can be freed when the context is not bound.
* In this case we delay the deletion until the context is bound again.
* \{ */
void GLSharedOrphanLists::orphans_clear(void)
{
/* Check if any context is active on this thread! */
BLI_assert(GPU_context_active_get());
lists_mutex.lock();
if (!buffers.is_empty()) {
glDeleteBuffers((uint)buffers.size(), buffers.data());
buffers.clear();
}
if (!textures.is_empty()) {
glDeleteTextures((uint)textures.size(), textures.data());
textures.clear();
}
lists_mutex.unlock();
};
void GLContext::orphans_clear(void)
{
/* Check if context has been activated by another thread! */
BLI_assert(this->is_active_on_thread());
lists_mutex_.lock();
if (!orphaned_vertarrays_.is_empty()) {
glDeleteVertexArrays((uint)orphaned_vertarrays_.size(), orphaned_vertarrays_.data());
orphaned_vertarrays_.clear();
}
if (!orphaned_framebuffers_.is_empty()) {
glDeleteFramebuffers((uint)orphaned_framebuffers_.size(), orphaned_framebuffers_.data());
orphaned_framebuffers_.clear();
}
lists_mutex_.unlock();
shared_orphan_list_.orphans_clear();
};
void GLContext::orphans_add(Vector<GLuint> &orphan_list, std::mutex &list_mutex, GLuint id)
{
list_mutex.lock();
orphan_list.append(id);
list_mutex.unlock();
}
void GLContext::vao_free(GLuint vao_id)
{
if (this == GPU_context_active_get()) {
glDeleteVertexArrays(1, &vao_id);
}
else {
orphans_add(orphaned_vertarrays_, lists_mutex_, vao_id);
}
}
void GLContext::fbo_free(GLuint fbo_id)
{
if (this == GPU_context_active_get()) {
glDeleteFramebuffers(1, &fbo_id);
}
else {
orphans_add(orphaned_framebuffers_, lists_mutex_, fbo_id);
}
}
void GLBackend::buf_free(GLuint buf_id)
{
/* Any context can free. */
if (GPU_context_active_get()) {
glDeleteBuffers(1, &buf_id);
}
else {
orphans_add(shared_orphan_list_.buffers, shared_orphan_list_.lists_mutex, buf_id);
}
}
void GLBackend::tex_free(GLuint tex_id)
{
/* Any context can free. */
if (GPU_context_active_get()) {
glDeleteTextures(1, &tex_id);
}
else {
orphans_add(shared_orphan_list_.textures, shared_orphan_list_.lists_mutex, tex_id);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Linked object deletion
*
* These objects contain data that are stored per context. We
* need to do some cleanup if they are used across context or if context
* is discarded.
* \{ */
void GLContext::vao_cache_register(GLVaoCache *cache)
{
lists_mutex_.lock();
vao_caches_.add(cache);
lists_mutex_.unlock();
}
void GLContext::vao_cache_unregister(GLVaoCache *cache)
{
lists_mutex_.lock();
vao_caches_.remove(cache);
lists_mutex_.unlock();
}
void GLContext::framebuffer_register(struct GPUFrameBuffer *fb)
{
#ifdef DEBUG
lists_mutex_.lock();
framebuffers_.add(fb);
lists_mutex_.unlock();
#else
UNUSED_VARS(fb);
#endif
}
void GLContext::framebuffer_unregister(struct GPUFrameBuffer *fb)
{
#ifdef DEBUG
lists_mutex_.lock();
framebuffers_.remove(fb);
lists_mutex_.unlock();
#else
UNUSED_VARS(fb);
#endif
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Error Checking
*
* This is only useful for implementation that does not support the KHR_debug extension.
* \{ */
void GLContext::check_error(const char *info)
{
GLenum error = glGetError();
#define ERROR_CASE(err) \
case err: \
fprintf(stderr, "GL error: %s : %s\n", #err, info); \
BLI_system_backtrace(stderr); \
break;
switch (error) {
ERROR_CASE(GL_INVALID_ENUM)
ERROR_CASE(GL_INVALID_VALUE)
ERROR_CASE(GL_INVALID_OPERATION)
ERROR_CASE(GL_INVALID_FRAMEBUFFER_OPERATION)
ERROR_CASE(GL_OUT_OF_MEMORY)
ERROR_CASE(GL_STACK_UNDERFLOW)
ERROR_CASE(GL_STACK_OVERFLOW)
case GL_NO_ERROR:
break;
default:
fprintf(stderr, "Unknown GL error: %x : %s", error, info);
break;
}
}
/** \} */