333 lines
8.8 KiB
C++
333 lines
8.8 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 "BKE_global.h"
|
|
|
|
#include "GPU_framebuffer.h"
|
|
|
|
#include "GHOST_C-api.h"
|
|
|
|
#include "gpu_context_private.hh"
|
|
#include "gpu_immediate_private.hh"
|
|
|
|
#include "gl_debug.hh"
|
|
#include "gl_immediate.hh"
|
|
#include "gl_state.hh"
|
|
#include "gl_uniform_buffer.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)
|
|
{
|
|
if (G.debug & G_DEBUG_GPU) {
|
|
debug::init_gl_callbacks();
|
|
}
|
|
|
|
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();
|
|
imm = new GLImmediate();
|
|
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) {
|
|
/* Bind default framebuffer, otherwise state might be undefined because of
|
|
* detect_mip_render_workaround(). */
|
|
glBindFramebuffer(GL_FRAMEBUFFER, default_fbo);
|
|
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);
|
|
}
|
|
|
|
GLboolean supports_stereo_quad_buffer = GL_FALSE;
|
|
glGetBooleanv(GL_STEREO, &supports_stereo_quad_buffer);
|
|
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;
|
|
static_cast<GLStateManager *>(state_manager)->active_fb = static_cast<GLFrameBuffer *>(
|
|
active_fb);
|
|
}
|
|
|
|
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 VAO's so the batch can be reused in another context. */
|
|
for (GLVaoCache *cache : vao_caches_) {
|
|
cache->clear();
|
|
}
|
|
glDeleteBuffers(1, &default_attr_vbo_);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Activate / Deactivate context
|
|
* \{ */
|
|
|
|
void GLContext::activate()
|
|
{
|
|
/* 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);
|
|
}
|
|
}
|
|
|
|
/* Not really following the state but we should consider
|
|
* no ubo bound when activating a context. */
|
|
bound_ubo_slots = 0;
|
|
|
|
immActivate();
|
|
}
|
|
|
|
void GLContext::deactivate()
|
|
{
|
|
immDeactivate();
|
|
is_active_ = false;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Flush, Finish & sync
|
|
* \{ */
|
|
|
|
void GLContext::flush()
|
|
{
|
|
glFlush();
|
|
}
|
|
|
|
void GLContext::finish()
|
|
{
|
|
glFinish();
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \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()
|
|
{
|
|
/* Check if any context is active on this thread! */
|
|
BLI_assert(GLContext::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()
|
|
{
|
|
/* 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 == GLContext::get()) {
|
|
glDeleteVertexArrays(1, &vao_id);
|
|
}
|
|
else {
|
|
orphans_add(orphaned_vertarrays_, lists_mutex_, vao_id);
|
|
}
|
|
}
|
|
|
|
void GLContext::fbo_free(GLuint fbo_id)
|
|
{
|
|
if (this == GLContext::get()) {
|
|
glDeleteFramebuffers(1, &fbo_id);
|
|
}
|
|
else {
|
|
orphans_add(orphaned_framebuffers_, lists_mutex_, fbo_id);
|
|
}
|
|
}
|
|
|
|
void GLContext::buf_free(GLuint buf_id)
|
|
{
|
|
/* Any context can free. */
|
|
if (GLContext::get()) {
|
|
glDeleteBuffers(1, &buf_id);
|
|
}
|
|
else {
|
|
GLSharedOrphanLists &orphan_list = GLBackend::get()->shared_orphan_list_get();
|
|
orphans_add(orphan_list.buffers, orphan_list.lists_mutex, buf_id);
|
|
}
|
|
}
|
|
|
|
void GLContext::tex_free(GLuint tex_id)
|
|
{
|
|
/* Any context can free. */
|
|
if (GLContext::get()) {
|
|
glDeleteTextures(1, &tex_id);
|
|
}
|
|
else {
|
|
GLSharedOrphanLists &orphan_list = GLBackend::get()->shared_orphan_list_get();
|
|
orphans_add(orphan_list.textures, 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();
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Memory statistics
|
|
* \{ */
|
|
|
|
void GLContext::memory_statistics_get(int *r_total_mem, int *r_free_mem)
|
|
{
|
|
/* TODO(merwin): use Apple's platform API to get this info. */
|
|
if (GLEW_NVX_gpu_memory_info) {
|
|
/* Returned value in Kb. */
|
|
glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, r_total_mem);
|
|
glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, r_free_mem);
|
|
}
|
|
else if (GLEW_ATI_meminfo) {
|
|
int stats[4];
|
|
glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, stats);
|
|
|
|
*r_total_mem = 0;
|
|
*r_free_mem = stats[0]; /* Total memory free in the pool. */
|
|
}
|
|
else {
|
|
*r_total_mem = 0;
|
|
*r_free_mem = 0;
|
|
}
|
|
}
|
|
|
|
/** \} */
|