r300g: rewrite occlusion queries

The previous implementation had issues with queries spanning over several
command streams as well as using a very large number of queries.

This fixes flickering in Enemy Territory: Quake Wars. The driver now renders
everything correctly in this game and the graphics is awesome.
This commit is contained in:
Marek Olšák 2010-06-14 05:18:47 +02:00
parent 824c1f6a4a
commit cf3778bae0
7 changed files with 168 additions and 159 deletions

View file

@ -25,8 +25,23 @@
#include "util/u_format.h"
static void r300_blitter_save_states(struct r300_context* r300)
enum r300_blitter_op
{
R300_CLEAR,
R300_CLEAR_SURFACE,
R300_COPY
};
static void r300_blitter_begin(struct r300_context* r300, enum r300_blitter_op op)
{
if (r300->query_current) {
r300->blitter_saved_query = r300->query_current;
r300_stop_query(r300);
}
/* Yeah we have to save all those states to ensure the blitter operation
* is really transparent. The states will be restored by the blitter once
* copying is done. */
util_blitter_save_blend(r300->blitter, r300->blend_state.state);
util_blitter_save_depth_stencil_alpha(r300->blitter, r300->dsa_state.state);
util_blitter_save_stencil_ref(r300->blitter, &(r300->stencil_ref));
@ -38,6 +53,30 @@ static void r300_blitter_save_states(struct r300_context* r300)
util_blitter_save_vertex_elements(r300->blitter, r300->velems);
util_blitter_save_vertex_buffers(r300->blitter, r300->vertex_buffer_count,
r300->vertex_buffer);
if (op & (R300_CLEAR_SURFACE | R300_COPY))
util_blitter_save_framebuffer(r300->blitter, r300->fb_state.state);
if (op & R300_COPY) {
struct r300_textures_state* state =
(struct r300_textures_state*)r300->textures_state.state;
util_blitter_save_fragment_sampler_states(
r300->blitter, state->sampler_state_count,
(void**)state->sampler_states);
util_blitter_save_fragment_sampler_views(
r300->blitter, state->sampler_view_count,
(struct pipe_sampler_view**)state->sampler_views);
}
}
static void r300_blitter_end(struct r300_context *r300)
{
if (r300->blitter_saved_query) {
r300_resume_query(r300, r300->blitter_saved_query);
r300->blitter_saved_query = NULL;
}
}
/* Clear currently bound buffers. */
@ -73,13 +112,45 @@ static void r300_clear(struct pipe_context* pipe,
struct pipe_framebuffer_state* fb =
(struct pipe_framebuffer_state*)r300->fb_state.state;
r300_blitter_save_states(r300);
r300_blitter_begin(r300, R300_CLEAR);
util_blitter_clear(r300->blitter,
fb->width,
fb->height,
fb->nr_cbufs,
buffers, rgba, depth, stencil);
r300_blitter_end(r300);
}
/* Clear a region of a color surface to a constant value. */
static void r300_clear_render_target(struct pipe_context *pipe,
struct pipe_surface *dst,
const float *rgba,
unsigned dstx, unsigned dsty,
unsigned width, unsigned height)
{
struct r300_context *r300 = r300_context(pipe);
r300_blitter_begin(r300, R300_CLEAR_SURFACE);
util_blitter_clear_render_target(r300->blitter, dst, rgba,
dstx, dsty, width, height);
r300_blitter_end(r300);
}
/* Clear a region of a depth stencil surface. */
static void r300_clear_depth_stencil(struct pipe_context *pipe,
struct pipe_surface *dst,
unsigned clear_flags,
double depth,
unsigned stencil,
unsigned dstx, unsigned dsty,
unsigned width, unsigned height)
{
struct r300_context *r300 = r300_context(pipe);
r300_blitter_begin(r300, R300_CLEAR_SURFACE);
util_blitter_clear_depth_stencil(r300->blitter, dst, clear_flags, depth, stencil,
dstx, dsty, width, height);
r300_blitter_end(r300);
}
/* Copy a block of pixels from one surface to another using HW. */
@ -93,27 +164,12 @@ static void r300_hw_copy_region(struct pipe_context* pipe,
unsigned width, unsigned height)
{
struct r300_context* r300 = r300_context(pipe);
struct r300_textures_state* state =
(struct r300_textures_state*)r300->textures_state.state;
/* Yeah we have to save all those states to ensure this blitter operation
* is really transparent. The states will be restored by the blitter once
* copying is done. */
r300_blitter_save_states(r300);
util_blitter_save_framebuffer(r300->blitter, r300->fb_state.state);
util_blitter_save_fragment_sampler_states(
r300->blitter, state->sampler_state_count,
(void**)state->sampler_states);
util_blitter_save_fragment_sampler_views(
r300->blitter, state->sampler_view_count,
(struct pipe_sampler_view**)state->sampler_views);
/* Do a copy */
r300_blitter_begin(r300, R300_COPY);
util_blitter_copy_region(r300->blitter, dst, subdst, dstx, dsty, dstz,
src, subsrc, srcx, srcy, srcz, width, height,
TRUE);
r300_blitter_end(r300);
}
/* Copy a block of pixels from one surface to another. */
@ -187,40 +243,6 @@ static void r300_resource_copy_region(struct pipe_context *pipe,
}
}
/* Clear a region of a color surface to a constant value. */
static void r300_clear_render_target(struct pipe_context *pipe,
struct pipe_surface *dst,
const float *rgba,
unsigned dstx, unsigned dsty,
unsigned width, unsigned height)
{
struct r300_context *r300 = r300_context(pipe);
r300_blitter_save_states(r300);
util_blitter_save_framebuffer(r300->blitter, r300->fb_state.state);
util_blitter_clear_render_target(r300->blitter, dst, rgba,
dstx, dsty, width, height);
}
/* Clear a region of a depth stencil surface. */
static void r300_clear_depth_stencil(struct pipe_context *pipe,
struct pipe_surface *dst,
unsigned clear_flags,
double depth,
unsigned stencil,
unsigned dstx, unsigned dsty,
unsigned width, unsigned height)
{
struct r300_context *r300 = r300_context(pipe);
r300_blitter_save_states(r300);
util_blitter_save_framebuffer(r300->blitter, r300->fb_state.state);
util_blitter_clear_depth_stencil(r300->blitter, dst, clear_flags, depth, stencil,
dstx, dsty, width, height);
}
void r300_init_blit_functions(struct r300_context *r300)
{
r300->context.clear = r300_clear;

View file

@ -39,7 +39,7 @@
static void r300_destroy_context(struct pipe_context* context)
{
struct r300_context* r300 = r300_context(context);
struct r300_query* query, * temp;
struct r300_query *query, *temp;
struct r300_atom *atom;
util_blitter_destroy(r300->blitter);
@ -55,9 +55,6 @@ static void r300_destroy_context(struct pipe_context* context)
}
}
/* Free the OQ BO. */
context->screen->resource_destroy(context->screen, r300->oqbo);
/* If there are any queries pending or not destroyed, remove them now. */
foreach_s(query, temp, &r300->query_list) {
remove_from_list(query);
@ -221,9 +218,6 @@ struct pipe_context* r300_create_context(struct pipe_screen* screen,
r300_setup_atoms(r300);
/* Open up the OQ BO. */
r300->oqbo = pipe_buffer_create(screen,
R300_BIND_OQBO, 4096);
make_empty_list(&r300->query_list);
r300_init_blit_functions(r300);

View file

@ -241,14 +241,23 @@ struct r300_constant_buffer {
struct r300_query {
/* The kind of query. Currently only OQ is supported. */
unsigned type;
/* The current count of this query. Required to be at least 32 bits. */
unsigned int count;
/* The offset of this query into the query buffer, in bytes. */
unsigned offset;
/* The number of pipes where query results are stored. */
unsigned num_pipes;
/* 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 */
boolean flushed;
/* if begin has been emitted */
boolean begin_emitted;
/* The buffer where query results are stored. */
struct r300_winsys_buffer *buffer;
/* The size of the buffer. */
unsigned buffer_size;
/* The domain of the buffer. */
enum r300_buffer_domain domain;
/* Linked list members. */
struct r300_query* prev;
struct r300_query* next;
@ -388,10 +397,11 @@ struct r300_context {
/* Offset into the VBO. */
size_t vbo_offset;
/* Occlusion query buffer. */
struct pipe_resource* oqbo;
/* Query list. */
/* The currently active query. */
struct r300_query *query_current;
/* The saved query for blitter operations. */
struct r300_query *blitter_saved_query;
/* Query list. */
struct r300_query query_list;
/* Various CSO state objects. */
@ -522,6 +532,11 @@ void r300_init_render_functions(struct r300_context *r300);
void r300_init_state_functions(struct r300_context* r300);
void r300_init_resource_functions(struct r300_context* r300);
/* r300_query.c */
void r300_resume_query(struct r300_context *r300,
struct r300_query *query);
void r300_stop_query(struct r300_context *r300);
/* r300_render_translate.c */
void r300_begin_vertex_translate(struct r300_context *r300);
void r300_end_vertex_translate(struct r300_context *r300);

View file

@ -32,12 +32,6 @@
#define R300_INVALID_FORMAT 0xffff
/* XXX: this is just a bandaid on larger problems in
* r300_screen_buffer.h which doesn't seem to be fully ported to
* gallium-resources.
*/
#define R300_BIND_OQBO (1<<21)
/* Tiling flags. */
enum r300_buffer_tiling {
R300_BUFFER_LINEAR = 0,

View file

@ -353,13 +353,14 @@ void r300_emit_query_start(struct r300_context *r300, unsigned size, void*state)
OUT_CS_REG(R300_ZB_ZPASS_DATA, 0);
END_CS;
query->begin_emitted = TRUE;
query->flushed = FALSE;
}
static void r300_emit_query_end_frag_pipes(struct r300_context *r300,
struct r300_query *query)
{
struct r300_capabilities* caps = &r300->screen->caps;
struct r300_winsys_buffer *buf = r300->query_current->buffer;
CS_LOCALS(r300);
assert(caps->num_frag_pipes);
@ -378,28 +379,28 @@ static void r300_emit_query_end_frag_pipes(struct r300_context *r300,
/* pipe 3 only */
OUT_CS_REG(R300_SU_REG_DEST, 1 << 3);
OUT_CS_REG_SEQ(R300_ZB_ZPASS_ADDR, 1);
OUT_CS_BUF_RELOC(r300->oqbo, query->offset + (sizeof(uint32_t) * 3),
0, r300_buffer(r300->oqbo)->domain, 0);
OUT_CS_RELOC(buf, (query->num_results + 3) * 4,
0, query->domain, 0);
case 3:
/* pipe 2 only */
OUT_CS_REG(R300_SU_REG_DEST, 1 << 2);
OUT_CS_REG_SEQ(R300_ZB_ZPASS_ADDR, 1);
OUT_CS_BUF_RELOC(r300->oqbo, query->offset + (sizeof(uint32_t) * 2),
0, r300_buffer(r300->oqbo)->domain, 0);
OUT_CS_RELOC(buf, (query->num_results + 2) * 4,
0, query->domain, 0);
case 2:
/* pipe 1 only */
/* As mentioned above, accomodate RV380 and older. */
OUT_CS_REG(R300_SU_REG_DEST,
1 << (caps->high_second_pipe ? 3 : 1));
OUT_CS_REG_SEQ(R300_ZB_ZPASS_ADDR, 1);
OUT_CS_BUF_RELOC(r300->oqbo, query->offset + (sizeof(uint32_t) * 1),
0, r300_buffer(r300->oqbo)->domain, 0);
OUT_CS_RELOC(buf, (query->num_results + 1) * 4,
0, query->domain, 0);
case 1:
/* pipe 0 only */
OUT_CS_REG(R300_SU_REG_DEST, 1 << 0);
OUT_CS_REG_SEQ(R300_ZB_ZPASS_ADDR, 1);
OUT_CS_BUF_RELOC(r300->oqbo, query->offset + (sizeof(uint32_t) * 0),
0, r300_buffer(r300->oqbo)->domain, 0);
OUT_CS_RELOC(buf, (query->num_results + 0) * 4,
0, query->domain, 0);
break;
default:
fprintf(stderr, "r300: Implementation error: Chipset reports %d"
@ -415,12 +416,13 @@ static void r300_emit_query_end_frag_pipes(struct r300_context *r300,
static void rv530_emit_query_end_single_z(struct r300_context *r300,
struct r300_query *query)
{
struct r300_winsys_buffer *buf = r300->query_current->buffer;
CS_LOCALS(r300);
BEGIN_CS(8);
OUT_CS_REG(RV530_FG_ZBREG_DEST, RV530_FG_ZBREG_DEST_PIPE_SELECT_0);
OUT_CS_REG_SEQ(R300_ZB_ZPASS_ADDR, 1);
OUT_CS_BUF_RELOC(r300->oqbo, query->offset, 0, r300_buffer(r300->oqbo)->domain, 0);
OUT_CS_RELOC(buf, query->num_results * 4, 0, query->domain, 0);
OUT_CS_REG(RV530_FG_ZBREG_DEST, RV530_FG_ZBREG_DEST_PIPE_SELECT_ALL);
END_CS;
}
@ -428,15 +430,16 @@ static void rv530_emit_query_end_single_z(struct r300_context *r300,
static void rv530_emit_query_end_double_z(struct r300_context *r300,
struct r300_query *query)
{
struct r300_winsys_buffer *buf = r300->query_current->buffer;
CS_LOCALS(r300);
BEGIN_CS(14);
OUT_CS_REG(RV530_FG_ZBREG_DEST, RV530_FG_ZBREG_DEST_PIPE_SELECT_0);
OUT_CS_REG_SEQ(R300_ZB_ZPASS_ADDR, 1);
OUT_CS_BUF_RELOC(r300->oqbo, query->offset, 0, r300_buffer(r300->oqbo)->domain, 0);
OUT_CS_RELOC(buf, (query->num_results + 0) * 4, 0, query->domain, 0);
OUT_CS_REG(RV530_FG_ZBREG_DEST, RV530_FG_ZBREG_DEST_PIPE_SELECT_1);
OUT_CS_REG_SEQ(R300_ZB_ZPASS_ADDR, 1);
OUT_CS_BUF_RELOC(r300->oqbo, query->offset + sizeof(uint32_t), 0, r300_buffer(r300->oqbo)->domain, 0);
OUT_CS_RELOC(buf, (query->num_results + 1) * 4, 0, query->domain, 0);
OUT_CS_REG(RV530_FG_ZBREG_DEST, RV530_FG_ZBREG_DEST_PIPE_SELECT_ALL);
END_CS;
}
@ -461,6 +464,13 @@ void r300_emit_query_end(struct r300_context* r300)
r300_emit_query_end_frag_pipes(r300, query);
query->begin_emitted = FALSE;
query->num_results += query->num_pipes;
/* XXX grab all the results and reset the counter. */
if (query->num_results >= query->buffer_size / 4 - 4) {
query->num_results = (query->buffer_size / 4) / 2;
fprintf(stderr, "r300: Rewinding OQBO...\n");
}
}
void r300_emit_rs_state(struct r300_context* r300, unsigned size, void* state)
@ -895,10 +905,9 @@ validate:
}
}
/* ...occlusion query buffer... */
if (r300->query_start.dirty ||
(r300->query_current && r300->query_current->begin_emitted)) {
if (!r300_add_buffer(r300->rws, r300->oqbo,
0, r300_buffer(r300->oqbo)->domain)) {
if (r300->query_current) {
if (!r300->rws->add_buffer(r300->rws, r300->query_current->buffer,
0, r300->query_current->domain)) {
r300->context.flush(&r300->context, 0, NULL);
goto validate;
}

View file

@ -26,6 +26,7 @@
#include "r300_context.h"
#include "r300_screen.h"
#include "r300_emit.h"
#include "r300_winsys.h"
#include <stdio.h>
@ -34,30 +35,27 @@ static struct pipe_query *r300_create_query(struct pipe_context *pipe,
{
struct r300_context *r300 = r300_context(pipe);
struct r300_screen *r300screen = r300->screen;
unsigned query_size;
struct r300_query *q, *qptr;
struct r300_query *q;
assert(query_type == PIPE_QUERY_OCCLUSION_COUNTER);
q = CALLOC_STRUCT(r300_query);
if (!q)
return NULL;
q->type = query_type;
assert(q->type == PIPE_QUERY_OCCLUSION_COUNTER);
q->domain = R300_DOMAIN_GTT;
q->buffer_size = 4096;
if (r300screen->caps.family == CHIP_FAMILY_RV530)
query_size = r300screen->caps.num_z_pipes * sizeof(uint32_t);
q->num_pipes = r300screen->caps.num_z_pipes;
else
query_size = r300screen->caps.num_frag_pipes * sizeof(uint32_t);
q->num_pipes = r300screen->caps.num_frag_pipes;
if (!is_empty_list(&r300->query_list)) {
qptr = last_elem(&r300->query_list);
q->offset = qptr->offset + query_size;
}
insert_at_tail(&r300->query_list, q);
/* XXX */
if (q->offset >= 4096) {
q->offset = 0;
fprintf(stderr, "r300: Rewinding OQBO...\n");
}
/* Open up the occlusion query buffer. */
q->buffer = r300->rws->buffer_create(r300->rws, 4096, 0, q->domain, q->buffer_size);
return (struct pipe_query*)q;
}
@ -65,18 +63,26 @@ static struct pipe_query *r300_create_query(struct pipe_context *pipe,
static void r300_destroy_query(struct pipe_context* pipe,
struct pipe_query* query)
{
struct r300_query* q = (struct r300_query*)query;
struct r300_context *r300 = r300_context(pipe);
struct r300_query* q = r300_query(query);
r300->rws->buffer_reference(r300->rws, &q->buffer, NULL);
remove_from_list(q);
FREE(query);
}
void r300_resume_query(struct r300_context *r300,
struct r300_query *query)
{
r300->query_current = query;
r300->query_start.dirty = TRUE;
}
static void r300_begin_query(struct pipe_context* pipe,
struct pipe_query* query)
{
uint32_t value = ~0U;
struct r300_context* r300 = r300_context(pipe);
struct r300_query* q = (struct r300_query*)query;
struct r300_query* q = r300_query(query);
if (r300->query_current != NULL) {
fprintf(stderr, "r300: begin_query: "
@ -85,30 +91,29 @@ static void r300_begin_query(struct pipe_context* pipe,
return;
}
pipe_buffer_write(pipe,
r300->oqbo,
q->offset,
sizeof value,
&value);
q->num_results = 0;
r300_resume_query(r300, q);
}
q->flushed = FALSE;
r300->query_current = q;
r300->query_start.dirty = TRUE;
void r300_stop_query(struct r300_context *r300)
{
r300_emit_query_end(r300);
r300->query_current = NULL;
}
static void r300_end_query(struct pipe_context* pipe,
struct pipe_query* query)
{
struct r300_context* r300 = r300_context(pipe);
struct r300_query *q = r300_query(query);
if ((struct r300_query*)query != r300->query_current) {
if (q != r300->query_current) {
fprintf(stderr, "r300: end_query: Got invalid query.\n");
assert(0);
return;
}
r300_emit_query_end(r300);
r300->query_current = NULL;
r300_stop_query(r300);
}
static boolean r300_get_query_result(struct pipe_context* pipe,
@ -117,54 +122,28 @@ static boolean r300_get_query_result(struct pipe_context* pipe,
void* vresult)
{
struct r300_context* r300 = r300_context(pipe);
struct r300_screen* r300screen = r300->screen;
struct r300_query *q = (struct r300_query*)query;
struct pipe_transfer *transfer;
unsigned flags = PIPE_TRANSFER_READ;
uint32_t* map;
uint32_t temp = 0;
unsigned i, num_results;
struct r300_query *q = r300_query(query);
unsigned flags, i;
uint32_t temp, *map;
uint64_t *result = (uint64_t*)vresult;
if (q->flushed == FALSE)
if (!q->flushed)
pipe->flush(pipe, 0, NULL);
if (!wait) {
flags |= PIPE_TRANSFER_DONTBLOCK;
}
map = pipe_buffer_map(pipe, r300->oqbo, flags, &transfer);
flags = PIPE_TRANSFER_READ | (!wait ? PIPE_TRANSFER_DONTBLOCK : 0);
map = r300->rws->buffer_map(r300->rws, q->buffer, flags);
if (!map)
return FALSE;
map += q->offset / 4;
if (r300screen->caps.family == CHIP_FAMILY_RV530)
num_results = r300screen->caps.num_z_pipes;
else
num_results = r300screen->caps.num_frag_pipes;
for (i = 0; i < num_results; i++) {
if (*map == ~0U) {
/* Looks like our results aren't ready yet. */
if (wait) {
fprintf(stderr, "r300: Despite waiting, OQ results haven't "
"come in yet. This is a driver bug.\n"
"r300: Returning bogus results to avoid "
"a possible infinite loop...\n");
temp = 987654321;
} else {
temp = ~0U;
}
break;
}
/* Sum up the results. */
temp = 0;
for (i = 0; i < q->num_results; i++) {
temp += *map;
map++;
}
pipe_buffer_unmap(pipe, r300->oqbo, transfer);
if (temp == ~0U) {
/* Our results haven't been written yet... */
return FALSE;
}
r300->rws->buffer_unmap(r300->rws, q->buffer);
*result = temp;
return TRUE;

View file

@ -264,10 +264,6 @@ struct pipe_resource *r300_buffer_create(struct pipe_screen *screen,
rbuf->b.vtbl = &r300_buffer_vtbl;
pipe_reference_init(&rbuf->b.b.reference, 1);
rbuf->b.b.screen = screen;
if (rbuf->b.b.bind & R300_BIND_OQBO)
alignment = 4096;
rbuf->domain = R300_DOMAIN_GTT;
rbuf->buf = r300screen->rws->buffer_create(r300screen->rws,