renderer: handle transform feedback 2 and 3 extensions

This fixes a number of issues with how transform feedback works
it does requires ARB_transform_feedback3 to work at all, but
hopefully this extension is widespread enough, if not we can
revisit later.

It uses transform feedback objects to store the stream out
state.
macos/master
Dave Airlie 10 years ago
parent c89bb9c5f9
commit 9259bc768e
  1. 3
      src/virgl_protocol.h
  2. 7
      src/vrend_decode.c
  3. 228
      src/vrend_renderer.c
  4. 3
      src/vrend_renderer.h
  5. 1
      tests/testvirgl_encode.c

@ -246,7 +246,7 @@ enum virgl_context_cmd {
#define VIRGL_SET_UNIFORM_BUFFER_RES_HANDLE 5
/* draw VBO */
#define VIRGL_DRAW_VBO_SIZE 11
#define VIRGL_DRAW_VBO_SIZE 12
#define VIRGL_DRAW_VBO_START 1
#define VIRGL_DRAW_VBO_COUNT 2
#define VIRGL_DRAW_VBO_MODE 3
@ -258,6 +258,7 @@ enum virgl_context_cmd {
#define VIRGL_DRAW_VBO_RESTART_INDEX 9
#define VIRGL_DRAW_VBO_MIN_INDEX 10
#define VIRGL_DRAW_VBO_MAX_INDEX 11
#define VIRGL_DRAW_VBO_COUNT_FROM_SO 12
/* create surface */
#define VIRGL_OBJ_SURFACE_SIZE 5

@ -343,7 +343,7 @@ static int vrend_decode_resource_inline_write(struct vrend_decode_ctx *ctx, uint
static int vrend_decode_draw_vbo(struct vrend_decode_ctx *ctx, int length)
{
struct pipe_draw_info info;
uint32_t cso;
if (length != VIRGL_DRAW_VBO_SIZE)
return EINVAL;
memset(&info, 0, sizeof(struct pipe_draw_info));
@ -359,7 +359,10 @@ static int vrend_decode_draw_vbo(struct vrend_decode_ctx *ctx, int length)
info.restart_index = get_buf_entry(ctx, VIRGL_DRAW_VBO_RESTART_INDEX);
info.min_index = get_buf_entry(ctx, VIRGL_DRAW_VBO_MIN_INDEX);
info.max_index = get_buf_entry(ctx, VIRGL_DRAW_VBO_MAX_INDEX);
vrend_draw_vbo(ctx->grctx, &info);
cso = get_buf_entry(ctx, VIRGL_DRAW_VBO_COUNT_FROM_SO);
vrend_draw_vbo(ctx->grctx, &info, cso);
return 0;
}

@ -194,6 +194,7 @@ struct vrend_so_target {
unsigned buffer_offset;
unsigned buffer_size;
struct vrend_resource *buffer;
struct vrend_sub_context *sub_ctx;
};
struct vrend_sampler_view {
@ -246,6 +247,16 @@ struct vrend_viewport {
GLclampd near_val, far_val;
};
/* create a streamout object to support pause/resume */
struct vrend_streamout_object {
GLuint id;
uint32_t num_targets;
uint32_t handles[16];
struct list_head head;
int xfb_state;
struct vrend_so_target *so_targets[16];
};
#define XFB_STATE_OFF 0
#define XFB_STATE_STARTED_NEED_BEGIN 1
#define XFB_STATE_STARTED 2
@ -313,9 +324,6 @@ struct vrend_sub_context {
struct pipe_depth_stencil_alpha_state dsa_state;
struct pipe_rasterizer_state rs_state;
int num_so_targets;
struct vrend_so_target *so_targets[16];
uint8_t stencil_refs[2];
GLuint blit_fb_ids[2];
@ -333,7 +341,8 @@ struct vrend_sub_context {
struct pipe_rasterizer_state hw_rs_state;
struct pipe_blend_state hw_blend_state;
int xfb_state;
struct vrend_streamout_object *current_so;
struct list_head streamout_list;
};
struct vrend_context {
@ -650,25 +659,103 @@ static void vrend_stencil_test_enable(struct vrend_context *ctx,
}
}
static void dump_stream_out(struct pipe_stream_output_info *so)
{
int i;
if (!so)
return;
printf("streamout: %d\n", so->num_outputs);
printf("strides: ");
for (i = 0; i < 4; i++)
printf("%d ", so->stride[i]);
printf("\n");
printf("outputs:\n");
for (i = 0; i < so->num_outputs; i++) {
printf("\t%d: reg: %d sc: %d, nc: %d ob: %d do: %d\n",
i,
so->output[i].register_index,
so->output[i].start_component,
so->output[i].num_components,
so->output[i].output_buffer,
so->output[i].dst_offset);
}
}
static char *get_skip_str(int *skip_val)
{
char *start_skip = NULL;
if (*skip_val < 0) {
*skip_val = 0;
return NULL;
}
if (*skip_val == 1) {
start_skip = strdup("gl_SkipComponents1");
*skip_val -= 1;
} else if (*skip_val == 2) {
start_skip = strdup("gl_SkipComponents2");
*skip_val -= 2;
} else if (*skip_val == 3) {
start_skip = strdup("gl_SkipComponents3");
*skip_val -= 3;
} else if (*skip_val >= 4) {
start_skip = strdup("gl_SkipComponents4");
*skip_val -= 4;
}
return start_skip;
}
static void set_stream_out_varyings(int prog_id, struct vrend_shader_info *sinfo)
{
struct pipe_stream_output_info *so = &sinfo->so_info;
char *varyings[PIPE_MAX_SHADER_OUTPUTS*2];
int i;
int i, j;
int n_outputs = 0;
int last_buffer = 0;
char *start_skip;
int buf_offset = 0;
int skip;
if (!so->num_outputs)
return;
if (vrend_dump_shaders)
dump_stream_out(so);
for (i = 0; i < so->num_outputs; i++) {
if (last_buffer != so->output[i].output_buffer) {
skip = so->stride[last_buffer] - buf_offset;
while (skip) {
start_skip = get_skip_str(&skip);
if (start_skip)
varyings[n_outputs++] = start_skip;
}
for (j = last_buffer; j < so->output[i].output_buffer; j++)
varyings[n_outputs++] = strdup("gl_NextBuffer");
last_buffer = so->output[i].output_buffer;
buf_offset = 0;
}
skip = so->output[i].dst_offset - buf_offset;
while (skip) {
start_skip = get_skip_str(&skip);
if (start_skip)
varyings[n_outputs++] = start_skip;
}
buf_offset = so->output[i].dst_offset;
buf_offset += so->output[i].num_components;
if (sinfo->so_names[i])
varyings[n_outputs++] = strdup(sinfo->so_names[i]);
}
skip = so->stride[last_buffer] - buf_offset;
while (skip) {
start_skip = get_skip_str(&skip);
if (start_skip)
varyings[n_outputs++] = start_skip;
}
glTransformFeedbackVaryings(prog_id, n_outputs,
(const GLchar **)varyings, GL_INTERLEAVED_ATTRIBS_EXT);
@ -910,6 +997,16 @@ static void vrend_free_programs(struct vrend_sub_context *sub)
}
}
static void vrend_destroy_streamout_object(struct vrend_streamout_object *obj)
{
int i;
list_del(&obj->head);
for (i = 0; i < obj->num_targets; i++)
vrend_so_target_reference(&obj->so_targets[i], NULL);
glDeleteTransformFeedbacks(1, &obj->id);
FREE(obj);
}
int vrend_create_surface(struct vrend_context *ctx,
uint32_t handle,
uint32_t res_handle, uint32_t format,
@ -963,6 +1060,31 @@ static void vrend_destroy_sampler_view_object(void *obj_ptr)
static void vrend_destroy_so_target_object(void *obj_ptr)
{
struct vrend_so_target *target = obj_ptr;
struct vrend_sub_context *sub_ctx = target->sub_ctx;
struct vrend_streamout_object *obj, *tmp;
bool found;
int i;
LIST_FOR_EACH_ENTRY_SAFE(obj, tmp, &sub_ctx->streamout_list, head) {
found = false;
for (i = 0; i < obj->num_targets; i++) {
if (obj->so_targets[i] == target) {
found = true;
break;
}
}
if (found) {
if (obj == sub_ctx->current_so)
sub_ctx->current_so = NULL;
if (obj->xfb_state == XFB_STATE_PAUSED) {
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, obj->id);
glEndTransformFeedback();
if (sub_ctx->current_so)
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, sub_ctx->current_so->id);
}
vrend_destroy_streamout_object(obj);
}
}
vrend_so_target_reference(&target, NULL);
}
@ -2091,7 +2213,8 @@ static GLenum get_xfb_mode(GLenum mode)
}
void vrend_draw_vbo(struct vrend_context *ctx,
const struct pipe_draw_info *info)
const struct pipe_draw_info *info,
uint32_t cso)
{
int i;
int sampler_id;
@ -2377,15 +2500,17 @@ void vrend_draw_vbo(struct vrend_context *ctx,
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// vrend_ctx_restart_queries(ctx);
if (ctx->sub->xfb_state == XFB_STATE_STARTED_NEED_BEGIN) {
if (ctx->sub->current_so) {
if (ctx->sub->current_so->xfb_state == XFB_STATE_STARTED_NEED_BEGIN) {
if (ctx->sub->gs)
glBeginTransformFeedback(get_gs_xfb_mode(ctx->sub->gs->sinfo.gs_out_prim));
else
glBeginTransformFeedback(get_xfb_mode(info->mode));
ctx->sub->xfb_state = XFB_STATE_STARTED;
} else if (ctx->sub->xfb_state == XFB_STATE_PAUSED) {
ctx->sub->current_so->xfb_state = XFB_STATE_STARTED;
} else if (ctx->sub->current_so->xfb_state == XFB_STATE_PAUSED) {
glResumeTransformFeedback();
ctx->sub->xfb_state = XFB_STATE_STARTED;
ctx->sub->current_so->xfb_state = XFB_STATE_STARTED;
}
}
if (info->primitive_restart) {
@ -2400,12 +2525,14 @@ void vrend_draw_vbo(struct vrend_context *ctx,
/* set the vertex state up now on a delay */
if (!info->indexed) {
GLenum mode = info->mode;
int count = cso ? cso : info->count;
int start = cso ? 0 : info->start;
if (info->instance_count <= 1)
glDrawArrays(mode, info->start, info->count);
glDrawArrays(mode, start, count);
else if (info->start_instance)
glDrawArraysInstancedBaseInstance(mode, info->start, info->count, info->instance_count, info->start_instance);
glDrawArraysInstancedBaseInstance(mode, start, count, info->instance_count, info->start_instance);
else
glDrawArraysInstancedARB(mode, info->start, info->count, info->instance_count);
glDrawArraysInstancedARB(mode, start, count, info->instance_count);
} else {
GLenum elsz;
GLenum mode = info->mode;
@ -2444,9 +2571,11 @@ void vrend_draw_vbo(struct vrend_context *ctx,
glDisable(GL_PRIMITIVE_RESTART);
}
if (ctx->sub->xfb_state == XFB_STATE_STARTED) {
if (ctx->sub->current_so) {
if (ctx->sub->current_so->xfb_state == XFB_STATE_STARTED) {
glPauseTransformFeedback();
ctx->sub->xfb_state = XFB_STATE_PAUSED;
ctx->sub->current_so->xfb_state = XFB_STATE_PAUSED;
}
}
}
@ -3239,6 +3368,8 @@ vrend_renderer_fini(void)
static void vrend_destroy_sub_context(struct vrend_sub_context *sub)
{
int i;
struct vrend_streamout_object *obj, *tmp;
if (sub->fb_id)
glDeleteFramebuffers(1, &sub->fb_id);
@ -3254,6 +3385,12 @@ static void vrend_destroy_sub_context(struct vrend_sub_context *sub)
glDeleteVertexArrays(1, &sub->vaoid);
if (sub->current_so)
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
LIST_FOR_EACH_ENTRY_SAFE(obj, tmp, &sub->streamout_list, head) {
vrend_destroy_streamout_object(obj);
}
vrend_shader_state_reference(&sub->vs, NULL);
vrend_shader_state_reference(&sub->fs, NULL);
vrend_shader_state_reference(&sub->gs, NULL);
@ -4424,15 +4561,15 @@ void vrend_set_sample_mask(struct vrend_context *ctx, unsigned sample_mask)
}
static void vrend_hw_emit_streamout_targets(struct vrend_context *ctx)
static void vrend_hw_emit_streamout_targets(struct vrend_context *ctx, struct vrend_streamout_object *so_obj)
{
int i;
for (i = 0; i < ctx->sub->num_so_targets; i++) {
if (ctx->sub->so_targets[i]->buffer_offset || ctx->sub->so_targets[i]->buffer_size < ctx->sub->so_targets[i]->buffer->base.width0)
glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, i, ctx->sub->so_targets[i]->buffer->id, ctx->sub->so_targets[i]->buffer_offset, ctx->sub->so_targets[i]->buffer_size);
for (i = 0; i < so_obj->num_targets; i++) {
if (so_obj->so_targets[i]->buffer_offset || so_obj->so_targets[i]->buffer_size < so_obj->so_targets[i]->buffer->base.width0)
glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, i, so_obj->so_targets[i]->buffer->id, so_obj->so_targets[i]->buffer_offset, so_obj->so_targets[i]->buffer_size);
else
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, i, ctx->sub->so_targets[i]->buffer->id);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, i, so_obj->so_targets[i]->buffer->id);
}
}
@ -4443,36 +4580,45 @@ void vrend_set_streamout_targets(struct vrend_context *ctx,
{
struct vrend_so_target *target;
int i;
int old_num = ctx->sub->num_so_targets;
ctx->sub->num_so_targets = num_targets;
if (num_targets == 0 && (ctx->sub->xfb_state == XFB_STATE_STARTED || ctx->sub->xfb_state == XFB_STATE_PAUSED)) {
glEndTransformFeedback();
ctx->sub->xfb_state = XFB_STATE_OFF;
if (num_targets) {
bool found = false;
struct vrend_streamout_object *obj;
LIST_FOR_EACH_ENTRY(obj, &ctx->sub->streamout_list, head) {
if (obj->num_targets == num_targets) {
if (!memcmp(handles, obj->handles, num_targets * 4)) {
found = true;
break;
}
}
}
if (found) {
ctx->sub->current_so = obj;
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, obj->id);
return;
}
obj = CALLOC_STRUCT(vrend_streamout_object);
glGenTransformFeedbacks(1, &obj->id);
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, obj->id);
obj->num_targets = num_targets;
for (i = 0; i < num_targets; i++) {
obj->handles[i] = handles[i];
target = vrend_object_lookup(ctx->sub->object_hash, handles[i], VIRGL_OBJECT_STREAMOUT_TARGET);
if (!target) {
report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_HANDLE, handles[i]);
return;
}
vrend_so_target_reference(&ctx->sub->so_targets[i], target);
vrend_so_target_reference(&obj->so_targets[i], target);
}
for (i = num_targets; i < old_num; i++) {
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, i, 0);
vrend_so_target_reference(&ctx->sub->so_targets[i], NULL);
}
vrend_hw_emit_streamout_targets(ctx);
if (ctx->sub->num_so_targets) {
if (ctx->sub->xfb_state == XFB_STATE_OFF)
ctx->sub->xfb_state = XFB_STATE_STARTED_NEED_BEGIN;
vrend_hw_emit_streamout_targets(ctx, obj);
list_addtail(&obj->head, &ctx->sub->streamout_list);
ctx->sub->current_so = obj;
obj->xfb_state = XFB_STATE_STARTED_NEED_BEGIN;
} else {
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
ctx->sub->current_so = NULL;
}
}
static void vrend_resource_buffer_copy(struct vrend_context *ctx,
@ -5163,7 +5309,7 @@ int vrend_create_so_target(struct vrend_context *ctx,
target->res_handle = res_handle;
target->buffer_offset = buffer_offset;
target->buffer_size = buffer_size;
target->sub_ctx = ctx->sub;
vrend_resource_reference(&target->buffer, res);
ret_handle = vrend_renderer_object_insert(ctx, target, sizeof(*target), handle,
@ -5566,6 +5712,8 @@ void vrend_renderer_create_sub_ctx(struct vrend_context *ctx, int sub_ctx_id)
glGenFramebuffers(2, sub->blit_fb_ids);
list_inithead(&sub->programs);
list_inithead(&sub->streamout_list);
sub->object_hash = vrend_object_init_ctx_table();
vrend_bind_va(sub->vaoid);

@ -124,7 +124,8 @@ void vrend_clear(struct vrend_context *ctx,
double depth, unsigned stencil);
void vrend_draw_vbo(struct vrend_context *ctx,
const struct pipe_draw_info *info);
const struct pipe_draw_info *info,
uint32_t cso);
void vrend_set_framebuffer_state(struct vrend_context *ctx,
uint32_t nr_cbufs, uint32_t surf_handle[8],

@ -351,6 +351,7 @@ int virgl_encoder_draw_vbo(struct virgl_context *ctx,
virgl_encoder_write_dword(ctx->cbuf, info->restart_index);
virgl_encoder_write_dword(ctx->cbuf, info->min_index);
virgl_encoder_write_dword(ctx->cbuf, info->max_index);
virgl_encoder_write_dword(ctx->cbuf, 0);
return 0;
}

Loading…
Cancel
Save