Was caused by some drawcall not being done if the volumetric resolve pass was drawn (using dual source blending seems to be the cause). Not sure why this is needed since it is still reset before the next drawcall.
1439 lines
42 KiB
C
1439 lines
42 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 2016, Blender Foundation.
|
|
*/
|
|
|
|
/** \file
|
|
* \ingroup draw
|
|
*/
|
|
|
|
#include "draw_manager.h"
|
|
|
|
#include "BLI_math.h"
|
|
#include "BLI_math_bits.h"
|
|
#include "BLI_memblock.h"
|
|
|
|
#include "BKE_global.h"
|
|
|
|
#include "GPU_extensions.h"
|
|
#include "intern/gpu_shader_private.h"
|
|
#include "intern/gpu_primitive_private.h"
|
|
|
|
#ifdef USE_GPU_SELECT
|
|
# include "GPU_select.h"
|
|
#endif
|
|
|
|
void DRW_select_load_id(uint id)
|
|
{
|
|
#ifdef USE_GPU_SELECT
|
|
BLI_assert(G.f & G_FLAG_PICKSEL);
|
|
DST.select_id = id;
|
|
#endif
|
|
}
|
|
|
|
#define DEBUG_UBO_BINDING
|
|
|
|
typedef struct DRWCommandsState {
|
|
GPUBatch *batch;
|
|
int resource_chunk;
|
|
int base_inst;
|
|
int inst_count;
|
|
int v_first;
|
|
int v_count;
|
|
bool neg_scale;
|
|
/* Resource location. */
|
|
int obmats_loc;
|
|
int obinfos_loc;
|
|
int baseinst_loc;
|
|
int chunkid_loc;
|
|
/* Legacy matrix support. */
|
|
int obmat_loc;
|
|
int obinv_loc;
|
|
int mvp_loc;
|
|
/* Selection ID state. */
|
|
GPUVertBuf *select_buf;
|
|
uint select_id;
|
|
/* Drawing State */
|
|
DRWState drw_state_enabled;
|
|
DRWState drw_state_disabled;
|
|
} DRWCommandsState;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Draw State (DRW_state)
|
|
* \{ */
|
|
|
|
void drw_state_set(DRWState state)
|
|
{
|
|
if (DST.state == state) {
|
|
return;
|
|
}
|
|
|
|
#define CHANGED_TO(f) \
|
|
((DST.state_lock & (f)) ? \
|
|
0 : \
|
|
(((DST.state & (f)) ? ((state & (f)) ? 0 : -1) : ((state & (f)) ? 1 : 0))))
|
|
|
|
#define CHANGED_ANY(f) (((DST.state & (f)) != (state & (f))) && ((DST.state_lock & (f)) == 0))
|
|
|
|
#define CHANGED_ANY_STORE_VAR(f, enabled) \
|
|
(((DST.state & (f)) != (enabled = (state & (f)))) && (((DST.state_lock & (f)) == 0)))
|
|
|
|
/* Depth Write */
|
|
{
|
|
int test;
|
|
if ((test = CHANGED_TO(DRW_STATE_WRITE_DEPTH))) {
|
|
if (test == 1) {
|
|
glDepthMask(GL_TRUE);
|
|
}
|
|
else {
|
|
glDepthMask(GL_FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Stencil Write */
|
|
{
|
|
DRWState test;
|
|
if (CHANGED_ANY_STORE_VAR(DRW_STATE_WRITE_STENCIL_ENABLED, test)) {
|
|
/* Stencil Write */
|
|
if (test) {
|
|
glStencilMask(0xFF);
|
|
if (test & DRW_STATE_WRITE_STENCIL) {
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
|
}
|
|
else if (test & DRW_STATE_WRITE_STENCIL_SHADOW_PASS) {
|
|
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
|
|
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
|
|
}
|
|
else if (test & DRW_STATE_WRITE_STENCIL_SHADOW_FAIL) {
|
|
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_KEEP);
|
|
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_KEEP);
|
|
}
|
|
else {
|
|
BLI_assert(0);
|
|
}
|
|
}
|
|
else {
|
|
glStencilMask(0x00);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Color Write */
|
|
{
|
|
int test;
|
|
if ((test = CHANGED_TO(DRW_STATE_WRITE_COLOR))) {
|
|
if (test == 1) {
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
}
|
|
else {
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Raster Discard */
|
|
{
|
|
if (CHANGED_ANY(DRW_STATE_RASTERIZER_ENABLED)) {
|
|
if ((state & DRW_STATE_RASTERIZER_ENABLED) != 0) {
|
|
glDisable(GL_RASTERIZER_DISCARD);
|
|
}
|
|
else {
|
|
glEnable(GL_RASTERIZER_DISCARD);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Cull */
|
|
{
|
|
DRWState test;
|
|
if (CHANGED_ANY_STORE_VAR(DRW_STATE_CULL_BACK | DRW_STATE_CULL_FRONT, test)) {
|
|
if (test) {
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
if ((state & DRW_STATE_CULL_BACK) != 0) {
|
|
glCullFace(GL_BACK);
|
|
}
|
|
else if ((state & DRW_STATE_CULL_FRONT) != 0) {
|
|
glCullFace(GL_FRONT);
|
|
}
|
|
else {
|
|
BLI_assert(0);
|
|
}
|
|
}
|
|
else {
|
|
glDisable(GL_CULL_FACE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Depth Test */
|
|
{
|
|
DRWState test;
|
|
if (CHANGED_ANY_STORE_VAR(DRW_STATE_DEPTH_TEST_ENABLED, test)) {
|
|
if (test) {
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
if (state & DRW_STATE_DEPTH_LESS) {
|
|
glDepthFunc(GL_LESS);
|
|
}
|
|
else if (state & DRW_STATE_DEPTH_LESS_EQUAL) {
|
|
glDepthFunc(GL_LEQUAL);
|
|
}
|
|
else if (state & DRW_STATE_DEPTH_EQUAL) {
|
|
glDepthFunc(GL_EQUAL);
|
|
}
|
|
else if (state & DRW_STATE_DEPTH_GREATER) {
|
|
glDepthFunc(GL_GREATER);
|
|
}
|
|
else if (state & DRW_STATE_DEPTH_GREATER_EQUAL) {
|
|
glDepthFunc(GL_GEQUAL);
|
|
}
|
|
else if (state & DRW_STATE_DEPTH_ALWAYS) {
|
|
glDepthFunc(GL_ALWAYS);
|
|
}
|
|
else {
|
|
BLI_assert(0);
|
|
}
|
|
}
|
|
else {
|
|
glDisable(GL_DEPTH_TEST);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Stencil Test */
|
|
{
|
|
int test;
|
|
if (CHANGED_ANY_STORE_VAR(DRW_STATE_STENCIL_TEST_ENABLED, test)) {
|
|
DST.stencil_mask = STENCIL_UNDEFINED;
|
|
if (test) {
|
|
glEnable(GL_STENCIL_TEST);
|
|
}
|
|
else {
|
|
glDisable(GL_STENCIL_TEST);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Wire Width */
|
|
{
|
|
int test;
|
|
if ((test = CHANGED_TO(DRW_STATE_WIRE_SMOOTH))) {
|
|
if (test == 1) {
|
|
GPU_line_width(2.0f);
|
|
GPU_line_smooth(true);
|
|
}
|
|
else {
|
|
GPU_line_width(1.0f);
|
|
GPU_line_smooth(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Blending (all buffer) */
|
|
{
|
|
int test;
|
|
if (CHANGED_ANY_STORE_VAR(DRW_STATE_BLEND_ALPHA | DRW_STATE_BLEND_ALPHA_PREMUL |
|
|
DRW_STATE_BLEND_ADD | DRW_STATE_BLEND_MUL |
|
|
DRW_STATE_BLEND_ADD_FULL | DRW_STATE_BLEND_OIT |
|
|
DRW_STATE_BLEND_ALPHA_UNDER_PREMUL | DRW_STATE_BLEND_CUSTOM,
|
|
test)) {
|
|
if (test) {
|
|
glEnable(GL_BLEND);
|
|
|
|
if ((state & DRW_STATE_BLEND_ALPHA) != 0) {
|
|
glBlendFuncSeparate(GL_SRC_ALPHA,
|
|
GL_ONE_MINUS_SRC_ALPHA, /* RGB */
|
|
GL_ONE,
|
|
GL_ONE_MINUS_SRC_ALPHA); /* Alpha */
|
|
}
|
|
else if ((state & DRW_STATE_BLEND_ALPHA_UNDER_PREMUL) != 0) {
|
|
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
|
|
}
|
|
else if ((state & DRW_STATE_BLEND_ALPHA_PREMUL) != 0) {
|
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
else if ((state & DRW_STATE_BLEND_MUL) != 0) {
|
|
glBlendFunc(GL_DST_COLOR, GL_ZERO);
|
|
}
|
|
else if ((state & DRW_STATE_BLEND_OIT) != 0) {
|
|
glBlendFuncSeparate(GL_ONE,
|
|
GL_ONE, /* RGB */
|
|
GL_ZERO,
|
|
GL_ONE_MINUS_SRC_ALPHA); /* Alpha */
|
|
}
|
|
else if ((state & DRW_STATE_BLEND_ADD) != 0) {
|
|
/* Do not let alpha accumulate but premult the source RGB by it. */
|
|
glBlendFuncSeparate(GL_SRC_ALPHA,
|
|
GL_ONE, /* RGB */
|
|
GL_ZERO,
|
|
GL_ONE); /* Alpha */
|
|
}
|
|
else if ((state & DRW_STATE_BLEND_ADD_FULL) != 0) {
|
|
/* Let alpha accumulate. */
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
}
|
|
else if ((state & DRW_STATE_BLEND_CUSTOM) != 0) {
|
|
/* Custom blend parameters using dual source blending.
|
|
* Can only be used with one Draw Buffer. */
|
|
glBlendFunc(GL_ONE, GL_SRC1_COLOR);
|
|
}
|
|
else {
|
|
BLI_assert(0);
|
|
}
|
|
}
|
|
else {
|
|
glDisable(GL_BLEND);
|
|
glBlendFunc(GL_ONE, GL_ONE); /* Don't multiply incoming color by alpha. */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Shadow Bias */
|
|
{
|
|
int test;
|
|
if ((test = CHANGED_TO(DRW_STATE_SHADOW_OFFSET))) {
|
|
if (test == 1) {
|
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
|
glEnable(GL_POLYGON_OFFSET_LINE);
|
|
/* 2.0 Seems to be the lowest possible slope bias that works in every case. */
|
|
glPolygonOffset(2.0f, 1.0f);
|
|
}
|
|
else {
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
glDisable(GL_POLYGON_OFFSET_LINE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Clip Planes */
|
|
{
|
|
int test;
|
|
if ((test = CHANGED_TO(DRW_STATE_CLIP_PLANES))) {
|
|
if (test == 1) {
|
|
for (int i = 0; i < DST.view_active->clip_planes_len; i++) {
|
|
glEnable(GL_CLIP_DISTANCE0 + i);
|
|
}
|
|
}
|
|
else {
|
|
for (int i = 0; i < MAX_CLIP_PLANES; i++) {
|
|
glDisable(GL_CLIP_DISTANCE0 + i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Program Points Size */
|
|
{
|
|
int test;
|
|
if ((test = CHANGED_TO(DRW_STATE_PROGRAM_POINT_SIZE))) {
|
|
if (test == 1) {
|
|
GPU_program_point_size(true);
|
|
}
|
|
else {
|
|
GPU_program_point_size(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Provoking Vertex */
|
|
{
|
|
int test;
|
|
if ((test = CHANGED_TO(DRW_STATE_FIRST_VERTEX_CONVENTION))) {
|
|
if (test == 1) {
|
|
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
|
|
}
|
|
else {
|
|
glProvokingVertex(GL_LAST_VERTEX_CONVENTION);
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef CHANGED_TO
|
|
#undef CHANGED_ANY
|
|
#undef CHANGED_ANY_STORE_VAR
|
|
|
|
DST.state = state;
|
|
}
|
|
|
|
static void drw_stencil_set(uint mask)
|
|
{
|
|
if (DST.stencil_mask != mask) {
|
|
DST.stencil_mask = mask;
|
|
if ((DST.state & DRW_STATE_STENCIL_ALWAYS) != 0) {
|
|
glStencilFunc(GL_ALWAYS, mask, 0xFF);
|
|
}
|
|
else if ((DST.state & DRW_STATE_STENCIL_EQUAL) != 0) {
|
|
glStencilFunc(GL_EQUAL, mask, 0xFF);
|
|
}
|
|
else if ((DST.state & DRW_STATE_STENCIL_NEQUAL) != 0) {
|
|
glStencilFunc(GL_NOTEQUAL, mask, 0xFF);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Reset state to not interfer with other UI drawcall */
|
|
void DRW_state_reset_ex(DRWState state)
|
|
{
|
|
DST.state = ~state;
|
|
drw_state_set(state);
|
|
}
|
|
|
|
static void drw_state_validate(void)
|
|
{
|
|
/* Cannot write to stencil buffer without stencil test. */
|
|
if ((DST.state & DRW_STATE_WRITE_STENCIL_ENABLED)) {
|
|
BLI_assert(DST.state & DRW_STATE_STENCIL_TEST_ENABLED);
|
|
}
|
|
/* Cannot write to depth buffer without depth test. */
|
|
if ((DST.state & DRW_STATE_WRITE_DEPTH)) {
|
|
BLI_assert(DST.state & DRW_STATE_DEPTH_TEST_ENABLED);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Use with care, intended so selection code can override passes depth settings,
|
|
* which is important for selection to work properly.
|
|
*
|
|
* Should be set in main draw loop, cleared afterwards
|
|
*/
|
|
void DRW_state_lock(DRWState state)
|
|
{
|
|
DST.state_lock = state;
|
|
}
|
|
|
|
void DRW_state_reset(void)
|
|
{
|
|
DRW_state_reset_ex(DRW_STATE_DEFAULT);
|
|
|
|
GPU_point_size(5);
|
|
|
|
/* Reset blending function */
|
|
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Culling (DRW_culling)
|
|
* \{ */
|
|
|
|
static bool draw_call_is_culled(const DRWResourceHandle *handle, DRWView *view)
|
|
{
|
|
DRWCullingState *culling = DRW_memblock_elem_from_handle(DST.vmempool->cullstates, handle);
|
|
return (culling->mask & view->culling_mask) != 0;
|
|
}
|
|
|
|
/* Set active view for rendering. */
|
|
void DRW_view_set_active(DRWView *view)
|
|
{
|
|
DST.view_active = (view) ? view : DST.view_default;
|
|
}
|
|
|
|
/* Return True if the given BoundSphere intersect the current view frustum */
|
|
static bool draw_culling_sphere_test(const BoundSphere *frustum_bsphere,
|
|
const float (*frustum_planes)[4],
|
|
const BoundSphere *bsphere)
|
|
{
|
|
/* Bypass test if radius is negative. */
|
|
if (bsphere->radius < 0.0f) {
|
|
return true;
|
|
}
|
|
|
|
/* Do a rough test first: Sphere VS Sphere intersect. */
|
|
float center_dist_sq = len_squared_v3v3(bsphere->center, frustum_bsphere->center);
|
|
float radius_sum = bsphere->radius + frustum_bsphere->radius;
|
|
if (center_dist_sq > SQUARE(radius_sum)) {
|
|
return false;
|
|
}
|
|
/* TODO we could test against the inscribed sphere of the frustum to early out positively. */
|
|
|
|
/* Test against the 6 frustum planes. */
|
|
/* TODO order planes with sides first then far then near clip. Should be better culling
|
|
* heuristic when sculpting. */
|
|
for (int p = 0; p < 6; p++) {
|
|
float dist = plane_point_side_v3(frustum_planes[p], bsphere->center);
|
|
if (dist < -bsphere->radius) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool draw_culling_box_test(const float (*frustum_planes)[4], const BoundBox *bbox)
|
|
{
|
|
/* 6 view frustum planes */
|
|
for (int p = 0; p < 6; p++) {
|
|
/* 8 box vertices. */
|
|
for (int v = 0; v < 8; v++) {
|
|
float dist = plane_point_side_v3(frustum_planes[p], bbox->vec[v]);
|
|
if (dist > 0.0f) {
|
|
/* At least one point in front of this plane.
|
|
* Go to next plane. */
|
|
break;
|
|
}
|
|
else if (v == 7) {
|
|
/* 8 points behind this plane. */
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool draw_culling_plane_test(const BoundBox *corners, const float plane[4])
|
|
{
|
|
/* Test against the 8 frustum corners. */
|
|
for (int c = 0; c < 8; c++) {
|
|
float dist = plane_point_side_v3(plane, corners->vec[c]);
|
|
if (dist < 0.0f) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Return True if the given BoundSphere intersect the current view frustum.
|
|
* bsphere must be in world space. */
|
|
bool DRW_culling_sphere_test(const DRWView *view, const BoundSphere *bsphere)
|
|
{
|
|
view = view ? view : DST.view_default;
|
|
return draw_culling_sphere_test(&view->frustum_bsphere, view->frustum_planes, bsphere);
|
|
}
|
|
|
|
/* Return True if the given BoundBox intersect the current view frustum.
|
|
* bbox must be in world space. */
|
|
bool DRW_culling_box_test(const DRWView *view, const BoundBox *bbox)
|
|
{
|
|
view = view ? view : DST.view_default;
|
|
return draw_culling_box_test(view->frustum_planes, bbox);
|
|
}
|
|
|
|
/* Return True if the view frustum is inside or intersect the given plane.
|
|
* plane must be in world space. */
|
|
bool DRW_culling_plane_test(const DRWView *view, const float plane[4])
|
|
{
|
|
view = view ? view : DST.view_default;
|
|
return draw_culling_plane_test(&view->frustum_corners, plane);
|
|
}
|
|
|
|
/* Return True if the given box intersect the current view frustum.
|
|
* This function will have to be replaced when world space bb per objects is implemented. */
|
|
bool DRW_culling_min_max_test(const DRWView *view, float obmat[4][4], float min[3], float max[3])
|
|
{
|
|
view = view ? view : DST.view_default;
|
|
float tobmat[4][4];
|
|
transpose_m4_m4(tobmat, obmat);
|
|
for (int i = 6; i--;) {
|
|
float frustum_plane_local[4], bb_near[3], bb_far[3];
|
|
mul_v4_m4v4(frustum_plane_local, tobmat, view->frustum_planes[i]);
|
|
aabb_get_near_far_from_plane(frustum_plane_local, min, max, bb_near, bb_far);
|
|
|
|
if (plane_point_side_v3(frustum_plane_local, bb_far) < 0.0f) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void DRW_culling_frustum_corners_get(const DRWView *view, BoundBox *corners)
|
|
{
|
|
view = view ? view : DST.view_default;
|
|
*corners = view->frustum_corners;
|
|
}
|
|
|
|
void DRW_culling_frustum_planes_get(const DRWView *view, float planes[6][4])
|
|
{
|
|
view = view ? view : DST.view_default;
|
|
memcpy(planes, view->frustum_planes, sizeof(float) * 6 * 4);
|
|
}
|
|
|
|
static void draw_compute_culling(DRWView *view)
|
|
{
|
|
view = view->parent ? view->parent : view;
|
|
|
|
/* TODO(fclem) multithread this. */
|
|
/* TODO(fclem) compute all dirty views at once. */
|
|
if (!view->is_dirty) {
|
|
return;
|
|
}
|
|
|
|
BLI_memblock_iter iter;
|
|
BLI_memblock_iternew(DST.vmempool->cullstates, &iter);
|
|
DRWCullingState *cull;
|
|
while ((cull = BLI_memblock_iterstep(&iter))) {
|
|
if (cull->bsphere.radius < 0.0) {
|
|
cull->mask = 0;
|
|
}
|
|
else {
|
|
bool culled = !draw_culling_sphere_test(
|
|
&view->frustum_bsphere, view->frustum_planes, &cull->bsphere);
|
|
|
|
#ifdef DRW_DEBUG_CULLING
|
|
if (G.debug_value != 0) {
|
|
if (culled) {
|
|
DRW_debug_sphere(
|
|
cull->bsphere.center, cull->bsphere.radius, (const float[4]){1, 0, 0, 1});
|
|
}
|
|
else {
|
|
DRW_debug_sphere(
|
|
cull->bsphere.center, cull->bsphere.radius, (const float[4]){0, 1, 0, 1});
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (view->visibility_fn) {
|
|
culled = !view->visibility_fn(!culled, cull->user_data);
|
|
}
|
|
|
|
SET_FLAG_FROM_TEST(cull->mask, culled, view->culling_mask);
|
|
}
|
|
}
|
|
|
|
view->is_dirty = false;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Draw (DRW_draw)
|
|
* \{ */
|
|
|
|
BLI_INLINE void draw_legacy_matrix_update(DRWShadingGroup *shgroup,
|
|
DRWResourceHandle *handle,
|
|
float obmat_loc,
|
|
float obinv_loc,
|
|
float mvp_loc)
|
|
{
|
|
/* Still supported for compatibility with gpu_shader_* but should be forbidden. */
|
|
DRWObjectMatrix *ob_mats = DRW_memblock_elem_from_handle(DST.vmempool->obmats, handle);
|
|
if (obmat_loc != -1) {
|
|
GPU_shader_uniform_vector(shgroup->shader, obmat_loc, 16, 1, (float *)ob_mats->model);
|
|
}
|
|
if (obinv_loc != -1) {
|
|
GPU_shader_uniform_vector(shgroup->shader, obinv_loc, 16, 1, (float *)ob_mats->modelinverse);
|
|
}
|
|
/* Still supported for compatibility with gpu_shader_* but should be forbidden
|
|
* and is slow (since it does not cache the result). */
|
|
if (mvp_loc != -1) {
|
|
float mvp[4][4];
|
|
mul_m4_m4m4(mvp, DST.view_active->storage.persmat, ob_mats->model);
|
|
GPU_shader_uniform_vector(shgroup->shader, mvp_loc, 16, 1, (float *)mvp);
|
|
}
|
|
}
|
|
|
|
BLI_INLINE void draw_geometry_bind(DRWShadingGroup *shgroup, GPUBatch *geom)
|
|
{
|
|
/* XXX hacking #GPUBatch. we don't want to call glUseProgram! (huge performance loss) */
|
|
if (DST.batch) {
|
|
DST.batch->program_in_use = false;
|
|
}
|
|
|
|
DST.batch = geom;
|
|
|
|
GPU_batch_program_set_no_use(
|
|
geom, GPU_shader_get_program(shgroup->shader), GPU_shader_get_interface(shgroup->shader));
|
|
|
|
geom->program_in_use = true; /* XXX hacking #GPUBatch */
|
|
|
|
GPU_batch_bind(geom);
|
|
}
|
|
|
|
BLI_INLINE void draw_geometry_execute(DRWShadingGroup *shgroup,
|
|
GPUBatch *geom,
|
|
int vert_first,
|
|
int vert_count,
|
|
int inst_first,
|
|
int inst_count,
|
|
int baseinst_loc)
|
|
{
|
|
/* inst_count can be -1. */
|
|
inst_count = max_ii(0, inst_count);
|
|
|
|
if (baseinst_loc != -1) {
|
|
/* Fallback when ARB_shader_draw_parameters is not supported. */
|
|
GPU_shader_uniform_vector_int(shgroup->shader, baseinst_loc, 1, 1, (int *)&inst_first);
|
|
/* Avoids VAO reconfiguration on older hardware. (see GPU_batch_draw_advanced) */
|
|
inst_first = 0;
|
|
}
|
|
|
|
/* bind vertex array */
|
|
if (DST.batch != geom) {
|
|
draw_geometry_bind(shgroup, geom);
|
|
}
|
|
|
|
GPU_batch_draw_advanced(geom, vert_first, vert_count, inst_first, inst_count);
|
|
}
|
|
|
|
BLI_INLINE void draw_indirect_call(DRWShadingGroup *shgroup, DRWCommandsState *state)
|
|
{
|
|
if (state->inst_count == 0) {
|
|
return;
|
|
}
|
|
if (state->baseinst_loc == -1) {
|
|
/* bind vertex array */
|
|
if (DST.batch != state->batch) {
|
|
GPU_draw_list_submit(DST.draw_list);
|
|
draw_geometry_bind(shgroup, state->batch);
|
|
}
|
|
GPU_draw_list_command_add(
|
|
DST.draw_list, state->v_first, state->v_count, state->base_inst, state->inst_count);
|
|
}
|
|
/* Fallback when unsupported */
|
|
else {
|
|
draw_geometry_execute(shgroup,
|
|
state->batch,
|
|
state->v_first,
|
|
state->v_count,
|
|
state->base_inst,
|
|
state->inst_count,
|
|
state->baseinst_loc);
|
|
}
|
|
}
|
|
|
|
enum {
|
|
BIND_NONE = 0,
|
|
BIND_TEMP = 1, /* Release slot after this shading group. */
|
|
BIND_PERSIST = 2, /* Release slot only after the next shader change. */
|
|
};
|
|
|
|
static void set_bound_flags(uint64_t *slots, uint64_t *persist_slots, int slot_idx, char bind_type)
|
|
{
|
|
uint64_t slot = 1llu << (unsigned long)slot_idx;
|
|
*slots |= slot;
|
|
if (bind_type == BIND_PERSIST) {
|
|
*persist_slots |= slot;
|
|
}
|
|
}
|
|
|
|
static int get_empty_slot_index(uint64_t slots)
|
|
{
|
|
uint64_t empty_slots = ~slots;
|
|
/* Find first empty slot using bitscan. */
|
|
if (empty_slots != 0) {
|
|
if ((empty_slots & 0xFFFFFFFFlu) != 0) {
|
|
return (int)bitscan_forward_uint(empty_slots);
|
|
}
|
|
else {
|
|
return (int)bitscan_forward_uint(empty_slots >> 32) + 32;
|
|
}
|
|
}
|
|
else {
|
|
/* Greater than GPU_max_textures() */
|
|
return 99999;
|
|
}
|
|
}
|
|
|
|
static void bind_texture(GPUTexture *tex, char bind_type)
|
|
{
|
|
int idx = GPU_texture_bound_number(tex);
|
|
if (idx == -1) {
|
|
/* Texture isn't bound yet. Find an empty slot and bind it. */
|
|
idx = get_empty_slot_index(DST.RST.bound_tex_slots);
|
|
|
|
if (idx < GPU_max_textures()) {
|
|
GPUTexture **gpu_tex_slot = &DST.RST.bound_texs[idx];
|
|
/* Unbind any previous texture. */
|
|
if (*gpu_tex_slot != NULL) {
|
|
GPU_texture_unbind(*gpu_tex_slot);
|
|
}
|
|
GPU_texture_bind(tex, idx);
|
|
*gpu_tex_slot = tex;
|
|
}
|
|
else {
|
|
printf("Not enough texture slots! Reduce number of textures used by your shader.\n");
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
/* This texture slot was released but the tex
|
|
* is still bound. Just flag the slot again. */
|
|
BLI_assert(DST.RST.bound_texs[idx] == tex);
|
|
}
|
|
set_bound_flags(&DST.RST.bound_tex_slots, &DST.RST.bound_tex_slots_persist, idx, bind_type);
|
|
}
|
|
|
|
static void bind_ubo(GPUUniformBuffer *ubo, char bind_type)
|
|
{
|
|
int idx = GPU_uniformbuffer_bindpoint(ubo);
|
|
if (idx == -1) {
|
|
/* UBO isn't bound yet. Find an empty slot and bind it. */
|
|
idx = get_empty_slot_index(DST.RST.bound_ubo_slots);
|
|
|
|
/* [0..1] are reserved ubo slots. */
|
|
idx += 2;
|
|
|
|
if (idx < GPU_max_ubo_binds()) {
|
|
GPUUniformBuffer **gpu_ubo_slot = &DST.RST.bound_ubos[idx];
|
|
/* Unbind any previous UBO. */
|
|
if (*gpu_ubo_slot != NULL) {
|
|
GPU_uniformbuffer_unbind(*gpu_ubo_slot);
|
|
}
|
|
GPU_uniformbuffer_bind(ubo, idx);
|
|
*gpu_ubo_slot = ubo;
|
|
}
|
|
else {
|
|
/* printf so user can report bad behavior */
|
|
printf("Not enough ubo slots! This should not happen!\n");
|
|
/* This is not depending on user input.
|
|
* It is our responsibility to make sure there is enough slots. */
|
|
BLI_assert(0);
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
BLI_assert(idx < 64);
|
|
/* This UBO slot was released but the UBO is
|
|
* still bound here. Just flag the slot again. */
|
|
BLI_assert(DST.RST.bound_ubos[idx] == ubo);
|
|
}
|
|
/* Remove offset for flag bitfield. */
|
|
idx -= 2;
|
|
set_bound_flags(&DST.RST.bound_ubo_slots, &DST.RST.bound_ubo_slots_persist, idx, bind_type);
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
/**
|
|
* Opengl specification is strict on buffer binding.
|
|
*
|
|
* " If any active uniform block is not backed by a
|
|
* sufficiently large buffer object, the results of shader
|
|
* execution are undefined, and may result in GL interruption or
|
|
* termination. " - Opengl 3.3 Core Specification
|
|
*
|
|
* For now we only check if the binding is correct. Not the size of
|
|
* the bound ubo.
|
|
*
|
|
* See T55475.
|
|
* */
|
|
static bool ubo_bindings_validate(DRWShadingGroup *shgroup)
|
|
{
|
|
bool valid = true;
|
|
# ifdef DEBUG_UBO_BINDING
|
|
/* Check that all active uniform blocks have a non-zero buffer bound. */
|
|
GLint program = 0;
|
|
GLint active_blocks = 0;
|
|
|
|
glGetIntegerv(GL_CURRENT_PROGRAM, &program);
|
|
glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &active_blocks);
|
|
|
|
for (uint i = 0; i < active_blocks; i++) {
|
|
int binding = 0;
|
|
int buffer = 0;
|
|
|
|
glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_BINDING, &binding);
|
|
glGetIntegeri_v(GL_UNIFORM_BUFFER_BINDING, binding, &buffer);
|
|
|
|
if (buffer == 0) {
|
|
char blockname[64];
|
|
glGetActiveUniformBlockName(program, i, sizeof(blockname), NULL, blockname);
|
|
|
|
if (valid) {
|
|
printf("Trying to draw with missing UBO binding.\n");
|
|
valid = false;
|
|
}
|
|
|
|
DRWPass *parent_pass = DRW_memblock_elem_from_handle(DST.vmempool->passes,
|
|
&shgroup->pass_handle);
|
|
|
|
printf("Pass : %s, Shader : %s, Block : %s\n",
|
|
parent_pass->name,
|
|
shgroup->shader->name,
|
|
blockname);
|
|
}
|
|
}
|
|
# endif
|
|
return valid;
|
|
}
|
|
#endif
|
|
|
|
static void release_texture_slots(bool with_persist)
|
|
{
|
|
if (with_persist) {
|
|
DST.RST.bound_tex_slots = 0;
|
|
DST.RST.bound_tex_slots_persist = 0;
|
|
}
|
|
else {
|
|
DST.RST.bound_tex_slots &= DST.RST.bound_tex_slots_persist;
|
|
}
|
|
}
|
|
|
|
static void release_ubo_slots(bool with_persist)
|
|
{
|
|
if (with_persist) {
|
|
DST.RST.bound_ubo_slots = 0;
|
|
DST.RST.bound_ubo_slots_persist = 0;
|
|
}
|
|
else {
|
|
DST.RST.bound_ubo_slots &= DST.RST.bound_ubo_slots_persist;
|
|
}
|
|
}
|
|
|
|
static void draw_update_uniforms(DRWShadingGroup *shgroup,
|
|
DRWCommandsState *state,
|
|
bool *use_tfeedback)
|
|
{
|
|
for (DRWUniformChunk *unichunk = shgroup->uniforms; unichunk; unichunk = unichunk->next) {
|
|
DRWUniform *uni = unichunk->uniforms;
|
|
for (int i = 0; i < unichunk->uniform_used; i++, uni++) {
|
|
GPUTexture *tex;
|
|
GPUUniformBuffer *ubo;
|
|
if (uni->location == -2) {
|
|
uni->location = GPU_shader_get_uniform_ensure(shgroup->shader,
|
|
DST.uniform_names.buffer + uni->name_ofs);
|
|
if (uni->location == -1) {
|
|
continue;
|
|
}
|
|
}
|
|
const void *data = uni->pvalue;
|
|
if (ELEM(uni->type, DRW_UNIFORM_INT_COPY, DRW_UNIFORM_FLOAT_COPY)) {
|
|
data = uni->fvalue;
|
|
}
|
|
switch (uni->type) {
|
|
case DRW_UNIFORM_INT_COPY:
|
|
case DRW_UNIFORM_INT:
|
|
GPU_shader_uniform_vector_int(
|
|
shgroup->shader, uni->location, uni->length, uni->arraysize, data);
|
|
break;
|
|
case DRW_UNIFORM_FLOAT_COPY:
|
|
case DRW_UNIFORM_FLOAT:
|
|
GPU_shader_uniform_vector(
|
|
shgroup->shader, uni->location, uni->length, uni->arraysize, data);
|
|
break;
|
|
case DRW_UNIFORM_TEXTURE:
|
|
tex = (GPUTexture *)uni->pvalue;
|
|
BLI_assert(tex);
|
|
bind_texture(tex, BIND_TEMP);
|
|
GPU_shader_uniform_texture(shgroup->shader, uni->location, tex);
|
|
break;
|
|
case DRW_UNIFORM_TEXTURE_PERSIST:
|
|
tex = (GPUTexture *)uni->pvalue;
|
|
BLI_assert(tex);
|
|
bind_texture(tex, BIND_PERSIST);
|
|
GPU_shader_uniform_texture(shgroup->shader, uni->location, tex);
|
|
break;
|
|
case DRW_UNIFORM_TEXTURE_REF:
|
|
tex = *((GPUTexture **)uni->pvalue);
|
|
BLI_assert(tex);
|
|
bind_texture(tex, BIND_TEMP);
|
|
GPU_shader_uniform_texture(shgroup->shader, uni->location, tex);
|
|
break;
|
|
case DRW_UNIFORM_BLOCK:
|
|
ubo = (GPUUniformBuffer *)uni->pvalue;
|
|
bind_ubo(ubo, BIND_TEMP);
|
|
GPU_shader_uniform_buffer(shgroup->shader, uni->location, ubo);
|
|
break;
|
|
case DRW_UNIFORM_BLOCK_PERSIST:
|
|
ubo = (GPUUniformBuffer *)uni->pvalue;
|
|
bind_ubo(ubo, BIND_PERSIST);
|
|
GPU_shader_uniform_buffer(shgroup->shader, uni->location, ubo);
|
|
break;
|
|
case DRW_UNIFORM_BLOCK_OBMATS:
|
|
state->obmats_loc = uni->location;
|
|
ubo = DST.vmempool->matrices_ubo[0];
|
|
GPU_uniformbuffer_bind(ubo, 0);
|
|
GPU_shader_uniform_buffer(shgroup->shader, uni->location, ubo);
|
|
break;
|
|
case DRW_UNIFORM_BLOCK_OBINFOS:
|
|
state->obinfos_loc = uni->location;
|
|
ubo = DST.vmempool->obinfos_ubo[0];
|
|
GPU_uniformbuffer_bind(ubo, 1);
|
|
GPU_shader_uniform_buffer(shgroup->shader, uni->location, ubo);
|
|
break;
|
|
case DRW_UNIFORM_RESOURCE_CHUNK:
|
|
state->chunkid_loc = uni->location;
|
|
GPU_shader_uniform_int(shgroup->shader, uni->location, 0);
|
|
break;
|
|
case DRW_UNIFORM_TFEEDBACK_TARGET:
|
|
BLI_assert(data && (*use_tfeedback == false));
|
|
*use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader,
|
|
((GPUVertBuf *)data)->vbo_id);
|
|
break;
|
|
/* Legacy/Fallback support. */
|
|
case DRW_UNIFORM_BASE_INSTANCE:
|
|
state->baseinst_loc = uni->location;
|
|
break;
|
|
case DRW_UNIFORM_MODEL_MATRIX:
|
|
state->obmat_loc = uni->location;
|
|
break;
|
|
case DRW_UNIFORM_MODEL_MATRIX_INVERSE:
|
|
state->obinv_loc = uni->location;
|
|
break;
|
|
case DRW_UNIFORM_MODELVIEWPROJECTION_MATRIX:
|
|
state->mvp_loc = uni->location;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
BLI_assert(ubo_bindings_validate(shgroup));
|
|
}
|
|
|
|
BLI_INLINE void draw_select_buffer(DRWShadingGroup *shgroup,
|
|
DRWCommandsState *state,
|
|
GPUBatch *batch,
|
|
const DRWResourceHandle *handle)
|
|
{
|
|
const bool is_instancing = (batch->inst != NULL);
|
|
int start = 0;
|
|
int count = 1;
|
|
int tot = is_instancing ? batch->inst->vertex_len : batch->verts[0]->vertex_len;
|
|
/* Hack : get "vbo" data without actually drawing. */
|
|
int *select_id = (void *)state->select_buf->data;
|
|
|
|
/* Batching */
|
|
if (!is_instancing) {
|
|
/* FIXME: Meh a bit nasty. */
|
|
if (batch->gl_prim_type == convert_prim_type_to_gl(GPU_PRIM_TRIS)) {
|
|
count = 3;
|
|
}
|
|
else if (batch->gl_prim_type == convert_prim_type_to_gl(GPU_PRIM_LINES)) {
|
|
count = 2;
|
|
}
|
|
}
|
|
|
|
while (start < tot) {
|
|
GPU_select_load_id(select_id[start]);
|
|
if (is_instancing) {
|
|
draw_geometry_execute(shgroup, batch, 0, 0, start, count, state->baseinst_loc);
|
|
}
|
|
else {
|
|
draw_geometry_execute(
|
|
shgroup, batch, start, count, DRW_handle_id_get(handle), 0, state->baseinst_loc);
|
|
}
|
|
start += count;
|
|
}
|
|
}
|
|
|
|
typedef struct DRWCommandIterator {
|
|
int cmd_index;
|
|
DRWCommandChunk *curr_chunk;
|
|
} DRWCommandIterator;
|
|
|
|
static void draw_command_iter_begin(DRWCommandIterator *iter, DRWShadingGroup *shgroup)
|
|
{
|
|
iter->curr_chunk = shgroup->cmd.first;
|
|
iter->cmd_index = 0;
|
|
}
|
|
|
|
static DRWCommand *draw_command_iter_step(DRWCommandIterator *iter, eDRWCommandType *cmd_type)
|
|
{
|
|
if (iter->curr_chunk) {
|
|
if (iter->cmd_index == iter->curr_chunk->command_len) {
|
|
iter->curr_chunk = iter->curr_chunk->next;
|
|
iter->cmd_index = 0;
|
|
}
|
|
if (iter->curr_chunk) {
|
|
*cmd_type = command_type_get(iter->curr_chunk->command_type, iter->cmd_index);
|
|
if (iter->cmd_index < iter->curr_chunk->command_used) {
|
|
return iter->curr_chunk->commands + iter->cmd_index++;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void draw_call_resource_bind(DRWCommandsState *state, const DRWResourceHandle *handle)
|
|
{
|
|
/* Front face is not a resource but it is inside the resource handle. */
|
|
bool neg_scale = DRW_handle_negative_scale_get(handle);
|
|
if (neg_scale != state->neg_scale) {
|
|
glFrontFace((neg_scale) ? GL_CW : GL_CCW);
|
|
state->neg_scale = neg_scale;
|
|
}
|
|
|
|
int chunk = DRW_handle_chunk_get(handle);
|
|
if (state->resource_chunk != chunk) {
|
|
if (state->chunkid_loc != -1) {
|
|
GPU_shader_uniform_int(NULL, state->chunkid_loc, chunk);
|
|
}
|
|
if (state->obmats_loc != -1) {
|
|
GPU_uniformbuffer_unbind(DST.vmempool->matrices_ubo[state->resource_chunk]);
|
|
GPU_uniformbuffer_bind(DST.vmempool->matrices_ubo[chunk], 0);
|
|
}
|
|
if (state->obinfos_loc != -1) {
|
|
GPU_uniformbuffer_unbind(DST.vmempool->obinfos_ubo[state->resource_chunk]);
|
|
GPU_uniformbuffer_bind(DST.vmempool->obinfos_ubo[chunk], 1);
|
|
}
|
|
state->resource_chunk = chunk;
|
|
}
|
|
}
|
|
|
|
static void draw_call_batching_flush(DRWShadingGroup *shgroup, DRWCommandsState *state)
|
|
{
|
|
draw_indirect_call(shgroup, state);
|
|
GPU_draw_list_submit(DST.draw_list);
|
|
|
|
state->batch = NULL;
|
|
state->inst_count = 0;
|
|
state->base_inst = -1;
|
|
}
|
|
|
|
static void draw_call_single_do(DRWShadingGroup *shgroup,
|
|
DRWCommandsState *state,
|
|
GPUBatch *batch,
|
|
DRWResourceHandle handle,
|
|
int vert_first,
|
|
int vert_count,
|
|
int inst_count)
|
|
{
|
|
draw_call_batching_flush(shgroup, state);
|
|
|
|
draw_call_resource_bind(state, &handle);
|
|
|
|
/* TODO This is Legacy. Need to be removed. */
|
|
if (state->obmats_loc == -1 &&
|
|
(state->obmat_loc != -1 || state->obinv_loc != -1 || state->mvp_loc != -1)) {
|
|
draw_legacy_matrix_update(
|
|
shgroup, &handle, state->obmat_loc, state->obinv_loc, state->mvp_loc);
|
|
}
|
|
|
|
if (G.f & G_FLAG_PICKSEL) {
|
|
if (state->select_buf != NULL) {
|
|
draw_select_buffer(shgroup, state, batch, &handle);
|
|
return;
|
|
}
|
|
else {
|
|
GPU_select_load_id(state->select_id);
|
|
}
|
|
}
|
|
|
|
draw_geometry_execute(shgroup,
|
|
batch,
|
|
vert_first,
|
|
vert_count,
|
|
DRW_handle_id_get(&handle),
|
|
inst_count,
|
|
state->baseinst_loc);
|
|
}
|
|
|
|
static void draw_call_batching_start(DRWCommandsState *state)
|
|
{
|
|
state->neg_scale = false;
|
|
state->resource_chunk = 0;
|
|
state->base_inst = 0;
|
|
state->inst_count = 0;
|
|
state->v_first = 0;
|
|
state->v_count = 0;
|
|
state->batch = NULL;
|
|
|
|
state->select_id = -1;
|
|
state->select_buf = NULL;
|
|
}
|
|
|
|
/* NOTE: Does not support batches with instancing VBOs. */
|
|
static void draw_call_batching_do(DRWShadingGroup *shgroup,
|
|
DRWCommandsState *state,
|
|
DRWCommandDraw *call)
|
|
{
|
|
/* If any condition requires to interupt the merging. */
|
|
bool neg_scale = DRW_handle_negative_scale_get(&call->handle);
|
|
int chunk = DRW_handle_chunk_get(&call->handle);
|
|
int id = DRW_handle_id_get(&call->handle);
|
|
if ((state->neg_scale != neg_scale) || /* Need to change state. */
|
|
(state->resource_chunk != chunk) || /* Need to change UBOs. */
|
|
(state->batch != call->batch) /* Need to change VAO. */
|
|
) {
|
|
draw_call_batching_flush(shgroup, state);
|
|
|
|
state->batch = call->batch;
|
|
state->v_first = (call->batch->elem) ? call->batch->elem->index_start : 0;
|
|
state->v_count = (call->batch->elem) ? call->batch->elem->index_len :
|
|
call->batch->verts[0]->vertex_len;
|
|
state->inst_count = 1;
|
|
state->base_inst = id;
|
|
|
|
draw_call_resource_bind(state, &call->handle);
|
|
|
|
GPU_draw_list_init(DST.draw_list, state->batch);
|
|
}
|
|
/* Is the id consecutive? */
|
|
else if (id != state->base_inst + state->inst_count) {
|
|
/* We need to add a draw command for the pending instances. */
|
|
draw_indirect_call(shgroup, state);
|
|
state->inst_count = 1;
|
|
state->base_inst = id;
|
|
}
|
|
/* We avoid a drawcall by merging with the precedent
|
|
* drawcall using instancing. */
|
|
else {
|
|
state->inst_count++;
|
|
}
|
|
}
|
|
|
|
/* Flush remaining pending drawcalls. */
|
|
static void draw_call_batching_finish(DRWShadingGroup *shgroup, DRWCommandsState *state)
|
|
{
|
|
draw_call_batching_flush(shgroup, state);
|
|
|
|
/* Reset state */
|
|
if (state->neg_scale) {
|
|
glFrontFace(GL_CCW);
|
|
}
|
|
if (state->obmats_loc != -1) {
|
|
GPU_uniformbuffer_unbind(DST.vmempool->matrices_ubo[state->resource_chunk]);
|
|
}
|
|
if (state->obinfos_loc != -1) {
|
|
GPU_uniformbuffer_unbind(DST.vmempool->obinfos_ubo[state->resource_chunk]);
|
|
}
|
|
}
|
|
|
|
static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
|
|
{
|
|
BLI_assert(shgroup->shader);
|
|
|
|
DRWCommandsState state = {
|
|
.obmats_loc = -1,
|
|
.obinfos_loc = -1,
|
|
.baseinst_loc = -1,
|
|
.chunkid_loc = -1,
|
|
.obmat_loc = -1,
|
|
.obinv_loc = -1,
|
|
.mvp_loc = -1,
|
|
.drw_state_enabled = 0,
|
|
.drw_state_disabled = 0,
|
|
};
|
|
|
|
const bool shader_changed = (DST.shader != shgroup->shader);
|
|
bool use_tfeedback = false;
|
|
|
|
if (shader_changed) {
|
|
if (DST.shader) {
|
|
GPU_shader_unbind();
|
|
}
|
|
GPU_shader_bind(shgroup->shader);
|
|
DST.shader = shgroup->shader;
|
|
/* XXX hacking gawain */
|
|
if (DST.batch) {
|
|
DST.batch->program_in_use = false;
|
|
}
|
|
DST.batch = NULL;
|
|
}
|
|
|
|
release_ubo_slots(shader_changed);
|
|
release_texture_slots(shader_changed);
|
|
|
|
draw_update_uniforms(shgroup, &state, &use_tfeedback);
|
|
|
|
drw_state_set(pass_state);
|
|
|
|
/* Rendering Calls */
|
|
{
|
|
DRWCommandIterator iter;
|
|
DRWCommand *cmd;
|
|
eDRWCommandType cmd_type;
|
|
|
|
draw_command_iter_begin(&iter, shgroup);
|
|
|
|
draw_call_batching_start(&state);
|
|
|
|
while ((cmd = draw_command_iter_step(&iter, &cmd_type))) {
|
|
|
|
switch (cmd_type) {
|
|
case DRW_CMD_DRWSTATE:
|
|
case DRW_CMD_STENCIL:
|
|
draw_call_batching_flush(shgroup, &state);
|
|
break;
|
|
case DRW_CMD_DRAW:
|
|
case DRW_CMD_DRAW_PROCEDURAL:
|
|
case DRW_CMD_DRAW_INSTANCE:
|
|
if (draw_call_is_culled(&cmd->instance.handle, DST.view_active)) {
|
|
continue;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (cmd_type) {
|
|
case DRW_CMD_CLEAR:
|
|
GPU_framebuffer_clear(
|
|
#ifndef NDEBUG
|
|
GPU_framebuffer_active_get(),
|
|
#else
|
|
NULL,
|
|
#endif
|
|
cmd->clear.clear_channels,
|
|
(float[4]){cmd->clear.r / 255.0f,
|
|
cmd->clear.g / 255.0f,
|
|
cmd->clear.b / 255.0f,
|
|
cmd->clear.a / 255.0f},
|
|
cmd->clear.depth,
|
|
cmd->clear.stencil);
|
|
break;
|
|
case DRW_CMD_DRWSTATE:
|
|
state.drw_state_enabled |= cmd->state.enable;
|
|
state.drw_state_disabled |= cmd->state.disable;
|
|
drw_state_set((pass_state & ~state.drw_state_disabled) | state.drw_state_enabled);
|
|
break;
|
|
case DRW_CMD_STENCIL:
|
|
drw_stencil_set(cmd->stencil.mask);
|
|
break;
|
|
case DRW_CMD_SELECTID:
|
|
state.select_id = cmd->select_id.select_id;
|
|
state.select_buf = cmd->select_id.select_buf;
|
|
break;
|
|
case DRW_CMD_DRAW:
|
|
if (!USE_BATCHING || state.obmats_loc == -1 || (G.f & G_FLAG_PICKSEL) ||
|
|
cmd->draw.batch->inst) {
|
|
draw_call_single_do(shgroup, &state, cmd->draw.batch, cmd->draw.handle, 0, 0, 0);
|
|
}
|
|
else {
|
|
draw_call_batching_do(shgroup, &state, &cmd->draw);
|
|
}
|
|
break;
|
|
case DRW_CMD_DRAW_PROCEDURAL:
|
|
draw_call_single_do(shgroup,
|
|
&state,
|
|
cmd->procedural.batch,
|
|
cmd->procedural.handle,
|
|
0,
|
|
cmd->procedural.vert_count,
|
|
1);
|
|
break;
|
|
case DRW_CMD_DRAW_INSTANCE:
|
|
draw_call_single_do(shgroup,
|
|
&state,
|
|
cmd->instance.batch,
|
|
cmd->instance.handle,
|
|
0,
|
|
0,
|
|
cmd->instance.inst_count);
|
|
break;
|
|
case DRW_CMD_DRAW_RANGE:
|
|
draw_call_single_do(shgroup,
|
|
&state,
|
|
cmd->range.batch,
|
|
(DRWResourceHandle)0,
|
|
cmd->range.vert_first,
|
|
cmd->range.vert_count,
|
|
1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
draw_call_batching_finish(shgroup, &state);
|
|
}
|
|
|
|
if (use_tfeedback) {
|
|
GPU_shader_transform_feedback_disable(shgroup->shader);
|
|
}
|
|
}
|
|
|
|
static void drw_update_view(void)
|
|
{
|
|
/* TODO(fclem) update a big UBO and only bind ranges here. */
|
|
DRW_uniformbuffer_update(G_draw.view_ubo, &DST.view_active->storage);
|
|
|
|
/* TODO get rid of this. */
|
|
DST.view_storage_cpy = DST.view_active->storage;
|
|
|
|
draw_compute_culling(DST.view_active);
|
|
}
|
|
|
|
static void drw_draw_pass_ex(DRWPass *pass,
|
|
DRWShadingGroup *start_group,
|
|
DRWShadingGroup *end_group)
|
|
{
|
|
if (start_group == NULL) {
|
|
return;
|
|
}
|
|
|
|
DST.shader = NULL;
|
|
|
|
BLI_assert(DST.buffer_finish_called &&
|
|
"DRW_render_instance_buffer_finish had not been called before drawing");
|
|
|
|
if (DST.view_previous != DST.view_active || DST.view_active->is_dirty) {
|
|
drw_update_view();
|
|
DST.view_active->is_dirty = false;
|
|
DST.view_previous = DST.view_active;
|
|
}
|
|
|
|
/* GPU_framebuffer_clear calls can change the state outside the DRW module.
|
|
* Force reset the affected states to avoid problems later. */
|
|
drw_state_set(DST.state | DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR);
|
|
|
|
drw_state_set(pass->state);
|
|
drw_state_validate();
|
|
|
|
DRW_stats_query_start(pass->name);
|
|
|
|
for (DRWShadingGroup *shgroup = start_group; shgroup; shgroup = shgroup->next) {
|
|
draw_shgroup(shgroup, pass->state);
|
|
/* break if upper limit */
|
|
if (shgroup == end_group) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Clear Bound textures */
|
|
for (int i = 0; i < DST_MAX_SLOTS; i++) {
|
|
if (DST.RST.bound_texs[i] != NULL) {
|
|
GPU_texture_unbind(DST.RST.bound_texs[i]);
|
|
DST.RST.bound_texs[i] = NULL;
|
|
}
|
|
}
|
|
|
|
/* Clear Bound Ubos */
|
|
for (int i = 0; i < DST_MAX_SLOTS; i++) {
|
|
if (DST.RST.bound_ubos[i] != NULL) {
|
|
GPU_uniformbuffer_unbind(DST.RST.bound_ubos[i]);
|
|
DST.RST.bound_ubos[i] = NULL;
|
|
}
|
|
}
|
|
|
|
if (DST.shader) {
|
|
GPU_shader_unbind();
|
|
DST.shader = NULL;
|
|
}
|
|
|
|
if (DST.batch) {
|
|
DST.batch->program_in_use = false;
|
|
DST.batch = NULL;
|
|
}
|
|
|
|
/* Fix T67342 for some reason. AMD Pro driver bug. */
|
|
if ((DST.state & DRW_STATE_BLEND_CUSTOM) != 0 &&
|
|
GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL)) {
|
|
drw_state_set(DST.state & ~DRW_STATE_BLEND_CUSTOM);
|
|
}
|
|
|
|
/* HACK: Rasterized discard can affect clear commands which are not
|
|
* part of a DRWPass (as of now). So disable rasterized discard here
|
|
* if it has been enabled. */
|
|
if ((DST.state & DRW_STATE_RASTERIZER_ENABLED) == 0) {
|
|
drw_state_set((DST.state & ~DRW_STATE_RASTERIZER_ENABLED) | DRW_STATE_DEFAULT);
|
|
}
|
|
|
|
DRW_stats_query_end();
|
|
}
|
|
|
|
void DRW_draw_pass(DRWPass *pass)
|
|
{
|
|
drw_draw_pass_ex(pass, pass->shgroups.first, pass->shgroups.last);
|
|
}
|
|
|
|
/* Draw only a subset of shgroups. Used in special situations as grease pencil strokes */
|
|
void DRW_draw_pass_subset(DRWPass *pass, DRWShadingGroup *start_group, DRWShadingGroup *end_group)
|
|
{
|
|
drw_draw_pass_ex(pass, start_group, end_group);
|
|
}
|
|
|
|
/** \} */
|