GPUQuery: GL Backend isolation

This is part of the Vulkan task T68990.

This introduce a new GLQueryPool for managing queries in an
implementation agnostic manner.

This modify the GPU selection query to use this new object.
This also make use of blender::Vector for better code quality.

No real functionnal change.
This commit is contained in:
Clément Foucault 2020-09-07 23:52:55 +02:00
parent d4fd363d05
commit 5c2ac8520e
Notes: blender-bot 2023-02-14 08:21:44 +01:00
Referenced by issue #47030, BMesh booleans (track todo's)
10 changed files with 294 additions and 63 deletions

View File

@ -74,9 +74,10 @@ set(SRC
intern/gpu_matrix.cc
intern/gpu_node_graph.c
intern/gpu_platform.cc
intern/gpu_query.cc
intern/gpu_select.c
intern/gpu_select_pick.c
intern/gpu_select_sample_query.c
intern/gpu_select_sample_query.cc
intern/gpu_shader.cc
intern/gpu_shader_builtin.c
intern/gpu_shader_interface.cc
@ -95,6 +96,7 @@ set(SRC
opengl/gl_framebuffer.cc
opengl/gl_immediate.cc
opengl/gl_index_buffer.cc
opengl/gl_query.cc
opengl/gl_shader.cc
opengl/gl_shader_interface.cc
opengl/gl_state.cc
@ -146,6 +148,7 @@ set(SRC
intern/gpu_node_graph.h
intern/gpu_private.h
intern/gpu_platform_private.hh
intern/gpu_query.hh
intern/gpu_select_private.h
intern/gpu_shader_private.hh
intern/gpu_shader_interface.hh
@ -164,6 +167,7 @@ set(SRC
opengl/gl_immediate.hh
opengl/gl_index_buffer.hh
opengl/gl_primitive.hh
opengl/gl_query.hh
opengl/gl_shader.hh
opengl/gl_shader_interface.hh
opengl/gl_state.hh

View File

@ -34,6 +34,7 @@ class Batch;
class DrawList;
class FrameBuffer;
class IndexBuf;
class QueryPool;
class Shader;
class Texture;
class UniformBuf;
@ -53,6 +54,7 @@ class GPUBackend {
virtual DrawList *drawlist_alloc(int list_length) = 0;
virtual FrameBuffer *framebuffer_alloc(const char *name) = 0;
virtual IndexBuf *indexbuf_alloc(void) = 0;
virtual QueryPool *querypool_alloc(void) = 0;
virtual Shader *shader_alloc(const char *name) = 0;
virtual Texture *texture_alloc(const char *name) = 0;
virtual UniformBuf *uniformbuf_alloc(int size, const char *name) = 0;

View File

@ -0,0 +1,28 @@
/*
* 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 "gpu_query.hh"
using namespace blender::gpu;
/* TODO(fclem) Make the associated C-API to use inside DRW profiler. */

View File

@ -0,0 +1,59 @@
/*
* 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
*/
#pragma once
#include "BLI_span.hh"
namespace blender::gpu {
typedef enum GPUQueryType {
GPU_QUERY_OCCLUSION = 0,
} GPUQueryType;
class QueryPool {
public:
virtual ~QueryPool(){};
/**
* Will start and end the query at this index inside the pool. The pool will resize
* automatically but does not support sparse allocation. So prefer using consecutive indices.
*/
virtual void init(GPUQueryType type) = 0;
/**
* Will start and end the query at this index inside the pool.
* The pool will resize automatically.
*/
virtual void begin_query(void) = 0;
virtual void end_query(void) = 0;
/**
* Must be fed with a buffer large enough to contain all the queries issued.
* IMPORTANT: Result for each query can be either binary or represent the number of samples
* drawn.
*/
virtual void get_occlusion_result(MutableSpan<uint32_t> r_values) = 0;
};
} // namespace blender::gpu

View File

@ -25,6 +25,10 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* gpu_select_pick */
void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, char mode);
bool gpu_select_pick_load_id(uint id, bool end);
@ -42,3 +46,7 @@ bool gpu_select_query_load_id(uint id);
uint gpu_select_query_end(void);
#define SELECT_ID_NONE ((uint)0xffffffff)
#ifdef __cplusplus
}
#endif

View File

@ -27,7 +27,6 @@
#include <stdlib.h>
#include "GPU_framebuffer.h"
#include "GPU_glew.h"
#include "GPU_select.h"
#include "GPU_state.h"
@ -35,24 +34,25 @@
#include "BLI_rect.h"
#include "BLI_bitmap.h"
#include "BLI_utildefines.h"
#include "BLI_vector.hh"
#include "gpu_backend.hh"
#include "gpu_query.hh"
#include "gpu_select_private.h"
/* Ad hoc number of queries to allocate to skip doing many glGenQueries */
#define ALLOC_QUERIES 200
using namespace blender;
using namespace blender::gpu;
typedef struct GPUQueryState {
typedef struct GPUSelectQueryState {
/* Tracks whether a query has been issued so that gpu_load_id can end the previous one */
bool query_issued;
/* array holding the OpenGL query identifiers */
uint *queries;
/* array holding the id corresponding to each query */
uint *id;
/* number of queries in *queries and *id */
uint num_of_queries;
/* index to the next query to start */
uint active_query;
/* GPU queries abstraction. Contains an array of queries. */
QueryPool *queries;
/* Array holding the id corresponding id to each query. */
Vector<uint> *ids;
/* cache on initialization */
uint (*buffer)[4];
/* buffer size (stores number of integers, for actual size multiply by sizeof integer)*/
@ -67,29 +67,23 @@ typedef struct GPUQueryState {
int scissor[4];
eGPUWriteMask write_mask;
eGPUDepthTest depth_test;
} GPUQueryState;
} GPUSelectQueryState;
static GPUQueryState g_query_state = {0};
static GPUSelectQueryState g_query_state = {0};
void gpu_select_query_begin(
uint (*buffer)[4], uint bufsize, const rcti *input, char mode, int oldhits)
{
g_query_state.query_issued = false;
g_query_state.active_query = 0;
g_query_state.num_of_queries = 0;
g_query_state.bufsize = bufsize;
g_query_state.buffer = buffer;
g_query_state.mode = mode;
g_query_state.index = 0;
g_query_state.oldhits = oldhits;
g_query_state.num_of_queries = ALLOC_QUERIES;
g_query_state.queries = MEM_mallocN(
g_query_state.num_of_queries * sizeof(*g_query_state.queries), "gpu selection queries");
g_query_state.id = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.id),
"gpu selection ids");
glGenQueries(g_query_state.num_of_queries, g_query_state.queries);
g_query_state.ids = new Vector<uint>();
g_query_state.queries = GPUBackend::get()->querypool_alloc();
g_query_state.queries->init(GPU_QUERY_OCCLUSION);
g_query_state.write_mask = GPU_write_mask_get();
g_query_state.depth_test = GPU_depth_test_get();
@ -133,21 +127,11 @@ void gpu_select_query_begin(
bool gpu_select_query_load_id(uint id)
{
if (g_query_state.query_issued) {
glEndQuery(GL_SAMPLES_PASSED);
}
/* if required, allocate extra queries */
if (g_query_state.active_query == g_query_state.num_of_queries) {
g_query_state.num_of_queries += ALLOC_QUERIES;
g_query_state.queries = MEM_reallocN(
g_query_state.queries, g_query_state.num_of_queries * sizeof(*g_query_state.queries));
g_query_state.id = MEM_reallocN(g_query_state.id,
g_query_state.num_of_queries * sizeof(*g_query_state.id));
glGenQueries(ALLOC_QUERIES, &g_query_state.queries[g_query_state.active_query]);
g_query_state.queries->end_query();
}
glBeginQuery(GL_SAMPLES_PASSED, g_query_state.queries[g_query_state.active_query]);
g_query_state.id[g_query_state.active_query] = id;
g_query_state.active_query++;
g_query_state.queries->begin_query();
g_query_state.ids->append(id);
g_query_state.query_issued = true;
if (g_query_state.mode == GPU_SELECT_NEAREST_SECOND_PASS) {
@ -158,39 +142,33 @@ bool gpu_select_query_load_id(uint id)
g_query_state.index++;
return true;
}
return false;
}
}
return true;
}
uint gpu_select_query_end(void)
{
int i;
uint hits = 0;
const uint maxhits = g_query_state.bufsize;
if (g_query_state.query_issued) {
glEndQuery(GL_SAMPLES_PASSED);
g_query_state.queries->end_query();
}
for (i = 0; i < g_query_state.active_query; i++) {
uint result = 0;
/* We are not using GL_QUERY_RESULT_AVAILABLE and sleep to wait for results,
* because it causes lagging on Windows/NVIDIA, see T61474. */
glGetQueryObjectuiv(g_query_state.queries[i], GL_QUERY_RESULT, &result);
if (result > 0) {
if (g_query_state.mode != GPU_SELECT_NEAREST_SECOND_PASS) {
Span<uint> ids = *g_query_state.ids;
Vector<uint32_t> result(ids.size());
g_query_state.queries->get_occlusion_result(result);
for (int i = 0; i < result.size(); i++) {
if (result[i] != 0) {
if (g_query_state.mode != GPU_SELECT_NEAREST_SECOND_PASS) {
if (hits < maxhits) {
g_query_state.buffer[hits][0] = 1;
g_query_state.buffer[hits][1] = 0xFFFF;
g_query_state.buffer[hits][2] = 0xFFFF;
g_query_state.buffer[hits][3] = g_query_state.id[i];
g_query_state.buffer[hits][3] = ids[i];
hits++;
}
else {
@ -202,7 +180,7 @@ uint gpu_select_query_end(void)
int j;
/* search in buffer and make selected object first */
for (j = 0; j < g_query_state.oldhits; j++) {
if (g_query_state.buffer[j][3] == g_query_state.id[i]) {
if (g_query_state.buffer[j][3] == ids[i]) {
g_query_state.buffer[j][1] = 0;
g_query_state.buffer[j][2] = 0;
}
@ -212,9 +190,8 @@ uint gpu_select_query_end(void)
}
}
glDeleteQueries(g_query_state.num_of_queries, g_query_state.queries);
MEM_freeN(g_query_state.queries);
MEM_freeN(g_query_state.id);
delete g_query_state.queries;
delete g_query_state.ids;
GPU_write_mask(g_query_state.write_mask);
GPU_depth_test(g_query_state.depth_test);

View File

@ -32,6 +32,7 @@
#include "gl_drawlist.hh"
#include "gl_framebuffer.hh"
#include "gl_index_buffer.hh"
#include "gl_query.hh"
#include "gl_shader.hh"
#include "gl_texture.hh"
#include "gl_uniform_buffer.hh"
@ -95,6 +96,11 @@ class GLBackend : public GPUBackend {
return new GLIndexBuf();
};
QueryPool *querypool_alloc(void) override
{
return new GLQueryPool();
};
Shader *shader_alloc(const char *name) override
{
return new GLShader(name);

View File

@ -68,13 +68,13 @@ class GLContext : public GPUContext {
static bool unused_fb_slot_workaround;
static float derivative_signs[2];
/** VBO for missing vertex attrib binding. Avoid undefined behavior on some implementation. */
GLuint default_attr_vbo_;
/** Used for debugging purpose. Bitflags of all bound slots. */
uint16_t bound_ubo_slots;
/* TODO(fclem) these needs to become private. */
public:
/** VBO for missing vertex attrib binding. Avoid undefined behavior on some implementation. */
GLuint default_attr_vbo_;
private:
/**
* GPUBatch & GPUFramebuffer have references to the context they are from, in the case the
* context is destroyed, we need to remove any reference to it.
@ -112,12 +112,12 @@ class GLContext : public GPUContext {
static void buf_free(GLuint buf_id);
static void tex_free(GLuint tex_id);
/* TODO(fclem) these needs to become private. */
public:
static void orphans_add(Vector<GLuint> &orphan_list, std::mutex &list_mutex, GLuint id);
void orphans_clear(void);
void vao_cache_register(GLVaoCache *cache);
void vao_cache_unregister(GLVaoCache *cache);
private:
static void orphans_add(Vector<GLuint> &orphan_list, std::mutex &list_mutex, GLuint id);
void orphans_clear(void);
};
} // namespace gpu

View File

@ -0,0 +1,78 @@
/*
* 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 "gl_query.hh"
namespace blender::gpu {
#define QUERY_CHUNCK_LEN 256
GLQueryPool::~GLQueryPool()
{
glDeleteQueries(query_ids_.size(), query_ids_.data());
}
void GLQueryPool::init(GPUQueryType type)
{
BLI_assert(initialized_ == false);
initialized_ = true;
type_ = type;
gl_type_ = to_gl(type);
query_issued_ = 0;
}
#if 0 /* TODO to avoid realloc of permanent query pool. */
void GLQueryPool::reset(GPUQueryType type)
{
initialized_ = false;
}
#endif
void GLQueryPool::begin_query(void)
{
/* TODO add assert about expected usage. */
while (query_issued_ >= query_ids_.size()) {
int64_t prev_size = query_ids_.size();
query_ids_.resize(prev_size + QUERY_CHUNCK_LEN);
glGenQueries(QUERY_CHUNCK_LEN, &query_ids_[prev_size]);
}
glBeginQuery(gl_type_, query_ids_[query_issued_++]);
}
void GLQueryPool::end_query(void)
{
/* TODO add assert about expected usage. */
glEndQuery(gl_type_);
}
void GLQueryPool::get_occlusion_result(MutableSpan<uint32_t> r_values)
{
BLI_assert(r_values.size() == query_issued_);
for (int i = 0; i < query_issued_; i++) {
/* Note: This is a sync point. */
glGetQueryObjectuiv(query_ids_[i], GL_QUERY_RESULT, &r_values[i]);
}
}
} // namespace blender::gpu

View File

@ -0,0 +1,69 @@
/*
* 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
*/
#pragma once
#include "BLI_vector.hh"
#include "gpu_query.hh"
#include "glew-mx.h"
namespace blender::gpu {
class GLQueryPool : public QueryPool {
private:
/** Contains queries object handles. */
Vector<GLuint> query_ids_;
/** Type of this query pool. */
GPUQueryType type_;
/** Associated GL type. */
GLenum gl_type_;
/** Number of queries that have been issued since last initialization.
* Should be equal to query_ids_.size(). */
uint32_t query_issued_;
/** Can only be initialized once. */
bool initialized_ = false;
public:
~GLQueryPool();
void init(GPUQueryType type) override;
void begin_query(void) override;
void end_query(void) override;
void get_occlusion_result(MutableSpan<uint32_t> r_values) override;
};
static inline GLenum to_gl(GPUQueryType type)
{
if (type == GPU_QUERY_OCCLUSION) {
/* TODO(fclem) try with GL_ANY_SAMPLES_PASSED. */
return GL_SAMPLES_PASSED;
}
BLI_assert(0);
return GL_SAMPLES_PASSED;
}
} // namespace blender::gpu