GPUShader/DRW: Add Transform Feedback support.
This is a usefull feature that can be used to do a lot of precomputation on the GPU instead of the CPU. Implementation is simple and only covers the most usefull case. How to use: - Create shader with transform feedback. - Create a pass with DRW_STATE_TRANS_FEEDBACK. - Create a target Gwn_VertBuf (make sure it's big enough). - Create a shading group with DRW_shgroup_transform_feedback_create(). - Add your draw calls to the shading group. - Render your pass normaly. Current limitation: - Only one output buffer. - Cannot pause/resume tfb rendering to interleave with normal drawcalls. - Cannot get the number of verts drawn.
This commit is contained in:
parent
a3f4c72ec9
commit
a25856f2a8
|
@ -47,6 +47,7 @@
|
|||
|
||||
#include "GPU_framebuffer.h"
|
||||
#include "GPU_texture.h"
|
||||
#include "GPU_shader.h"
|
||||
|
||||
#include "draw_common.h"
|
||||
#include "draw_cache.h"
|
||||
|
@ -229,6 +230,9 @@ struct GPUShader *DRW_shader_create(
|
|||
const char *vert, const char *geom, const char *frag, const char *defines);
|
||||
struct GPUShader *DRW_shader_create_with_lib(
|
||||
const char *vert, const char *geom, const char *frag, const char *lib, const char *defines);
|
||||
struct GPUShader *DRW_shader_create_with_transform_feedback(
|
||||
const char *vert, const char *geom, const char *defines,
|
||||
const GPUShaderTFBType prim_type, const char **varying_names, const int varying_count);
|
||||
struct GPUShader *DRW_shader_create_2D(const char *frag, const char *defines);
|
||||
struct GPUShader *DRW_shader_create_3D(const char *frag, const char *defines);
|
||||
struct GPUShader *DRW_shader_create_fullscreen(const char *frag, const char *defines);
|
||||
|
@ -274,6 +278,7 @@ typedef enum {
|
|||
DRW_STATE_ADDITIVE_FULL = (1 << 19), /* Same as DRW_STATE_ADDITIVE but let alpha accumulate without premult. */
|
||||
DRW_STATE_BLEND_PREMUL = (1 << 20), /* Use that if color is already premult by alpha. */
|
||||
DRW_STATE_WIRE_SMOOTH = (1 << 21),
|
||||
DRW_STATE_TRANS_FEEDBACK = (1 << 22),
|
||||
|
||||
DRW_STATE_WRITE_STENCIL = (1 << 27),
|
||||
DRW_STATE_WRITE_STENCIL_SHADOW = (1 << 28),
|
||||
|
@ -312,6 +317,7 @@ DRWShadingGroup *DRW_shgroup_instance_create(
|
|||
DRWShadingGroup *DRW_shgroup_point_batch_create(struct GPUShader *shader, DRWPass *pass);
|
||||
DRWShadingGroup *DRW_shgroup_line_batch_create(struct GPUShader *shader, DRWPass *pass);
|
||||
DRWShadingGroup *DRW_shgroup_empty_tri_batch_create(struct GPUShader *shader, DRWPass *pass, int size);
|
||||
DRWShadingGroup *DRW_shgroup_transform_feedback_create(struct GPUShader *shader, DRWPass *pass, struct Gwn_VertBuf *tf_target);
|
||||
|
||||
typedef void (DRWCallGenerateFn)(
|
||||
DRWShadingGroup *shgroup,
|
||||
|
|
|
@ -193,6 +193,7 @@ typedef enum {
|
|||
DRW_SHG_TRIANGLE_BATCH,
|
||||
DRW_SHG_INSTANCE,
|
||||
DRW_SHG_INSTANCE_EXTERNAL,
|
||||
DRW_SHG_FEEDBACK_TRANSFORM,
|
||||
} DRWShadingGroupType;
|
||||
|
||||
struct DRWShadingGroup {
|
||||
|
@ -206,6 +207,10 @@ struct DRWShadingGroup {
|
|||
struct { /* DRW_SHG_NORMAL */
|
||||
DRWCall *first, *last; /* Linked list of DRWCall or DRWCallDynamic depending of type */
|
||||
} calls;
|
||||
struct { /* DRW_SHG_FEEDBACK_TRANSFORM */
|
||||
DRWCall *first, *last; /* Linked list of DRWCall or DRWCallDynamic depending of type */
|
||||
struct Gwn_VertBuf *tfeedback_target; /* Transform Feedback target. */
|
||||
};
|
||||
struct { /* DRW_SHG_***_BATCH */
|
||||
struct Gwn_Batch *batch_geom; /* Result of call batching */
|
||||
struct Gwn_VertBuf *batch_vbo;
|
||||
|
|
|
@ -353,7 +353,7 @@ static DRWCallState *drw_call_state_object(DRWShadingGroup *shgroup, float (*obm
|
|||
void DRW_shgroup_call_add(DRWShadingGroup *shgroup, Gwn_Batch *geom, float (*obmat)[4])
|
||||
{
|
||||
BLI_assert(geom != NULL);
|
||||
BLI_assert(shgroup->type == DRW_SHG_NORMAL);
|
||||
BLI_assert(ELEM(shgroup->type, DRW_SHG_NORMAL, DRW_SHG_FEEDBACK_TRANSFORM));
|
||||
|
||||
DRWCall *call = BLI_mempool_alloc(DST.vmempool->calls);
|
||||
call->state = drw_call_state_create(shgroup, obmat, NULL);
|
||||
|
@ -828,6 +828,18 @@ DRWShadingGroup *DRW_shgroup_empty_tri_batch_create(struct GPUShader *shader, DR
|
|||
return shgroup;
|
||||
}
|
||||
|
||||
DRWShadingGroup *DRW_shgroup_transform_feedback_create(struct GPUShader *shader, DRWPass *pass, Gwn_VertBuf *tf_target)
|
||||
{
|
||||
DRWShadingGroup *shgroup = drw_shgroup_create_ex(shader, pass);
|
||||
shgroup->type = DRW_SHG_FEEDBACK_TRANSFORM;
|
||||
|
||||
drw_shgroup_init(shgroup, shader);
|
||||
|
||||
shgroup->tfeedback_target = tf_target;
|
||||
|
||||
return shgroup;
|
||||
}
|
||||
|
||||
/* Specify an external batch instead of adding each attrib one by one. */
|
||||
void DRW_shgroup_instance_batch(DRWShadingGroup *shgroup, struct Gwn_Batch *batch)
|
||||
{
|
||||
|
|
|
@ -102,6 +102,21 @@ void drw_state_set(DRWState state)
|
|||
}
|
||||
}
|
||||
|
||||
/* Raster Discard */
|
||||
{
|
||||
if (CHANGED_ANY(DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR |
|
||||
DRW_STATE_WRITE_STENCIL | DRW_STATE_WRITE_STENCIL_SHADOW))
|
||||
{
|
||||
if ((state & (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR |
|
||||
DRW_STATE_WRITE_STENCIL | DRW_STATE_WRITE_STENCIL_SHADOW)) != 0) {
|
||||
glDisable(GL_RASTERIZER_DISCARD);
|
||||
}
|
||||
else {
|
||||
glEnable(GL_RASTERIZER_DISCARD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Cull */
|
||||
{
|
||||
DRWState test;
|
||||
|
@ -900,6 +915,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
|
|||
int val;
|
||||
float fval;
|
||||
const bool shader_changed = (DST.shader != shgroup->shader);
|
||||
bool use_tfeedback = false;
|
||||
|
||||
if (shader_changed) {
|
||||
if (DST.shader) GPU_shader_unbind();
|
||||
|
@ -907,6 +923,13 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
|
|||
DST.shader = shgroup->shader;
|
||||
}
|
||||
|
||||
if ((pass_state & DRW_STATE_TRANS_FEEDBACK) != 0 &&
|
||||
(shgroup->type == DRW_SHG_FEEDBACK_TRANSFORM))
|
||||
{
|
||||
use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader,
|
||||
shgroup->tfeedback_target->vbo_id);
|
||||
}
|
||||
|
||||
release_ubo_slots(shader_changed);
|
||||
release_texture_slots(shader_changed);
|
||||
|
||||
|
@ -1023,7 +1046,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
|
|||
#endif
|
||||
|
||||
/* Rendering Calls */
|
||||
if (!ELEM(shgroup->type, DRW_SHG_NORMAL)) {
|
||||
if (!ELEM(shgroup->type, DRW_SHG_NORMAL, DRW_SHG_FEEDBACK_TRANSFORM)) {
|
||||
/* Replacing multiple calls with only one */
|
||||
if (ELEM(shgroup->type, DRW_SHG_INSTANCE, DRW_SHG_INSTANCE_EXTERNAL)) {
|
||||
if (shgroup->type == DRW_SHG_INSTANCE_EXTERNAL) {
|
||||
|
@ -1104,6 +1127,10 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
|
|||
glFrontFace(DST.frontface);
|
||||
}
|
||||
|
||||
if (use_tfeedback) {
|
||||
GPU_shader_transform_feedback_disable(shgroup->shader);
|
||||
}
|
||||
|
||||
/* TODO: remove, (currently causes alpha issue with sculpt, need to investigate) */
|
||||
DRW_state_reset();
|
||||
}
|
||||
|
|
|
@ -295,6 +295,14 @@ GPUShader *DRW_shader_create_with_lib(
|
|||
return sh;
|
||||
}
|
||||
|
||||
GPUShader *DRW_shader_create_with_transform_feedback(
|
||||
const char *vert, const char *geom, const char *defines,
|
||||
const GPUShaderTFBType prim_type, const char **varying_names, const int varying_count)
|
||||
{
|
||||
return GPU_shader_create_ex(vert, NULL, geom, NULL, defines, GPU_SHADER_FLAGS_NONE,
|
||||
prim_type, varying_names, varying_count);
|
||||
}
|
||||
|
||||
GPUShader *DRW_shader_create_2D(const char *frag, const char *defines)
|
||||
{
|
||||
return GPU_shader_create(datatoc_gpu_shader_2D_vert_glsl, frag, NULL, NULL, defines);
|
||||
|
|
|
@ -50,6 +50,13 @@ enum {
|
|||
GPU_SHADER_FLAGS_NEW_SHADING = (1 << 1),
|
||||
};
|
||||
|
||||
typedef enum GPUShaderTFBType {
|
||||
GPU_SHADER_TFB_NONE = 0, /* Transform feedback unsupported. */
|
||||
GPU_SHADER_TFB_POINTS = 1,
|
||||
GPU_SHADER_TFB_LINES = 2,
|
||||
GPU_SHADER_TFB_TRIANGLES = 3,
|
||||
} GPUShaderTFBType;
|
||||
|
||||
GPUShader *GPU_shader_create(
|
||||
const char *vertexcode,
|
||||
const char *fragcode,
|
||||
|
@ -62,12 +69,19 @@ GPUShader *GPU_shader_create_ex(
|
|||
const char *geocode,
|
||||
const char *libcode,
|
||||
const char *defines,
|
||||
const int flags);
|
||||
const int flags,
|
||||
const GPUShaderTFBType tf_type,
|
||||
const char **tf_names,
|
||||
const int tf_count);
|
||||
void GPU_shader_free(GPUShader *shader);
|
||||
|
||||
void GPU_shader_bind(GPUShader *shader);
|
||||
void GPU_shader_unbind(void);
|
||||
|
||||
/* Returns true if transform feedback was succesfully enabled. */
|
||||
bool GPU_shader_transform_feedback_enable(GPUShader *shader, unsigned int vbo_id);
|
||||
void GPU_shader_transform_feedback_disable(GPUShader *shader);
|
||||
|
||||
int GPU_shader_get_program(GPUShader *shader);
|
||||
|
||||
void *GPU_shader_get_interface(GPUShader *shader);
|
||||
|
|
|
@ -268,7 +268,10 @@ GPUShader *GPU_shader_create(const char *vertexcode,
|
|||
geocode,
|
||||
libcode,
|
||||
defines,
|
||||
GPU_SHADER_FLAGS_NONE);
|
||||
GPU_SHADER_FLAGS_NONE,
|
||||
GPU_SHADER_TFB_NONE,
|
||||
NULL,
|
||||
0);
|
||||
}
|
||||
|
||||
#define DEBUG_SHADER_NONE ""
|
||||
|
@ -323,7 +326,10 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode,
|
|||
const char *geocode,
|
||||
const char *libcode,
|
||||
const char *defines,
|
||||
const int flags)
|
||||
const int flags,
|
||||
const GPUShaderTFBType tf_type,
|
||||
const char **tf_names,
|
||||
const int tf_count)
|
||||
{
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
bool use_opensubdiv = (flags & GPU_SHADER_FLAGS_SPECIAL_OPENSUBDIV) != 0;
|
||||
|
@ -469,6 +475,13 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode,
|
|||
}
|
||||
#endif
|
||||
|
||||
if (tf_names != NULL) {
|
||||
glTransformFeedbackVaryings(shader->program, tf_count, tf_names, GL_INTERLEAVED_ATTRIBS);
|
||||
/* Primitive type must be setup */
|
||||
BLI_assert(tf_type != GPU_SHADER_TFB_NONE);
|
||||
shader->feedback_transform_type = tf_type;
|
||||
}
|
||||
|
||||
glLinkProgram(shader->program);
|
||||
glGetProgramiv(shader->program, GL_LINK_STATUS, &status);
|
||||
if (!status) {
|
||||
|
@ -527,6 +540,27 @@ void GPU_shader_unbind(void)
|
|||
glUseProgram(0);
|
||||
}
|
||||
|
||||
bool GPU_shader_transform_feedback_enable(GPUShader *shader, unsigned int vbo_id)
|
||||
{
|
||||
if (shader->feedback_transform_type == GPU_SHADER_TFB_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo_id);
|
||||
|
||||
switch (shader->feedback_transform_type) {
|
||||
case GPU_SHADER_TFB_POINTS: glBeginTransformFeedback(GL_POINTS); return true;
|
||||
case GPU_SHADER_TFB_LINES: glBeginTransformFeedback(GL_LINES); return true;
|
||||
case GPU_SHADER_TFB_TRIANGLES: glBeginTransformFeedback(GL_TRIANGLES); return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader))
|
||||
{
|
||||
glEndTransformFeedback();
|
||||
}
|
||||
|
||||
void GPU_shader_free(GPUShader *shader)
|
||||
{
|
||||
BLI_assert(shader);
|
||||
|
|
|
@ -36,6 +36,8 @@ struct GPUShader {
|
|||
GLuint fragment; /* handle for fragment shader */
|
||||
|
||||
Gwn_ShaderInterface *interface; /* cached uniform & attrib interface for shader */
|
||||
|
||||
int feedback_transform_type;
|
||||
};
|
||||
|
||||
#endif /* __GPU_SHADER_PRIVATE_H__ */
|
||||
|
|
Loading…
Reference in New Issue