From 5d7144f24ea44b568a52343f18329992fa0657b2 Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Fri, 1 Jul 2011 11:49:14 +0200 Subject: [PATCH] Hardware acceleration for GL_SELECT render mode This is experimental, it should : - work for all Gallium-based drivers - breaks others. --- src/mesa/main/feedback.c | 215 +++++++++++++++++++++++++++---- src/mesa/main/mtypes.h | 17 ++- src/mesa/main/queryobj.c | 8 +- src/mesa/state_tracker/st_cb_feedback.c | 57 ++++++++- 4 files changed, 257 insertions(+), 40 deletions(-) diff --git a/src/mesa/main/feedback.c b/src/mesa/main/feedback.c index fcb089f..3fdd20c 100644 --- a/src/mesa/main/feedback.c +++ b/src/mesa/main/feedback.c @@ -48,7 +48,82 @@ #define FB_COLOR 0x04 #define FB_TEXTURE 0X08 +void _mesa_select_before_object(struct gl_context* ctx, int batchEntry) { + assert(ctx->ReadBuffer->Name == ctx->Select._OffscreenSurface); + assert(ctx->DrawBuffer->Name == ctx->Select._OffscreenSurface); + /* change viewport */ + _mesa_Viewport( + ctx->Select._OffscreenSurfaceWidth * batchEntry, 0, + ctx->Select._OffscreenSurfaceWidth, ctx->Select._OffscreenSurfaceHeight); + + /* begin query */ + _mesa_BeginQueryARB(GL_SAMPLES_PASSED, ctx->Select._Queries[batchEntry]); +} + + +void _mesa_select_after_object(struct gl_context* ctx) { + assert(ctx->ReadBuffer->Name == ctx->Select._OffscreenSurface); + assert(ctx->DrawBuffer->Name == ctx->Select._OffscreenSurface); + + /* end query */ + _mesa_EndQueryARB(GL_SAMPLES_PASSED); +} + +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); + + _mesa_BindRenderbufferEXT(GL_RENDERBUFFER, rbs[1]); + _mesa_RenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA, + ctx->Select.BatchSize * ctx->Select._OffscreenSurfaceWidth, + ctx->Select._OffscreenSurfaceHeight); + + _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; +} + +static GLuint* +_mesa_init_select_queries(struct gl_context* ctx) { + assert(ctx->Select.BatchSize > 0); + + GLuint* result = (GLuint*) malloc(ctx->Select.BatchSize * sizeof(GLuint)); + _mesa_GenQueriesARB(ctx->Select.BatchSize, result); + return result; +} + +void +_mesa_init_select_emulation(struct gl_context* ctx) +{ + + if (ctx->Select._OffscreenSurface == 0) { + ctx->Select._OffscreenSurfaceWidth = ctx->Select._OffscreenSurfaceHeight = 16; + ctx->Select._OffscreenSurface = _mesa_init_select_fbo(ctx); + } + + if (ctx->Select._Queries == 0) + ctx->Select._Queries = _mesa_init_select_queries(ctx); +} static void GLAPIENTRY _mesa_FeedbackBuffer( GLsizei size, GLenum type, GLfloat *buffer ) @@ -177,9 +252,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 +291,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 +299,7 @@ _mesa_update_hitflag(struct gl_context *ctx, GLfloat z) if (z > ctx->Select.HitMaxZ) { ctx->Select.HitMaxZ = z; } +*/ } @@ -236,7 +315,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,23 +324,67 @@ 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; } +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.BatchSize; + const GLuint fboH = ctx->Select._OffscreenSurfaceHeight; + 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++) { + /* gather query result */ + GLint queryResult; + _mesa_GetQueryObjectivARB(ctx->Select._Queries[i], GL_QUERY_RESULT_ARB, &queryResult); + + if (queryResult) { + int count = 0; + int xOffset = ctx->Select._OffscreenSurfaceWidth * i; + float minZ = 1, maxZ = -1; + + for(int y=0; ySelect._OffscreenSurfaceHeight && count < queryResult; y++) { + for(int x=0; xSelect._OffscreenSurfaceWidth && count < queryResult; x++) { + float z = buffer[y * fboW + xOffset + x]; + if (z != ctx->Depth.Clear) { + if (z < minZ) minZ = z; + if (z > maxZ) maxZ = z; + ++count; + } + } + } + if (count) { + write_hit_record(ctx, minZ, maxZ, + ctx->Select.SelectBatch[i].NameStackDepth, + ctx->Select.SelectBatch[i].NameStack); + } + } + } + + /* clear FBO */ + _mesa_Clear(GL_DEPTH_BUFFER_BIT); + /* reset entry count */ + ctx->Select.BatchEntryCount = 0; +} + /** * Initialize the name stack. * @@ -275,16 +398,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 +438,27 @@ _mesa_LoadName( GLuint name ) return; } - FLUSH_VERTICES(ctx, _NEW_RENDERMODE); + _mesa_select_after_object(ctx); + + 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)); + _mesa_select_before_object(ctx, ctx->Select.BatchEntryCount); + ctx->Select.BatchEntryCount++; } @@ -349,17 +483,32 @@ _mesa_PushName( GLuint name ) return; } + if (ctx->Select.NameStackDepth > 0) + _mesa_select_after_object(ctx); + 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)); + _mesa_select_before_object(ctx, ctx->Select.BatchEntryCount); + ctx->Select.BatchEntryCount++; + } } +struct SelectBufferEntry { + GLuint NameStack[MAX_NAME_STACK_DEPTH]; +}; /** * Pop a name into the name stack. @@ -380,10 +529,10 @@ _mesa_PopName( void ) return; } + _mesa_select_after_object(ctx); + 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 +579,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 +594,9 @@ _mesa_RenderMode( GLenum mode ) ctx->Select.BufferCount = 0; ctx->Select.Hits = 0; ctx->Select.NameStackDepth = 0; + ctx->Select.BatchEntryCount = 0; + + _mesa_init_select_emulation(ctx); break; #if _HAVE_FULL_GL case GL_FEEDBACK: @@ -537,6 +688,16 @@ void _mesa_init_feedback( struct gl_context * ctx ) /* Miscellaneous */ ctx->RenderMode = GL_RENDER; + + /* */ + ctx->Select._OffscreenSurface = 0; + ctx->Select._WriteDepthFS = 0; + ctx->Select._Queries = 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 f018c75..1165679 100644 --- a/src/mesa/main/mtypes.h +++ b/src/mesa/main/mtypes.h @@ -1718,9 +1718,20 @@ 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 */ + GLuint _WriteDepthFS; /** GL_SELECT special fragment shader */ + GLuint* _Queries; /** glQuery */ + + struct Entry { + GLuint NameStackDepth; /**< name stack depth */ + GLuint NameStack[MAX_NAME_STACK_DEPTH]; /**< name stack */ + }* SelectBatch; + GLint BatchSize, BatchEntryCount; }; diff --git a/src/mesa/main/queryobj.c b/src/mesa/main/queryobj.c index f0a9a79..9e9c817 100644 --- a/src/mesa/main/queryobj.c +++ b/src/mesa/main/queryobj.c @@ -173,7 +173,7 @@ get_query_binding_point(struct gl_context *ctx, GLenum target) } -static void GLAPIENTRY +void GLAPIENTRY _mesa_GenQueriesARB(GLsizei n, GLuint *ids) { GLuint first; @@ -264,7 +264,7 @@ _mesa_IsQueryARB(GLuint id) } -static void GLAPIENTRY +void GLAPIENTRY _mesa_BeginQueryARB(GLenum target, GLuint id) { struct gl_query_object *q, **bindpt; @@ -319,7 +319,7 @@ _mesa_BeginQueryARB(GLenum target, GLuint id) } -static void GLAPIENTRY +void GLAPIENTRY _mesa_EndQueryARB(GLenum target) { struct gl_query_object *q, **bindpt; @@ -386,7 +386,7 @@ _mesa_GetQueryivARB(GLenum target, GLenum pname, GLint *params) } -static void GLAPIENTRY +void GLAPIENTRY _mesa_GetQueryObjectivARB(GLuint id, GLenum pname, GLint *params) { struct gl_query_object *q = NULL; diff --git a/src/mesa/state_tracker/st_cb_feedback.c b/src/mesa/state_tracker/st_cb_feedback.c index 9b85a39..c5df367 100644 --- a/src/mesa/state_tracker/st_cb_feedback.c +++ b/src/mesa/state_tracker/st_cb_feedback.c @@ -270,6 +270,48 @@ draw_glselect_stage(struct gl_context *ctx, struct draw_context *draw) return &fs->stage; } +/* move to appropriate place (gl_context ?) */ +bool stateSaved; +GLuint fboSave = 0; +bool fpEnabled = false; +GLuint depthSave, scissorSave; +struct gl_viewport_attrib vpSave; + +static void +init_select_mode(struct gl_context *ctx) +{ + /* save states */ + fboSave = ctx->DrawBuffer->Name; + memcpy(&vpSave, &ctx->Viewport, sizeof(struct gl_viewport_attrib)); + depthSave = ctx->Depth.Test; + scissorSave = ctx->Scissor.Enabled; + + /* change render target */ + _mesa_BindFramebufferEXT(GL_FRAMEBUFFER, ctx->Select._OffscreenSurface); + + _mesa_Clear(GL_DEPTH_BUFFER_BIT); + + /* if not forced to false, Blender selection doesn't works reliably... */ + _mesa_set_enable(ctx, GL_DEPTH_TEST, GL_FALSE); + _mesa_set_enable(ctx, GL_SCISSOR_TEST, GL_FALSE); + + //_mesa_update_state(ctx); + stateSaved = true; +} + +static void +exit_select_mode(struct gl_context *ctx) +{ + if (stateSaved) { + /* restore states */ + _mesa_BindFramebufferEXT(GL_FRAMEBUFFER, fboSave); + _mesa_Viewport(vpSave.X, vpSave.Y, vpSave.Width, vpSave.Height); + _mesa_set_enable(ctx, GL_DEPTH_TEST, depthSave); + _mesa_set_enable(ctx, GL_SCISSOR_TEST, scissorSave); + } + stateSaved = false; + // _mesa_update_state(ctx); +} static void st_RenderMode(struct gl_context *ctx, GLenum newMode ) @@ -277,16 +319,20 @@ st_RenderMode(struct gl_context *ctx, GLenum newMode ) struct st_context *st = st_context(ctx); struct draw_context *draw = st->draw; + + if (newMode == GL_RENDER) { + exit_select_mode(ctx); + /* restore normal VBO draw function */ 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); + _mesa_init_select_emulation(ctx); + init_select_mode(ctx); + + /* use normal VBO draw function */ + vbo_set_draw_func(ctx, st_draw_vbo); } else { if (!st->feedback_stage) @@ -300,7 +346,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