From ef0a054bb3c893b47250d3da6e0eb6692fd41f6c Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Sun, 17 Jul 2011 16:47:25 +0200 Subject: [PATCH] GL_SELECT v5 : diff from v4: mark FBO region when drawn (avoid reading invalid hit value from unused names) For instance, Blender always init name staks with a first 'invalid' object name '-1', so the corresponding entry in the batch structure must not be read from. Should fix: https://bugs.freedesktop.org/show_bug.cgi?id=34495#c47 --- src/mesa/main/feedback.c | 246 +++++++++++++++++++++++++++---- src/mesa/main/mtypes.h | 18 ++- src/mesa/state_tracker/st_cb_feedback.c | 92 +++++++++++- 3 files changed, 320 insertions(+), 36 deletions(-) diff --git a/src/mesa/main/feedback.c b/src/mesa/main/feedback.c index 597ec1e..f102234 100644 --- a/src/mesa/main/feedback.c +++ b/src/mesa/main/feedback.c @@ -48,7 +48,47 @@ #define FB_COLOR 0x04 #define FB_TEXTURE 0X08 +static GLuint +_mesa_init_select_fbo(struct gl_context* ctx) { + GLuint result; + GLuint rbs[2]; + _mesa_GenFramebuffersEXT(1, &result); + _mesa_GenRenderbuffersEXT(2, rbs); + + _mesa_BindFramebufferEXT(GL_FRAMEBUFFER, result); + + _mesa_BindRenderbufferEXT(GL_RENDERBUFFER, rbs[0]); + + _mesa_RenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, + ctx->Select.BatchSize * ctx->Select._OffscreenSurfaceWidth, + ctx->Select._OffscreenSurfaceHeight * 2); + + _mesa_BindRenderbufferEXT(GL_RENDERBUFFER, rbs[1]); + _mesa_RenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA, + ctx->Select.BatchSize * ctx->Select._OffscreenSurfaceWidth, + ctx->Select._OffscreenSurfaceHeight * 2); + + _mesa_BindRenderbufferEXT(GL_RENDERBUFFER, 0); + + _mesa_FramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbs[0]); + _mesa_FramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbs[1]); + + assert(_mesa_CheckFramebufferStatusEXT(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + + _mesa_BindFramebufferEXT(GL_FRAMEBUFFER, 0); + + return result; +} + +void +_mesa_init_select_emulation(struct gl_context* ctx) +{ + if (ctx->Select._OffscreenSurface == 0) { + ctx->Select._OffscreenSurfaceWidth = ctx->Select._OffscreenSurfaceHeight = 1; + ctx->Select._OffscreenSurface = _mesa_init_select_fbo(ctx); + } +} static void GLAPIENTRY _mesa_FeedbackBuffer( GLsizei size, GLenum type, GLfloat *buffer ) @@ -144,6 +184,32 @@ _mesa_feedback_vertex(struct gl_context *ctx, } } +static void +_mesa_init_select_mode(struct gl_context *ctx) +{ + _mesa_init_select_emulation(ctx); + + /* save relevant states */ + ctx->Select.FboSave = ctx->DrawBuffer->Name; + // memcpy(&ctx->Select.Scissor, &ctx->Scissor, sizeof(struct gl_scissor_attrib)); + + /* change render target */ + _mesa_BindFramebufferEXT(GL_FRAMEBUFFER, ctx->Select._OffscreenSurface); + // _mesa_set_enable(ctx, GL_SCISSOR_TEST, GL_FALSE); + + /* init */ + _mesa_select_clear_fbo(ctx); +} + +static void +_mesa_exit_select_mode(struct gl_context *ctx) +{ + /* restore states */ + _mesa_BindFramebufferEXT(GL_FRAMEBUFFER, ctx->Select.FboSave); + +} + + /**********************************************************************/ /** \name Selection */ @@ -177,9 +243,11 @@ _mesa_SelectBuffer( GLsizei size, GLuint *buffer ) ctx->Select.Buffer = buffer; ctx->Select.BufferSize = size; ctx->Select.BufferCount = 0; +/* ctx->Select.HitFlag = GL_FALSE; ctx->Select.HitMinZ = 1.0; ctx->Select.HitMaxZ = 0.0; +*/ } @@ -214,6 +282,7 @@ write_record(struct gl_context *ctx, GLuint value) void _mesa_update_hitflag(struct gl_context *ctx, GLfloat z) { +/* ctx->Select.HitFlag = GL_TRUE; if (z < ctx->Select.HitMinZ) { ctx->Select.HitMinZ = z; @@ -221,6 +290,7 @@ _mesa_update_hitflag(struct gl_context *ctx, GLfloat z) if (z > ctx->Select.HitMaxZ) { ctx->Select.HitMaxZ = z; } +*/ } @@ -236,7 +306,7 @@ _mesa_update_hitflag(struct gl_context *ctx, GLfloat z) * \sa gl_selection. */ static void -write_hit_record(struct gl_context *ctx) +write_hit_record(struct gl_context *ctx, GLfloat hitMinZ, GLfloat hitMaxZ, GLuint nameStackDepth, GLuint* nameStack) { GLuint i; GLuint zmin, zmax, zscale = (~0u); @@ -245,22 +315,114 @@ write_hit_record(struct gl_context *ctx) /* 2^32-1 and round to nearest unsigned integer. */ assert( ctx != NULL ); /* this line magically fixes a SunOS 5.x/gcc bug */ - zmin = (GLuint) ((GLfloat) zscale * ctx->Select.HitMinZ); - zmax = (GLuint) ((GLfloat) zscale * ctx->Select.HitMaxZ); + zmin = (GLuint) ((GLfloat) zscale * hitMinZ); + zmax = (GLuint) ((GLfloat) zscale * hitMaxZ); - write_record( ctx, ctx->Select.NameStackDepth ); + write_record( ctx, nameStackDepth ); write_record( ctx, zmin ); write_record( ctx, zmax ); - for (i = 0; i < ctx->Select.NameStackDepth; i++) { - write_record( ctx, ctx->Select.NameStack[i] ); + for (i = 0; i < nameStackDepth; i++) { + write_record( ctx, nameStack[i] ); } ctx->Select.Hits++; - ctx->Select.HitFlag = GL_FALSE; - ctx->Select.HitMinZ = 1.0; - ctx->Select.HitMaxZ = -1.0; } +void _mesa_select_clear_fbo(struct gl_context *ctx) +{ + struct gl_scissor_attrib scissorSave; + /* save state */ + memcpy(&scissorSave, &ctx->Scissor, sizeof(struct gl_scissor_attrib)); + + GLfloat clearDepth = ctx->Depth.Clear; + + assert(ctx->ReadBuffer->Name == ctx->Select._OffscreenSurface); + assert(ctx->DrawBuffer->Name == ctx->Select._OffscreenSurface); + + /* hack needed because Clear does nothing if render mode != GL_RENDER */ + ctx->RenderMode = GL_RENDER; + + _mesa_set_enable(ctx, GL_SCISSOR_TEST, GL_TRUE); + + /* clear upper part which holds minZ */ + _mesa_Scissor(0, 0, + ctx->Select.BatchSize * ctx->Select._OffscreenSurfaceWidth, ctx->Select._OffscreenSurfaceHeight); + _mesa_ClearDepth(1.0f); + _mesa_Clear(GL_DEPTH_BUFFER_BIT); + + /* clear upper part which holds maxZ */ + _mesa_Scissor( + 0, ctx->Select._OffscreenSurfaceHeight, + ctx->Select.BatchSize * ctx->Select._OffscreenSurfaceWidth, ctx->Select._OffscreenSurfaceHeight); + _mesa_ClearDepth(0.0f); + _mesa_Clear(GL_DEPTH_BUFFER_BIT); + + ctx->RenderMode = GL_SELECT; + + /* restore state */ + _mesa_ClearDepth(clearDepth); + _mesa_set_enable(ctx, GL_SCISSOR_TEST, scissorSave.Enabled); + _mesa_Scissor(scissorSave.X, scissorSave.Y, + scissorSave.Width, scissorSave.Height); +} + + +static void +_mesa_exec_select_batch(struct gl_context *ctx) +{ + if (!ctx->Select.BatchEntryCount) + return; + + /* read back fbo */ + const GLuint fboW = ctx->Select._OffscreenSurfaceWidth * ctx->Select.BatchEntryCount; + const GLuint fboH = ctx->Select._OffscreenSurfaceHeight * 2; + float buffer[fboW * fboH]; + _mesa_ReadPixels(0, 0, + fboW, fboH, + GL_DEPTH_COMPONENT, GL_FLOAT, buffer); + + /* process each entry */ + for(int i=0; iSelect.BatchEntryCount; i++) { + if (ctx->Select.SelectBatch[i].HasBeenDrawn) { + int count = 0; + int xOffset = ctx->Select._OffscreenSurfaceWidth * i; + float minZ = 1, maxZ = -1; + + for(int y=0; ySelect._OffscreenSurfaceHeight; y++) { + for(int x=0; xSelect._OffscreenSurfaceWidth; x++) { + { + float z = buffer[y * fboW + xOffset + x]; + if (z < 1.0) { + if (z < minZ) + minZ = z; + ++count; + } + } + { + float z = buffer[(y + ctx->Select._OffscreenSurfaceHeight) * fboW + xOffset + x]; + if (z > 0.0) { + if (z > maxZ) + maxZ = z; + ++count; + } + } + } + } + if (count) { + printf("[%d] count: %d (%.3f %.3f) : name=%d\n", i, count, minZ, maxZ, ctx->Select.SelectBatch[i].NameStack[ctx->Select.SelectBatch[i].NameStackDepth - 1]); + write_hit_record(ctx, minZ, maxZ, + ctx->Select.SelectBatch[i].NameStackDepth, + ctx->Select.SelectBatch[i].NameStack); + } + } + } + + /* clear FBO */ + _mesa_select_clear_fbo(ctx); + + /* reset entry count */ + ctx->Select.BatchEntryCount = 0; +} /** * Initialize the name stack. @@ -275,16 +437,17 @@ _mesa_InitNames( void ) GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - /* Record the hit before the HitFlag is wiped out again. */ if (ctx->RenderMode == GL_SELECT) { - if (ctx->Select.HitFlag) { - write_hit_record( ctx ); - } + _mesa_exec_select_batch(ctx); } + ctx->Select.NameStackDepth = 0; +/* ctx->Select.HitFlag = GL_FALSE; ctx->Select.HitMinZ = 1.0; ctx->Select.HitMaxZ = 0.0; +*/ + ctx->Select.BatchEntryCount = 0; ctx->NewState |= _NEW_RENDERMODE; } @@ -314,17 +477,25 @@ _mesa_LoadName( GLuint name ) return; } - FLUSH_VERTICES(ctx, _NEW_RENDERMODE); + FLUSH_VERTICES(ctx, _NEW_RENDERMODE); // ?? + + if (ctx->Select.BatchEntryCount >= ctx->Select.BatchSize) { + _mesa_exec_select_batch(ctx); + assert(ctx->Select.BatchEntryCount == 0); + } - if (ctx->Select.HitFlag) { - write_hit_record( ctx ); - } if (ctx->Select.NameStackDepth < MAX_NAME_STACK_DEPTH) { ctx->Select.NameStack[ctx->Select.NameStackDepth-1] = name; } else { ctx->Select.NameStack[MAX_NAME_STACK_DEPTH-1] = name; } + + /* reserve entry in batch */ + ctx->Select.SelectBatch[ctx->Select.BatchEntryCount].NameStackDepth = ctx->Select.NameStackDepth; + memcpy(ctx->Select.SelectBatch[ctx->Select.BatchEntryCount].NameStack, ctx->Select.NameStack, sizeof(ctx->Select.NameStack)); + ctx->Select.SelectBatch[ctx->Select.BatchEntryCount].HasBeenDrawn = GL_FALSE; + ctx->Select.BatchEntryCount++; } @@ -350,16 +521,28 @@ _mesa_PushName( GLuint name ) } FLUSH_VERTICES(ctx, _NEW_RENDERMODE); - if (ctx->Select.HitFlag) { - write_hit_record( ctx ); - } + if (ctx->Select.NameStackDepth >= MAX_NAME_STACK_DEPTH) { _mesa_error( ctx, GL_STACK_OVERFLOW, "glPushName" ); } - else + else { ctx->Select.NameStack[ctx->Select.NameStackDepth++] = name; + + /* reserve entry in batch */ + if (ctx->Select.BatchEntryCount >= ctx->Select.BatchSize) { + _mesa_exec_select_batch(ctx); + assert(ctx->Select.BatchEntryCount == 0); + } + ctx->Select.SelectBatch[ctx->Select.BatchEntryCount].NameStackDepth = ctx->Select.NameStackDepth; + memcpy(ctx->Select.SelectBatch[ctx->Select.BatchEntryCount].NameStack, ctx->Select.NameStack, sizeof(ctx->Select.NameStack)); + ctx->Select.SelectBatch[ctx->Select.BatchEntryCount].HasBeenDrawn = GL_FALSE; + ctx->Select.BatchEntryCount++; + } } +struct SelectBufferEntry { + GLuint NameStack[MAX_NAME_STACK_DEPTH]; +}; /** * Pop a name into the name stack. @@ -381,9 +564,7 @@ _mesa_PopName( void ) } FLUSH_VERTICES(ctx, _NEW_RENDERMODE); - if (ctx->Select.HitFlag) { - write_hit_record( ctx ); - } + if (ctx->Select.NameStackDepth == 0) { _mesa_error( ctx, GL_STACK_UNDERFLOW, "glPopName" ); } @@ -430,9 +611,8 @@ _mesa_RenderMode( GLenum mode ) result = 0; break; case GL_SELECT: - if (ctx->Select.HitFlag) { - write_hit_record( ctx ); - } + _mesa_exec_select_batch(ctx); + if (ctx->Select.BufferCount > ctx->Select.BufferSize) { /* overflow */ #ifdef DEBUG @@ -446,6 +626,9 @@ _mesa_RenderMode( GLenum mode ) ctx->Select.BufferCount = 0; ctx->Select.Hits = 0; ctx->Select.NameStackDepth = 0; + ctx->Select.BatchEntryCount = 0; + + _mesa_exit_select_mode(ctx); break; #if _HAVE_FULL_GL case GL_FEEDBACK: @@ -468,6 +651,7 @@ _mesa_RenderMode( GLenum mode ) case GL_RENDER: break; case GL_SELECT: + _mesa_init_select_mode(ctx); if (ctx->Select.BufferSize==0) { /* haven't called glSelectBuffer yet */ _mesa_error( ctx, GL_INVALID_OPERATION, "glRenderMode" ); @@ -537,6 +721,14 @@ void _mesa_init_feedback( struct gl_context * ctx ) /* Miscellaneous */ ctx->RenderMode = GL_RENDER; + + /* */ + ctx->Select._OffscreenSurface = 0; + + ctx->Select.BatchSize = 16; + ctx->Select.BatchEntryCount = 0; + ctx->Select.SelectBatch = + (struct Entry*) malloc(sizeof(struct Entry) * ctx->Select.BatchSize); } /*@}*/ diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h index b881183..236f132 100644 --- a/src/mesa/main/mtypes.h +++ b/src/mesa/main/mtypes.h @@ -1718,9 +1718,21 @@ struct gl_selection GLuint Hits; /**< number of records in the selection buffer */ GLuint NameStackDepth; /**< name stack depth */ GLuint NameStack[MAX_NAME_STACK_DEPTH]; /**< name stack */ - GLboolean HitFlag; /**< hit flag */ - GLfloat HitMinZ; /**< minimum hit depth */ - GLfloat HitMaxZ; /**< maximum hit depth */ + // GLboolean HitFlag; /**< hit flag */ + // GLfloat HitMinZ; /**< minimum hit depth */ + // GLfloat HitMaxZ; /**< maximum hit depth */ + + GLuint _OffscreenSurface; /** off-screen rendering surface */ + GLuint _OffscreenSurfaceWidth, _OffscreenSurfaceHeight; /** off-screen rendering surface */ + + struct Entry { + GLuint NameStackDepth; /**< name stack depth */ + GLuint NameStack[MAX_NAME_STACK_DEPTH]; /**< name stack */ + GLboolean HasBeenDrawn; + }* SelectBatch; + GLint BatchSize, BatchEntryCount; + + int FboSave; /** FBO active when GL_SELECT enabled */ }; diff --git a/src/mesa/state_tracker/st_cb_feedback.c b/src/mesa/state_tracker/st_cb_feedback.c index 9b85a39..69c5edc 100644 --- a/src/mesa/state_tracker/st_cb_feedback.c +++ b/src/mesa/state_tracker/st_cb_feedback.c @@ -270,6 +270,90 @@ draw_glselect_stage(struct gl_context *ctx, struct draw_context *draw) return &fs->stage; } +/* This function contains copy-paste code from depth.c except FLUSH_VERTICES is not called. + This should be safe as it should be called only from a current flush call. + */ +static void +select_change_depth_state(struct gl_context *ctx, GLenum func, GLboolean mask, GLboolean test) { + /* depth function */ + if (ctx->Depth.Func != func) { + ctx->NewState |= _NEW_DEPTH; + ctx->Depth.Func = func; + + if (ctx->Driver.DepthFunc) + ctx->Driver.DepthFunc( ctx, func ); + } + + /* depth mask */ + if (ctx->Depth.Mask != mask) { + ctx->NewState |= _NEW_DEPTH; + ctx->Depth.Mask = mask; + + if (ctx->Driver.DepthMask) + ctx->Driver.DepthMask( ctx, mask ); + } + + /* depth test */ + if (ctx->Depth.Test != test) { + ctx->NewState |= _NEW_DEPTH; + ctx->Depth.Test = test; + } +} + +/* called once par object */ +static void +select_draw_func (struct gl_context *ctx, + const struct gl_client_array **arrays, + const struct _mesa_prim *prims, + GLuint nr_prims, + const struct _mesa_index_buffer *ib, + GLboolean index_bounds_valid, + GLuint min_index, + GLuint max_index) + { + struct gl_depthbuffer_attrib depthSave; + struct gl_viewport_attrib viewportSave; + GLboolean scissorEnabledSave; + int batchEntry; + + /* save state */ + memcpy(&depthSave, &ctx->Depth, sizeof(struct gl_depthbuffer_attrib)); + memcpy(&viewportSave, &ctx->Viewport, sizeof(struct gl_viewport_attrib)); + scissorEnabledSave = ctx->Scissor.Enabled; + + assert(ctx->ReadBuffer->Name == ctx->Select._OffscreenSurface); + assert(ctx->DrawBuffer->Name == ctx->Select._OffscreenSurface); + assert(ctx->Select.BatchEntryCount > 0); + + batchEntry = ctx->Select.BatchEntryCount - 1; + + ctx->Select.SelectBatch[batchEntry].HasBeenDrawn = GL_TRUE; + + /* setup for minZ */ + ctx->Scissor.Enabled = GL_FALSE; + select_change_depth_state(ctx, GL_LESS, GL_TRUE, GL_TRUE); + _mesa_set_viewport(ctx, + ctx->Select._OffscreenSurfaceWidth * batchEntry, 0, + ctx->Select._OffscreenSurfaceWidth, ctx->Select._OffscreenSurfaceHeight); + _mesa_update_state(ctx); + /* draw */ + st_draw_vbo(ctx, arrays, prims, nr_prims, ib, index_bounds_valid, min_index, max_index); + + /* setup for maxZ */ + _mesa_set_viewport(ctx, + ctx->Select._OffscreenSurfaceWidth * batchEntry, ctx->Select._OffscreenSurfaceHeight, + ctx->Select._OffscreenSurfaceWidth, ctx->Select._OffscreenSurfaceHeight); + select_change_depth_state(ctx, GL_GREATER, GL_TRUE, GL_TRUE); + _mesa_update_state(ctx); + /* draw */ + st_draw_vbo(ctx, arrays, prims, nr_prims, ib, index_bounds_valid, min_index, max_index); + + /* restore state */ + select_change_depth_state(ctx, depthSave.Func, depthSave.Mask, depthSave.Test); + _mesa_set_viewport(ctx, viewportSave.X, viewportSave.Y, + viewportSave.Width, viewportSave.Height); + ctx->Scissor.Enabled = scissorEnabledSave; + } static void st_RenderMode(struct gl_context *ctx, GLenum newMode ) @@ -282,11 +366,8 @@ st_RenderMode(struct gl_context *ctx, GLenum newMode ) vbo_set_draw_func(ctx, st_draw_vbo); } else if (newMode == GL_SELECT) { - if (!st->selection_stage) - st->selection_stage = draw_glselect_stage(ctx, draw); - draw_set_rasterize_stage(draw, st->selection_stage); - /* Plug in new vbo draw function */ - vbo_set_draw_func(ctx, st_feedback_draw_vbo); + /* use custom hw VBO draw function */ + vbo_set_draw_func(ctx, select_draw_func); } else { if (!st->feedback_stage) @@ -300,7 +381,6 @@ st_RenderMode(struct gl_context *ctx, GLenum newMode ) } - void st_init_feedback_functions(struct dd_function_table *functions) { functions->RenderMode = st_RenderMode; -- 1.7.5.4