gallium/u_vbuf: handle indirect multidraws correctly and efficiently (v3)
v2: need to do MAX{start+count} instead of MAX{count} added piglit tests v3: use malloc Cc: 18.2 <mesa-stable@lists.freedesktop.org> Reviewed-by: Eric Anholt <eric@anholt.net>
This commit is contained in:
@@ -1131,6 +1131,31 @@ static void u_vbuf_set_driver_vertex_buffers(struct u_vbuf *mgr)
|
||||
mgr->dirty_real_vb_mask = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
u_vbuf_split_indexed_multidraw(struct u_vbuf *mgr, struct pipe_draw_info *info,
|
||||
unsigned *indirect_data, unsigned stride,
|
||||
unsigned draw_count)
|
||||
{
|
||||
assert(info->index_size);
|
||||
info->indirect = NULL;
|
||||
|
||||
for (unsigned i = 0; i < draw_count; i++) {
|
||||
unsigned offset = i * stride / 4;
|
||||
|
||||
info->count = indirect_data[offset + 0];
|
||||
info->instance_count = indirect_data[offset + 1];
|
||||
|
||||
if (!info->count || !info->instance_count)
|
||||
continue;
|
||||
|
||||
info->start = indirect_data[offset + 2];
|
||||
info->index_bias = indirect_data[offset + 3];
|
||||
info->start_instance = indirect_data[offset + 4];
|
||||
|
||||
u_vbuf_draw_vbo(mgr, info);
|
||||
}
|
||||
}
|
||||
|
||||
void u_vbuf_draw_vbo(struct u_vbuf *mgr, const struct pipe_draw_info *info)
|
||||
{
|
||||
struct pipe_context *pipe = mgr->pipe;
|
||||
@@ -1160,33 +1185,163 @@ void u_vbuf_draw_vbo(struct u_vbuf *mgr, const struct pipe_draw_info *info)
|
||||
|
||||
new_info = *info;
|
||||
|
||||
/* Fallback. We need to know all the parameters. */
|
||||
/* Handle indirect (multi)draws. */
|
||||
if (new_info.indirect) {
|
||||
struct pipe_transfer *transfer = NULL;
|
||||
int *data;
|
||||
const struct pipe_draw_indirect_info *indirect = new_info.indirect;
|
||||
unsigned draw_count = 0;
|
||||
|
||||
if (new_info.index_size) {
|
||||
data = pipe_buffer_map_range(pipe, new_info.indirect->buffer,
|
||||
new_info.indirect->offset, 20,
|
||||
PIPE_TRANSFER_READ, &transfer);
|
||||
new_info.index_bias = data[3];
|
||||
new_info.start_instance = data[4];
|
||||
}
|
||||
else {
|
||||
data = pipe_buffer_map_range(pipe, new_info.indirect->buffer,
|
||||
new_info.indirect->offset, 16,
|
||||
PIPE_TRANSFER_READ, &transfer);
|
||||
new_info.start_instance = data[3];
|
||||
/* Get the number of draws. */
|
||||
if (indirect->indirect_draw_count) {
|
||||
pipe_buffer_read(pipe, indirect->indirect_draw_count,
|
||||
indirect->indirect_draw_count_offset,
|
||||
4, &draw_count);
|
||||
} else {
|
||||
draw_count = indirect->draw_count;
|
||||
}
|
||||
|
||||
new_info.count = data[0];
|
||||
new_info.instance_count = data[1];
|
||||
new_info.start = data[2];
|
||||
pipe_buffer_unmap(pipe, transfer);
|
||||
new_info.indirect = NULL;
|
||||
|
||||
if (!new_info.count)
|
||||
if (!draw_count)
|
||||
return;
|
||||
|
||||
unsigned data_size = (draw_count - 1) * indirect->stride +
|
||||
(new_info.index_size ? 20 : 16);
|
||||
unsigned *data = malloc(data_size);
|
||||
if (!data)
|
||||
return; /* report an error? */
|
||||
|
||||
/* Read the used buffer range only once, because the read can be
|
||||
* uncached.
|
||||
*/
|
||||
pipe_buffer_read(pipe, indirect->buffer, indirect->offset, data_size,
|
||||
data);
|
||||
|
||||
if (info->index_size) {
|
||||
/* Indexed multidraw. */
|
||||
unsigned index_bias0 = data[3];
|
||||
bool index_bias_same = true;
|
||||
|
||||
/* If we invoke the translate path, we have to split the multidraw. */
|
||||
if (incompatible_vb_mask ||
|
||||
mgr->ve->incompatible_elem_mask) {
|
||||
u_vbuf_split_indexed_multidraw(mgr, &new_info, data,
|
||||
indirect->stride, draw_count);
|
||||
free(data);
|
||||
return;
|
||||
}
|
||||
|
||||
/* See if index_bias is the same for all draws. */
|
||||
for (unsigned i = 1; i < draw_count; i++) {
|
||||
if (data[i * indirect->stride / 4 + 3] != index_bias0) {
|
||||
index_bias_same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Split the multidraw if index_bias is different. */
|
||||
if (!index_bias_same) {
|
||||
u_vbuf_split_indexed_multidraw(mgr, &new_info, data,
|
||||
indirect->stride, draw_count);
|
||||
free(data);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If we don't need to use the translate path and index_bias is
|
||||
* the same, we can process the multidraw with the time complexity
|
||||
* equal to 1 draw call (except for the index range computation).
|
||||
* We only need to compute the index range covering all draw calls
|
||||
* of the multidraw.
|
||||
*
|
||||
* The driver will not look at these values because indirect != NULL.
|
||||
* These values determine the user buffer bounds to upload.
|
||||
*/
|
||||
new_info.index_bias = index_bias0;
|
||||
new_info.min_index = ~0u;
|
||||
new_info.max_index = 0;
|
||||
new_info.start_instance = ~0u;
|
||||
unsigned end_instance = 0;
|
||||
|
||||
struct pipe_transfer *transfer = NULL;
|
||||
const uint8_t *indices;
|
||||
|
||||
if (info->has_user_indices) {
|
||||
indices = (uint8_t*)info->index.user;
|
||||
} else {
|
||||
indices = (uint8_t*)pipe_buffer_map(pipe, info->index.resource,
|
||||
PIPE_TRANSFER_READ, &transfer);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < draw_count; i++) {
|
||||
unsigned offset = i * indirect->stride / 4;
|
||||
unsigned start = data[offset + 2];
|
||||
unsigned count = data[offset + 0];
|
||||
unsigned start_instance = data[offset + 4];
|
||||
unsigned instance_count = data[offset + 1];
|
||||
|
||||
if (!count || !instance_count)
|
||||
continue;
|
||||
|
||||
/* Update the ranges of instances. */
|
||||
new_info.start_instance = MIN2(new_info.start_instance,
|
||||
start_instance);
|
||||
end_instance = MAX2(end_instance, start_instance + instance_count);
|
||||
|
||||
/* Update the index range. */
|
||||
unsigned min, max;
|
||||
new_info.count = count; /* only used by get_minmax_index */
|
||||
u_vbuf_get_minmax_index_mapped(&new_info,
|
||||
indices +
|
||||
new_info.index_size * start,
|
||||
&min, &max);
|
||||
|
||||
new_info.min_index = MIN2(new_info.min_index, min);
|
||||
new_info.max_index = MAX2(new_info.max_index, max);
|
||||
}
|
||||
free(data);
|
||||
|
||||
if (transfer)
|
||||
pipe_buffer_unmap(pipe, transfer);
|
||||
|
||||
/* Set the final instance count. */
|
||||
new_info.instance_count = end_instance - new_info.start_instance;
|
||||
|
||||
if (new_info.start_instance == ~0u || !new_info.instance_count)
|
||||
return;
|
||||
} else {
|
||||
/* Non-indexed multidraw.
|
||||
*
|
||||
* Keep the draw call indirect and compute minimums & maximums,
|
||||
* which will determine the user buffer bounds to upload, but
|
||||
* the driver will not look at these values because indirect != NULL.
|
||||
*
|
||||
* This efficiently processes the multidraw with the time complexity
|
||||
* equal to 1 draw call.
|
||||
*/
|
||||
new_info.start = ~0u;
|
||||
new_info.start_instance = ~0u;
|
||||
unsigned end_vertex = 0;
|
||||
unsigned end_instance = 0;
|
||||
|
||||
for (unsigned i = 0; i < draw_count; i++) {
|
||||
unsigned offset = i * indirect->stride / 4;
|
||||
unsigned start = data[offset + 2];
|
||||
unsigned count = data[offset + 0];
|
||||
unsigned start_instance = data[offset + 3];
|
||||
unsigned instance_count = data[offset + 1];
|
||||
|
||||
new_info.start = MIN2(new_info.start, start);
|
||||
new_info.start_instance = MIN2(new_info.start_instance,
|
||||
start_instance);
|
||||
|
||||
end_vertex = MAX2(end_vertex, start + count);
|
||||
end_instance = MAX2(end_instance, start_instance + instance_count);
|
||||
}
|
||||
|
||||
/* Set the final counts. */
|
||||
new_info.count = end_vertex - new_info.start;
|
||||
new_info.instance_count = end_instance - new_info.start_instance;
|
||||
|
||||
if (new_info.start == ~0u || !new_info.count || !new_info.instance_count)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_info.index_size) {
|
||||
@@ -1211,7 +1366,8 @@ void u_vbuf_draw_vbo(struct u_vbuf *mgr, const struct pipe_draw_info *info)
|
||||
* We would have to break this drawing operation into several ones. */
|
||||
/* Use some heuristic to see if unrolling indices improves
|
||||
* performance. */
|
||||
if (!new_info.primitive_restart &&
|
||||
if (!info->indirect &&
|
||||
!new_info.primitive_restart &&
|
||||
num_vertices > new_info.count*2 &&
|
||||
num_vertices - new_info.count > 32 &&
|
||||
!u_vbuf_mapping_vertex_buffer_blocks(mgr)) {
|
||||
|
Reference in New Issue
Block a user