Changeset View
Changeset View
Standalone View
Standalone View
source/blender/gpu/intern/gpu_select.c
| Show All 23 Lines | |||||
| */ | */ | ||||
| /** \file blender/gpu/intern/gpu_select.c | /** \file blender/gpu/intern/gpu_select.c | ||||
| * \ingroup gpu | * \ingroup gpu | ||||
| * | * | ||||
| * Interface for accessing gpu-related methods for selection. The semantics will be | * Interface for accessing gpu-related methods for selection. The semantics will be | ||||
| * similar to glRenderMode(GL_SELECT) since the goal is to maintain compatibility. | * similar to glRenderMode(GL_SELECT) since the goal is to maintain compatibility. | ||||
| */ | */ | ||||
| #include <string.h> | |||||
| #include <stdlib.h> | |||||
| #include "GPU_select.h" | #include "GPU_select.h" | ||||
| #include "GPU_extensions.h" | #include "GPU_extensions.h" | ||||
| #include "GPU_glew.h" | #include "GPU_glew.h" | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "DNA_userdef_types.h" | #include "DNA_userdef_types.h" | ||||
| #include "BLI_rect.h" | |||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| /* Ad hoc number of queries to allocate to skip doing many glGenQueries */ | /* Ad hoc number of queries to allocate to skip doing many glGenQueries */ | ||||
| #define ALLOC_QUERIES 200 | #define ALLOC_QUERIES 200 | ||||
| typedef struct DepthID { | |||||
| unsigned int id; | |||||
| float depth; | |||||
| } DepthID; | |||||
| static int id_depth_cmp(const void *v1, const void *v2) | |||||
| { | |||||
| const DepthID *d1 = v1, *d2 = v2; | |||||
| if (d1->id < d2->id) { | |||||
| return -1; | |||||
| } | |||||
| else if (d1->id > d2->id) { | |||||
| return 1; | |||||
| } | |||||
| else { | |||||
| if (d1->depth < d2->depth) { | |||||
| return -1; | |||||
| } | |||||
| else if (d1->depth > d2->depth) { | |||||
| return 1; | |||||
| } | |||||
| else { | |||||
| return 0; | |||||
| } | |||||
| } | |||||
| } | |||||
| static int depth_cmp(const void *v1, const void *v2) | |||||
| { | |||||
| const DepthID *d1 = v1, *d2 = v2; | |||||
| if (d1->depth < d2->depth) { | |||||
| return -1; | |||||
| } | |||||
| else if (d1->depth > d2->depth) { | |||||
| return 1; | |||||
| } | |||||
| else { | |||||
| return 0; | |||||
| } | |||||
| } | |||||
| /* depth sorting */ | |||||
| typedef struct GPUQueryStateDepth { | |||||
| unsigned int rect_len; | |||||
| unsigned int *rect_id; | |||||
| unsigned int *rect_depth; | |||||
| /* scratch buffer, avoid allocs every time */ | |||||
| unsigned int *rect_depth_test; | |||||
| rcti clip_rect; | |||||
| /* Pass to glReadPixels (x,y,w,h */ | |||||
| int clip_readpixels[4]; | |||||
| unsigned int prev_id; | |||||
| bool is_init; | |||||
| } GPUQueryStateDepth; | |||||
| typedef struct GPUQueryState { | typedef struct GPUQueryState { | ||||
| GPUQueryStateDepth depth; | |||||
| /* To ignore selection id calls when not initialized */ | /* To ignore selection id calls when not initialized */ | ||||
| bool select_is_active; | bool select_is_active; | ||||
| /* Tracks whether a query has been issued so that gpu_load_id can end the previous one */ | /* Tracks whether a query has been issued so that gpu_load_id can end the previous one */ | ||||
| bool query_issued; | bool query_issued; | ||||
| /* array holding the OpenGL query identifiers */ | /* array holding the OpenGL query identifiers */ | ||||
| unsigned int *queries; | unsigned int *queries; | ||||
| /* array holding the id corresponding to each query */ | /* array holding the id corresponding to each query */ | ||||
| unsigned int *id; | unsigned int *id; | ||||
| /* number of queries in *queries and *id */ | /* number of queries in *queries and *id */ | ||||
| unsigned int num_of_queries; | unsigned int num_of_queries; | ||||
| /* index to the next query to start */ | /* index to the next query to start */ | ||||
| unsigned int active_query; | unsigned int active_query; | ||||
| /* flag to cache user preference for occlusion based selection */ | /* flag to cache user preference for occlusion based selection */ | ||||
| bool use_gpu_select; | bool use_gpu_select; | ||||
| /* experimental depth buffer picking */ | |||||
| bool use_gpu_select_depth; | |||||
| /* cache on initialization */ | /* cache on initialization */ | ||||
| unsigned int *buffer; | unsigned int *buffer; | ||||
| /* buffer size (stores number of integers, for actual size multiply by sizeof integer)*/ | /* buffer size (stores number of integers, for actual size multiply by sizeof integer)*/ | ||||
| unsigned int bufsize; | unsigned int bufsize; | ||||
| /* mode of operation */ | /* mode of operation */ | ||||
| char mode; | char mode; | ||||
| unsigned int index; | unsigned int index; | ||||
| int oldhits; | int oldhits; | ||||
| Show All 12 Lines | void GPU_select_begin(unsigned int *buffer, unsigned int bufsize, const rctf *input, char mode, int oldhits) | ||||
| g_query_state.use_gpu_select = GPU_select_query_check_active(); | g_query_state.use_gpu_select = GPU_select_query_check_active(); | ||||
| g_query_state.num_of_queries = 0; | g_query_state.num_of_queries = 0; | ||||
| g_query_state.bufsize = bufsize; | g_query_state.bufsize = bufsize; | ||||
| g_query_state.buffer = buffer; | g_query_state.buffer = buffer; | ||||
| g_query_state.mode = mode; | g_query_state.mode = mode; | ||||
| g_query_state.index = 0; | g_query_state.index = 0; | ||||
| g_query_state.oldhits = oldhits; | g_query_state.oldhits = oldhits; | ||||
| if (!g_query_state.use_gpu_select) { | if (!g_query_state.use_gpu_select) { | ||||
| glSelectBuffer(bufsize, (GLuint *)buffer); | glSelectBuffer(bufsize, (GLuint *)buffer); | ||||
| glRenderMode(GL_SELECT); | glRenderMode(GL_SELECT); | ||||
| glInitNames(); | glInitNames(); | ||||
| glPushName(-1); | glPushName(-1); | ||||
| } | } | ||||
| else if (mode == GPU_SELECT_NEAREST_DEPTH_SORT) { | |||||
| float viewport[4]; | |||||
| glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_VIEWPORT_BIT); | |||||
merwin: glPush/PopAttrib are not part of core profile | |||||
| /* disable writing to the framebuffer */ | |||||
| glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); | |||||
| glClear(GL_DEPTH_BUFFER_BIT); | |||||
| glEnable(GL_DEPTH_TEST); | |||||
| glDepthMask(GL_TRUE); | |||||
| glDepthFunc(GL_LEQUAL); | |||||
| GPUQueryStateDepth *qsd = &g_query_state.depth; | |||||
| BLI_rcti_rctf_copy(&qsd->clip_rect, input); | |||||
| glGetFloatv(GL_SCISSOR_BOX, viewport); | |||||
| qsd->clip_readpixels[0] = viewport[0]; | |||||
| qsd->clip_readpixels[1] = viewport[1]; | |||||
| qsd->clip_readpixels[2] = BLI_rcti_size_x(&qsd->clip_rect); | |||||
| qsd->clip_readpixels[3] = BLI_rcti_size_y(&qsd->clip_rect); | |||||
| glViewport(viewport[0], viewport[1], qsd->clip_readpixels[2], qsd->clip_readpixels[3]); | |||||
| const unsigned int rect_len = BLI_rcti_size_x(&qsd->clip_rect) * BLI_rcti_size_y(&qsd->clip_rect); | |||||
| qsd->rect_len = rect_len; | |||||
| qsd->rect_id = MEM_mallocN(sizeof(unsigned int) * rect_len, __func__); | |||||
| memset(qsd->rect_id, 0xff, sizeof(unsigned int) * rect_len); | |||||
| qsd->rect_depth = MEM_callocN(sizeof(unsigned int) * rect_len, __func__); | |||||
| glReadPixels(UNPACK4(qsd->clip_readpixels), GL_DEPTH_COMPONENT, GL_FLOAT, qsd->rect_depth); | |||||
| /* scratch buffer (read new values here) */ | |||||
| qsd->rect_depth_test = MEM_callocN(sizeof(unsigned int) * rect_len, __func__); | |||||
| qsd->prev_id = 0; | |||||
| qsd->is_init = false; | |||||
| } | |||||
| else { | else { | ||||
| float viewport[4]; | float viewport[4]; | ||||
| g_query_state.num_of_queries = ALLOC_QUERIES; | 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.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"); | 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); | glGenQueries(g_query_state.num_of_queries, g_query_state.queries); | ||||
| Show All 40 Lines | |||||
| { | { | ||||
| /* if no selection mode active, ignore */ | /* if no selection mode active, ignore */ | ||||
| if (!g_query_state.select_is_active) | if (!g_query_state.select_is_active) | ||||
| return true; | return true; | ||||
| if (!g_query_state.use_gpu_select) { | if (!g_query_state.use_gpu_select) { | ||||
| glLoadName(id); | glLoadName(id); | ||||
| } | } | ||||
| else if (g_query_state.mode == GPU_SELECT_NEAREST_DEPTH_SORT) { | |||||
| GPUQueryStateDepth *qsd = &g_query_state.depth; | |||||
| if (qsd->is_init) { | |||||
| const unsigned int rect_len = qsd->rect_len; | |||||
| glReadPixels(UNPACK4(qsd->clip_readpixels), GL_DEPTH_COMPONENT, GL_FLOAT, qsd->rect_depth_test); | |||||
| /* perform initial memcmp since most cases the array remains unchanged */ | |||||
| if (memcmp(qsd->rect_depth, qsd->rect_depth_test, rect_len * sizeof(unsigned int)) != 0) { | |||||
| const unsigned int prev_id = qsd->prev_id; | |||||
| const unsigned int *prev = qsd->rect_depth; | |||||
| const unsigned int *curr = qsd->rect_depth_test; | |||||
| unsigned int *id_ptr = qsd->rect_id; | |||||
| for (unsigned int i = 0; i < rect_len; i++, curr++, prev++, id_ptr++) { | |||||
| if (*curr != *prev) { | |||||
| *id_ptr = prev_id; | |||||
| } | |||||
| } | |||||
| SWAP(unsigned int *, qsd->rect_depth_test, qsd->rect_depth); | |||||
| } | |||||
| } | |||||
| qsd->prev_id = id; | |||||
| qsd->is_init = true; | |||||
| } | |||||
| else { | else { | ||||
| if (g_query_state.query_issued) { | if (g_query_state.query_issued) { | ||||
| glEndQuery(GL_SAMPLES_PASSED); | glEndQuery(GL_SAMPLES_PASSED); | ||||
| } | } | ||||
| /* if required, allocate extra queries */ | /* if required, allocate extra queries */ | ||||
| if (g_query_state.active_query == g_query_state.num_of_queries) { | if (g_query_state.active_query == g_query_state.num_of_queries) { | ||||
| g_query_state.num_of_queries += ALLOC_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.queries = MEM_reallocN(g_query_state.queries, g_query_state.num_of_queries * sizeof(*g_query_state.queries)); | ||||
| Show All 27 Lines | |||||
| */ | */ | ||||
| unsigned int GPU_select_end(void) | unsigned int GPU_select_end(void) | ||||
| { | { | ||||
| unsigned int hits = 0; | unsigned int hits = 0; | ||||
| if (!g_query_state.use_gpu_select) { | if (!g_query_state.use_gpu_select) { | ||||
| glPopName(); | glPopName(); | ||||
| hits = glRenderMode(GL_RENDER); | hits = glRenderMode(GL_RENDER); | ||||
| } | } | ||||
| else if (g_query_state.mode == GPU_SELECT_NEAREST_DEPTH_SORT) { | |||||
| GPUQueryStateDepth *qsd = &g_query_state.depth; | |||||
| if (qsd->is_init) { | |||||
| /* force finishing last pass */ | |||||
| GPU_select_load_id(qsd->prev_id); | |||||
| } | |||||
| glPopAttrib(); | |||||
| glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); | |||||
| unsigned int maxhits = g_query_state.bufsize / 4; | |||||
| DepthID *depth = MEM_mallocN(qsd->rect_len * sizeof(*depth), __func__); | |||||
| /* unsigned int depth_last = 0; */ | |||||
| unsigned int depth_len = 0; | |||||
| for (unsigned int i = 0; i < qsd->rect_len; i++) { | |||||
| if (qsd->rect_id[i] != 0xffffffff) { | |||||
| DepthID *d = &depth[depth_len++]; | |||||
| d->id = qsd->rect_id[i]; | |||||
| d->depth = *((float *)&qsd->rect_depth[i]); | |||||
| } | |||||
| } | |||||
| qsort(depth, depth_len, sizeof(DepthID), id_depth_cmp); | |||||
| unsigned int id_last = 0xffffffff; | |||||
| unsigned int depth_compact_len = 0; | |||||
| for (unsigned int i = 0; i < depth_len; i++) { | |||||
| if (depth[i].id != id_last) { | |||||
| id_last = depth[i].id; | |||||
| depth[depth_compact_len++] = depth[i]; | |||||
| } | |||||
| } | |||||
| qsort(depth, depth_compact_len, sizeof(DepthID), depth_cmp); | |||||
| for (unsigned int i = 0; i < depth_compact_len; i++) { | |||||
| if (hits < maxhits) { | |||||
| g_query_state.buffer[hits * 4] = 1; | |||||
| g_query_state.buffer[hits * 4 + 1] = 0xFFFF; | |||||
| g_query_state.buffer[hits * 4 + 2] = 0xFFFF; | |||||
| g_query_state.buffer[hits * 4 + 3] = depth[i].id; | |||||
| hits++; | |||||
| } | |||||
| else { | |||||
| hits = -1; | |||||
| break; | |||||
| } | |||||
| } | |||||
| MEM_freeN(depth); | |||||
| MEM_freeN(qsd->rect_id); | |||||
| MEM_freeN(qsd->rect_depth); | |||||
| MEM_freeN(qsd->rect_depth_test); | |||||
| qsd->rect_id = NULL; | |||||
| qsd->rect_depth = NULL; | |||||
| qsd->rect_depth_test = NULL; | |||||
| } | |||||
| else { | else { | ||||
| int i; | int i; | ||||
| if (g_query_state.query_issued) { | if (g_query_state.query_issued) { | ||||
| glEndQuery(GL_SAMPLES_PASSED); | glEndQuery(GL_SAMPLES_PASSED); | ||||
| } | } | ||||
| for (i = 0; i < g_query_state.active_query; i++) { | for (i = 0; i < g_query_state.active_query; i++) { | ||||
| ▲ Show 20 Lines • Show All 57 Lines • Show Last 20 Lines | |||||
glPush/PopAttrib are not part of core profile