[Mesa-dev] [PATCH] r600g: fix queries and predication
Vadim Girlin
vadimgirlin at gmail.com
Thu Jul 14 20:22:20 PDT 2011
Use all zpass data for predication instead of the last block only.
Use query buffer as a ring instead of reusing the same area
for each new BeginQuery. All query buffer offsets are in bytes
to simplify offsets math.
---
src/gallium/drivers/r600/r600.h | 19 +++-
src/gallium/drivers/r600/r600_query.c | 15 ++-
src/gallium/winsys/r600/drm/r600_hw_context.c | 131 +++++++++++++++++-------
src/gallium/winsys/r600/drm/r600d.h | 2 +
4 files changed, 118 insertions(+), 49 deletions(-)
diff --git a/src/gallium/drivers/r600/r600.h b/src/gallium/drivers/r600/r600.h
index 2af4d31..61adc7e 100644
--- a/src/gallium/drivers/r600/r600.h
+++ b/src/gallium/drivers/r600/r600.h
@@ -211,14 +211,21 @@ struct r600_reloc {
*/
struct r600_query {
u64 result;
- /* The kind of query. Currently only OQ is supported. */
+ /* The kind of query */
unsigned type;
- /* How many results have been written, in dwords. It's incremented
- * after end_query and flush. */
- unsigned num_results;
- /* if we've flushed the query */
+ /* Offset of the first result for current query */
+ unsigned results_start;
+ /* Offset of the next free result after current query data */
+ unsigned results_end;
+ /* Size of the result */
+ unsigned result_size;
+ /* Count of new queries started in one stream without flushing */
+ unsigned queries_emitted;
+ /* State flags */
unsigned state;
- /* The buffer where query results are stored. */
+ /* The buffer where query results are stored. It's used as a ring,
+ * data blocks for current query are stored sequentially from
+ * results_start to results_end, with wrapping on the buffer end */
struct r600_bo *buffer;
unsigned buffer_size;
/* linked list of queries */
diff --git a/src/gallium/drivers/r600/r600_query.c b/src/gallium/drivers/r600/r600_query.c
index 174505c..de1f5d0 100644
--- a/src/gallium/drivers/r600/r600_query.c
+++ b/src/gallium/drivers/r600/r600_query.c
@@ -43,7 +43,7 @@ static void r600_begin_query(struct pipe_context *ctx, struct pipe_query *query)
struct r600_query *rquery = (struct r600_query *)query;
rquery->result = 0;
- rquery->num_results = 0;
+ rquery->results_start = rquery->results_end;
r600_query_begin(&rctx->ctx, (struct r600_query *)query);
}
@@ -72,12 +72,18 @@ static void r600_render_condition(struct pipe_context *ctx,
struct r600_query *rquery = (struct r600_query *)query;
int wait_flag = 0;
+ /* If we already have nonzero result, render unconditionally */
+ if (query != NULL && rquery->result != 0)
+ return;
+
rctx->current_render_cond = query;
rctx->current_render_cond_mode = mode;
- if (!query) {
- rctx->ctx.predicate_drawing = false;
- r600_query_predication(&rctx->ctx, NULL, PREDICATION_OP_CLEAR, 1);
+ if (query == NULL) {
+ if (rctx->ctx.predicate_drawing) {
+ rctx->ctx.predicate_drawing = false;
+ r600_query_predication(&rctx->ctx, NULL, PREDICATION_OP_CLEAR, 1);
+ }
return;
}
@@ -88,7 +94,6 @@ static void r600_render_condition(struct pipe_context *ctx,
rctx->ctx.predicate_drawing = true;
r600_query_predication(&rctx->ctx, rquery, PREDICATION_OP_ZPASS, wait_flag);
-
}
void r600_init_query_functions(struct r600_pipe_context *rctx)
diff --git a/src/gallium/winsys/r600/drm/r600_hw_context.c b/src/gallium/winsys/r600/drm/r600_hw_context.c
index a2f13ff..922367d 100644
--- a/src/gallium/winsys/r600/drm/r600_hw_context.c
+++ b/src/gallium/winsys/r600/drm/r600_hw_context.c
@@ -1695,10 +1695,9 @@ out_err:
static boolean r600_query_result(struct r600_context *ctx, struct r600_query *query, boolean wait)
{
+ unsigned results_base = query->results_start;
u64 start, end;
- u32 *results;
- int i;
- int size;
+ u32 *results, *current_result;
if (wait)
results = r600_bo_map(ctx->radeon, query->buffer, PB_USAGE_CPU_READ, NULL);
@@ -1707,25 +1706,31 @@ static boolean r600_query_result(struct r600_context *ctx, struct r600_query *qu
if (!results)
return FALSE;
- /* query->num_results contains how many dwords were used for the query */
- size = query->num_results;
- for (i = 0; i < size; i += 4) {
- start = (u64)results[i] | (u64)results[i + 1] << 32;
- end = (u64)results[i + 2] | (u64)results[i + 3] << 32;
+
+ /* count all results across all data blocks */
+ while (results_base != query->results_end) {
+ current_result = (u32*)((char*)results + results_base);
+
+ start = (u64)current_result[0] | (u64)current_result[1] << 32;
+ end = (u64)current_result[2] | (u64)current_result[3] << 32;
if (((start & 0x8000000000000000UL) && (end & 0x8000000000000000UL))
|| query->type == PIPE_QUERY_TIME_ELAPSED) {
query->result += end - start;
}
+
+ results_base += 4 * 4;
+ if (results_base >= query->buffer_size)
+ results_base = 0;
}
- r600_bo_unmap(ctx->radeon, query->buffer);
- query->num_results = 0;
+ query->results_start = query->results_end;
+ r600_bo_unmap(ctx->radeon, query->buffer);
return TRUE;
}
void r600_query_begin(struct r600_context *ctx, struct r600_query *query)
{
- unsigned required_space, required_buffer;
+ unsigned required_space, new_results_end;
int num_backends = r600_get_num_backends(ctx->radeon);
/* query request needs 6/8 dwords for begin + 6/8 dwords for end */
@@ -1739,26 +1744,41 @@ void r600_query_begin(struct r600_context *ctx, struct r600_query *query)
r600_context_flush(ctx);
}
- required_buffer = query->num_results +
- 4 * (query->type == PIPE_QUERY_OCCLUSION_COUNTER ? ctx->max_db : 1);
+ /* if it's new OQ (not resume) */
+ if (query->type == PIPE_QUERY_OCCLUSION_COUNTER &&
+ query->results_start == query->results_end) {
+ /* Count queries emitted without flushes, and flush if more than
+ * half of buffer used, to avoid overwriting results which may be
+ * still in use. */
+ if (query->state & R600_QUERY_STATE_FLUSHED) {
+ query->queries_emitted = 1;
+ } else {
+ if (++query->queries_emitted > query->buffer_size / query->result_size / 2)
+ r600_context_flush(ctx);
+ }
+ }
+
+ new_results_end = query->results_end + query->result_size;
+ if (new_results_end > query->buffer_size)
+ new_results_end = 0;
- /* if query buffer is full force a flush */
- if (required_buffer*4 > query->buffer_size) {
+ /* collect current results if query buffer is full */
+ if (new_results_end == query->results_start) {
if (!(query->state & R600_QUERY_STATE_FLUSHED))
r600_context_flush(ctx);
r600_query_result(ctx, query, TRUE);
}
- if (query->type == PIPE_QUERY_OCCLUSION_COUNTER &&
- num_backends > 0) {
- /* as per info on ZPASS the driver must set the unusued DB top bits */
+ if (query->type == PIPE_QUERY_OCCLUSION_COUNTER) {
u32 *results;
int i;
results = r600_bo_map(ctx->radeon, query->buffer, PB_USAGE_CPU_WRITE, NULL);
if (results) {
- memset(results + query->num_results, 0, ctx->max_db * 4 * 4);
+ results = (u32*)((char*)results + query->results_end);
+ memset(results, 0, query->result_size);
+ /* Set top bits for unused backends */
for (i = num_backends; i < ctx->max_db; i++) {
results[(i * 4)+1] = 0x80000000;
results[(i * 4)+3] = 0x80000000;
@@ -1771,14 +1791,14 @@ void r600_query_begin(struct r600_context *ctx, struct r600_query *query)
if (query->type == PIPE_QUERY_TIME_ELAPSED) {
ctx->pm4[ctx->pm4_cdwords++] = PKT3(PKT3_EVENT_WRITE_EOP, 4, 0);
ctx->pm4[ctx->pm4_cdwords++] = EVENT_TYPE(EVENT_TYPE_CACHE_FLUSH_AND_INV_TS_EVENT) | EVENT_INDEX(5);
- ctx->pm4[ctx->pm4_cdwords++] = query->num_results*4 + r600_bo_offset(query->buffer);
+ ctx->pm4[ctx->pm4_cdwords++] = query->results_end + r600_bo_offset(query->buffer);
ctx->pm4[ctx->pm4_cdwords++] = (3 << 29);
ctx->pm4[ctx->pm4_cdwords++] = 0;
ctx->pm4[ctx->pm4_cdwords++] = 0;
} else {
ctx->pm4[ctx->pm4_cdwords++] = PKT3(PKT3_EVENT_WRITE, 2, 0);
ctx->pm4[ctx->pm4_cdwords++] = EVENT_TYPE(EVENT_TYPE_ZPASS_DONE) | EVENT_INDEX(1);
- ctx->pm4[ctx->pm4_cdwords++] = query->num_results*4 + r600_bo_offset(query->buffer);
+ ctx->pm4[ctx->pm4_cdwords++] = query->results_end + r600_bo_offset(query->buffer);
ctx->pm4[ctx->pm4_cdwords++] = 0;
}
ctx->pm4[ctx->pm4_cdwords++] = PKT3(PKT3_NOP, 0, 0);
@@ -1792,50 +1812,75 @@ void r600_query_begin(struct r600_context *ctx, struct r600_query *query)
void r600_query_end(struct r600_context *ctx, struct r600_query *query)
{
- /* emit begin query */
+ /* emit end query */
if (query->type == PIPE_QUERY_TIME_ELAPSED) {
ctx->pm4[ctx->pm4_cdwords++] = PKT3(PKT3_EVENT_WRITE_EOP, 4, 0);
ctx->pm4[ctx->pm4_cdwords++] = EVENT_TYPE(EVENT_TYPE_CACHE_FLUSH_AND_INV_TS_EVENT) | EVENT_INDEX(5);
- ctx->pm4[ctx->pm4_cdwords++] = query->num_results*4 + 8 + r600_bo_offset(query->buffer);
+ ctx->pm4[ctx->pm4_cdwords++] = query->results_end + 8 + r600_bo_offset(query->buffer);
ctx->pm4[ctx->pm4_cdwords++] = (3 << 29);
ctx->pm4[ctx->pm4_cdwords++] = 0;
ctx->pm4[ctx->pm4_cdwords++] = 0;
} else {
ctx->pm4[ctx->pm4_cdwords++] = PKT3(PKT3_EVENT_WRITE, 2, 0);
ctx->pm4[ctx->pm4_cdwords++] = EVENT_TYPE(EVENT_TYPE_ZPASS_DONE) | EVENT_INDEX(1);
- ctx->pm4[ctx->pm4_cdwords++] = query->num_results*4 + 8 + r600_bo_offset(query->buffer);
+ ctx->pm4[ctx->pm4_cdwords++] = query->results_end + 8 + r600_bo_offset(query->buffer);
ctx->pm4[ctx->pm4_cdwords++] = 0;
}
ctx->pm4[ctx->pm4_cdwords++] = PKT3(PKT3_NOP, 0, 0);
ctx->pm4[ctx->pm4_cdwords++] = 0;
r600_context_bo_reloc(ctx, &ctx->pm4[ctx->pm4_cdwords - 1], query->buffer);
- query->num_results += 4 * (query->type == PIPE_QUERY_OCCLUSION_COUNTER ? ctx->max_db : 1);
+ query->results_end += query->result_size;
+ if (query->results_end >= query->buffer_size)
+ query->results_end = 0;
+
query->state ^= R600_QUERY_STATE_STARTED;
query->state |= R600_QUERY_STATE_ENDED;
query->state &= ~R600_QUERY_STATE_FLUSHED;
+
ctx->num_query_running--;
}
void r600_query_predication(struct r600_context *ctx, struct r600_query *query, int operation,
int flag_wait)
{
- ctx->pm4[ctx->pm4_cdwords++] = PKT3(PKT3_SET_PREDICATION, 1, 0);
-
if (operation == PREDICATION_OP_CLEAR) {
+ if (ctx->pm4_cdwords + 3 > ctx->pm4_ndwords)
+ r600_context_flush(ctx);
+
+ ctx->pm4[ctx->pm4_cdwords++] = PKT3(PKT3_SET_PREDICATION, 1, 0);
ctx->pm4[ctx->pm4_cdwords++] = 0;
ctx->pm4[ctx->pm4_cdwords++] = PRED_OP(PREDICATION_OP_CLEAR);
} else {
- int results_base = query->num_results - (4 * ctx->max_db);
+ unsigned results_base = query->results_start;
+ unsigned count;
+ u32 op;
- if (results_base < 0)
- results_base = 0;
+ /* find count of the query data blocks */
+ count = query->buffer_size + query->results_end - query->results_start;
+ if (count > query->buffer_size) count-=query->buffer_size;
+ count /= query->result_size;
- ctx->pm4[ctx->pm4_cdwords++] = results_base*4 + r600_bo_offset(query->buffer);
- ctx->pm4[ctx->pm4_cdwords++] = PRED_OP(operation) | (flag_wait ? PREDICATION_HINT_WAIT : PREDICATION_HINT_NOWAIT_DRAW) | PREDICATION_DRAW_VISIBLE;
- ctx->pm4[ctx->pm4_cdwords++] = PKT3(PKT3_NOP, 0, 0);
- ctx->pm4[ctx->pm4_cdwords++] = 0;
- r600_context_bo_reloc(ctx, &ctx->pm4[ctx->pm4_cdwords - 1], query->buffer);
+ if (ctx->pm4_cdwords + 5 * count > ctx->pm4_ndwords)
+ r600_context_flush(ctx);
+
+ op = PRED_OP(operation) | PREDICATION_DRAW_VISIBLE |
+ (flag_wait ? PREDICATION_HINT_WAIT : PREDICATION_HINT_NOWAIT_DRAW);
+
+ /* emit predicate packets for all data blocks */
+ while (results_base != query->results_end) {
+ ctx->pm4[ctx->pm4_cdwords++] = PKT3(PKT3_SET_PREDICATION, 1, 0);
+ ctx->pm4[ctx->pm4_cdwords++] = results_base + r600_bo_offset(query->buffer);
+ ctx->pm4[ctx->pm4_cdwords++] = op;
+ ctx->pm4[ctx->pm4_cdwords++] = PKT3(PKT3_NOP, 0, 0);
+ ctx->pm4[ctx->pm4_cdwords++] = 0;
+ r600_context_bo_reloc(ctx, &ctx->pm4[ctx->pm4_cdwords - 1], query->buffer);
+ results_base += query->result_size;
+ if (results_base >= query->buffer_size)
+ results_base = 0;
+ /* set CONTINUE bit for all packets except the first */
+ op |= PREDICATION_CONTINUE;
+ }
}
}
@@ -1853,6 +1898,14 @@ struct r600_query *r600_context_query_create(struct r600_context *ctx, unsigned
query->type = query_type;
query->buffer_size = 4096;
+ if (query_type == PIPE_QUERY_OCCLUSION_COUNTER)
+ query->result_size = 4 * 4 * ctx->max_db;
+ else
+ query->result_size = 4 * 4;
+
+ /* adjust buffer size to simplify offsets wrapping math */
+ query->buffer_size -= query->buffer_size % query->result_size;
+
/* As of GL4, query buffers are normally read by the CPU after
* being written by the gpu, hence staging is probably a good
* usage pattern.
@@ -1882,7 +1935,7 @@ boolean r600_context_query_result(struct r600_context *ctx,
{
uint64_t *result = (uint64_t*)vresult;
- if (query->num_results && !(query->state & R600_QUERY_STATE_FLUSHED)) {
+ if (!(query->state & R600_QUERY_STATE_FLUSHED)) {
r600_context_flush(ctx);
}
if (!r600_query_result(ctx, query, wait))
@@ -1912,10 +1965,12 @@ void r600_context_queries_resume(struct r600_context *ctx, boolean flushed)
struct r600_query *query;
LIST_FOR_EACH_ENTRY(query, &ctx->query_list, list) {
+ if (flushed)
+ query->state |= R600_QUERY_STATE_FLUSHED;
+
if (query->state & R600_QUERY_STATE_SUSPENDED) {
r600_query_begin(ctx, query);
query->state ^= R600_QUERY_STATE_SUSPENDED;
- } else if (flushed && query->state==R600_QUERY_STATE_ENDED)
- query->state |= R600_QUERY_STATE_FLUSHED;
+ }
}
}
diff --git a/src/gallium/winsys/r600/drm/r600d.h b/src/gallium/winsys/r600/drm/r600d.h
index 8042481..4a19dcf 100644
--- a/src/gallium/winsys/r600/drm/r600d.h
+++ b/src/gallium/winsys/r600/drm/r600d.h
@@ -114,6 +114,8 @@
#define PRED_OP(x) ((x) << 16)
+#define PREDICATION_CONTINUE (1 << 31)
+
#define PREDICATION_HINT_WAIT (0 << 12)
#define PREDICATION_HINT_NOWAIT_DRAW (1 << 12)
--
1.7.6
More information about the mesa-dev
mailing list