draw: fix/improve dirty state validation

This patch does two things:

1. Constant buffer state changes were broken (but happened to work by
   dumb luck).  The problem is we weren't calling draw_do_flush() in
   draw_set_mapped_constant_buffer() when we changed that state.  All the
   other draw_set_foo() functions were calling draw_do_flush() already.

2. Use a simpler state validation step when we're changing light-weight
   parameter state such as constant buffers, viewport dims or clip planes.
   There's no need to revalidate the whole pipeline when changing state
   like that.  The new validation method is called bind_parameters()
   and is called instead of the prepare() method.  A new
   DRAW_FLUSH_PARAMETER_CHANGE flag is used to signal these light-weight
   state changes.  This results in a modest but measurable increase in
   FPS for many Mesa demos.

Reviewed-by: Jose Fonseca <jfonseca@vmware.com>
This commit is contained in:
Brian Paul 2012-12-07 13:58:34 -07:00
parent c5f544e690
commit 4b73cdb864
8 changed files with 68 additions and 23 deletions

View File

@ -289,7 +289,7 @@ void draw_set_rasterize_stage( struct draw_context *draw,
void draw_set_clip_state( struct draw_context *draw,
const struct pipe_clip_state *clip )
{
draw_do_flush( draw, DRAW_FLUSH_STATE_CHANGE );
draw_do_flush(draw, DRAW_FLUSH_PARAMETER_CHANGE);
memcpy(&draw->plane[6], clip->ucp, sizeof(clip->ucp));
}
@ -301,7 +301,7 @@ void draw_set_clip_state( struct draw_context *draw,
void draw_set_viewport_state( struct draw_context *draw,
const struct pipe_viewport_state *viewport )
{
draw_do_flush( draw, DRAW_FLUSH_STATE_CHANGE );
draw_do_flush(draw, DRAW_FLUSH_PARAMETER_CHANGE);
draw->viewport = *viewport; /* struct copy */
draw->identity_viewport = (viewport->scale[0] == 1.0f &&
viewport->scale[1] == 1.0f &&
@ -368,6 +368,8 @@ draw_set_mapped_constant_buffer(struct draw_context *draw,
shader_type == PIPE_SHADER_GEOMETRY);
debug_assert(slot < PIPE_MAX_CONSTANT_BUFFERS);
draw_do_flush(draw, DRAW_FLUSH_PARAMETER_CHANGE);
switch (shader_type) {
case PIPE_SHADER_VERTEX:
draw->pt.user.vs_constants[slot] = buffer;

View File

@ -347,6 +347,6 @@ void draw_pipeline_flush( struct draw_context *draw,
unsigned flags )
{
draw->pipeline.first->flush( draw->pipeline.first, flags );
if (!(flags & DRAW_FLUSH_BACKEND))
if (flags & DRAW_FLUSH_STATE_CHANGE)
draw->pipeline.first = draw->pipeline.validate;
}

View File

@ -144,6 +144,8 @@ struct draw_context
unsigned opt; /**< bitmask of PT_x flags */
unsigned eltSize; /* saved eltSize for flushing */
boolean rebind_parameters;
struct {
struct draw_pt_middle_end *fetch_emit;
struct draw_pt_middle_end *fetch_shade_emit;
@ -434,8 +436,9 @@ void draw_pipeline_flush( struct draw_context *draw,
* Flushing
*/
#define DRAW_FLUSH_STATE_CHANGE 0x8
#define DRAW_FLUSH_BACKEND 0x10
#define DRAW_FLUSH_PARAMETER_CHANGE 0x1 /**< Constants, viewport, etc */
#define DRAW_FLUSH_STATE_CHANGE 0x2 /**< Other/heavy state changes */
#define DRAW_FLUSH_BACKEND 0x4 /**< Flush the output buffer */
void draw_do_flush( struct draw_context *draw, unsigned flags );

View File

@ -139,6 +139,12 @@ draw_pt_arrays(struct draw_context *draw,
draw->pt.opt = opt;
}
if (draw->pt.rebind_parameters) {
/* update constants, viewport dims, clip planes, etc */
middle->bind_parameters(middle);
draw->pt.rebind_parameters = FALSE;
}
frontend->run( frontend, start, count );
return TRUE;
@ -146,13 +152,19 @@ draw_pt_arrays(struct draw_context *draw,
void draw_pt_flush( struct draw_context *draw, unsigned flags )
{
assert(flags);
if (draw->pt.frontend) {
draw->pt.frontend->flush( draw->pt.frontend, flags );
/* don't prepare if we only are flushing the backend */
if (!(flags & DRAW_FLUSH_BACKEND))
if (flags & DRAW_FLUSH_STATE_CHANGE)
draw->pt.frontend = NULL;
}
if (flags & DRAW_FLUSH_PARAMETER_CHANGE) {
draw->pt.rebind_parameters = TRUE;
}
}

View File

@ -93,6 +93,13 @@ struct draw_pt_middle_end {
unsigned opt,
unsigned *max_vertices );
/**
* Bind/update parameter state such as constants, viewport dims
* and clip planes. Basically, stuff which isn't "baked" into the
* shader or pipeline state.
*/
void (*bind_parameters)(struct draw_pt_middle_end *);
void (*run)( struct draw_pt_middle_end *,
const unsigned *fetch_elts,
unsigned fetch_count,

View File

@ -137,6 +137,15 @@ static void fetch_pipeline_prepare( struct draw_pt_middle_end *middle,
}
static void
fetch_pipeline_bind_parameters(struct draw_pt_middle_end *middle)
{
/* No-op since the vertex shader executor and drawing pipeline
* just grab the constants, viewport, etc. from the draw context state.
*/
}
static void fetch( struct pt_fetch *fetch,
const struct draw_fetch_info *fetch_info,
char *output)
@ -421,6 +430,7 @@ struct draw_pt_middle_end *draw_pt_fetch_pipeline_or_emit( struct draw_context *
goto fail;
fpme->base.prepare = fetch_pipeline_prepare;
fpme->base.bind_parameters = fetch_pipeline_bind_parameters;
fpme->base.run = fetch_pipeline_run;
fpme->base.run_linear = fetch_pipeline_linear_run;
fpme->base.run_linear_elts = fetch_pipeline_linear_run_elts;

View File

@ -180,24 +180,34 @@ llvm_middle_end_prepare( struct draw_pt_middle_end *middle,
fpme->current_variant = variant;
}
}
/* Bind the VS and GS input constants, clip planes and viewport */
{
unsigned i;
for (i = 0; i < Elements(fpme->llvm->jit_context.vs_constants); ++i) {
fpme->llvm->jit_context.vs_constants[i] =
draw->pt.user.vs_constants[i];
}
for (i = 0; i < Elements(fpme->llvm->jit_context.gs_constants); ++i) {
fpme->llvm->jit_context.gs_constants[i] =
draw->pt.user.gs_constants[i];
}
fpme->llvm->jit_context.planes =
(float (*) [DRAW_TOTAL_CLIP_PLANES][4]) draw->pt.user.planes[0];
fpme->llvm->jit_context.viewport =
(float *) draw->viewport.scale;
}
/**
* Bind/update constant buffer pointers, clip planes and viewport dims.
* These are "light weight" parameters which aren't baked into the
* generated code. Updating these items is much cheaper than revalidating
* and rebuilding the generated pipeline code.
*/
static void
llvm_middle_end_bind_parameters(struct draw_pt_middle_end *middle)
{
struct llvm_middle_end *fpme = (struct llvm_middle_end *)middle;
struct draw_context *draw = fpme->draw;
unsigned i;
for (i = 0; i < Elements(fpme->llvm->jit_context.vs_constants); ++i) {
fpme->llvm->jit_context.vs_constants[i] = draw->pt.user.vs_constants[i];
}
for (i = 0; i < Elements(fpme->llvm->jit_context.gs_constants); ++i) {
fpme->llvm->jit_context.gs_constants[i] = draw->pt.user.gs_constants[i];
}
fpme->llvm->jit_context.planes =
(float (*)[DRAW_TOTAL_CLIP_PLANES][4]) draw->pt.user.planes[0];
fpme->llvm->jit_context.viewport = (float *) draw->viewport.scale;
}
@ -448,6 +458,7 @@ draw_pt_fetch_pipeline_or_emit_llvm(struct draw_context *draw)
goto fail;
fpme->base.prepare = llvm_middle_end_prepare;
fpme->base.bind_parameters = llvm_middle_end_bind_parameters;
fpme->base.run = llvm_middle_end_run;
fpme->base.run_linear = llvm_middle_end_linear_run;
fpme->base.run_linear_elts = llvm_middle_end_linear_run_elts;

View File

@ -182,7 +182,7 @@ static void vsplit_flush(struct draw_pt_front_end *frontend, unsigned flags)
{
struct vsplit_frontend *vsplit = (struct vsplit_frontend *) frontend;
if (!(flags & DRAW_FLUSH_BACKEND)) {
if (flags & DRAW_FLUSH_STATE_CHANGE) {
vsplit->middle->finish(vsplit->middle);
vsplit->middle = NULL;
}