From faf23ce917c15bc36d10f76512789edc73893a5e Mon Sep 17 00:00:00 2001 From: Gert Wollny Date: Thu, 7 Mar 2019 14:28:49 +0100 Subject: [PATCH] vrend,shader: Inject a TC shader if it is missing on GLES On D-GL a program may contain a TE shader but no TC shader. On GLES either both or none of TES and TCS need to be available. So if the guest sends a shader program without a TCS, inject a passthrough shader using the patch parameters given in the GL code. Closes #84 Signed-off-by: Gert Wollny Reviewed-by: Gurchetan Singh --- src/vrend_renderer.c | 78 +++++++++++++--- src/vrend_shader.c | 208 ++++++++++++++++++++++++++++++++++++++++++- src/vrend_shader.h | 9 ++ 3 files changed, 280 insertions(+), 15 deletions(-) diff --git a/src/vrend_renderer.c b/src/vrend_renderer.c index 1c22551..f590a96 100644 --- a/src/vrend_renderer.c +++ b/src/vrend_renderer.c @@ -271,6 +271,9 @@ struct global_renderer_state { pipe_thread sync_thread; virgl_gl_context sync_context; + + /* Needed on GLES to inject a TCS */ + float tess_factors[6]; }; static struct global_renderer_state vrend_state; @@ -2993,7 +2996,7 @@ static inline void vrend_fill_shader_key(struct vrend_context *ctx, if (!ctx->shader_cfg.use_gles) next_type = PIPE_SHADER_TESS_EVAL; else - report_context_error(ctx, VIRGL_ERROR_CTX_GLES_HAVE_TES_BUT_MISS_TCS, 0); + next_type = PIPE_SHADER_TESS_CTRL; } else next_type = PIPE_SHADER_FRAGMENT; break; @@ -3037,20 +3040,23 @@ static int vrend_shader_create(struct vrend_context *ctx, struct vrend_shader_key key) { - if (!shader->sel->tokens) { - report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_SHADER, 0); - return -1; - } - shader->id = glCreateShader(conv_shader_type(shader->sel->type)); shader->compiled_fs_id = 0; - bool ret = vrend_convert_shader(ctx, &ctx->shader_cfg, shader->sel->tokens, - shader->sel->req_local_mem, &key, &shader->sel->sinfo, &shader->glsl_strings); - if (!ret) { - report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_SHADER, 0); + + if (shader->sel->tokens) { + bool ret = vrend_convert_shader(ctx, &ctx->shader_cfg, shader->sel->tokens, + shader->sel->req_local_mem, &key, &shader->sel->sinfo, &shader->glsl_strings); + if (!ret) { + report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_SHADER, shader->sel->type); + glDeleteShader(shader->id); + return -1; + } + } else if (!ctx->shader_cfg.use_gles && shader->sel->type != TGSI_PROCESSOR_TESS_CTRL) { + report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_SHADER, shader->sel->type); glDeleteShader(shader->id); return -1; } + shader->key = key; if (1) {//shader->sel->type == PIPE_SHADER_FRAGMENT || shader->sel->type == PIPE_SHADER_GEOMETRY) { bool ret; @@ -3967,6 +3973,38 @@ static void vrend_draw_bind_objects(struct vrend_context *ctx, bool new_program) } } +static +void vrend_inject_tcs(struct vrend_context *ctx, int vertices_per_patch) +{ + struct pipe_stream_output_info so_info; + + memset(&so_info, 0, sizeof(so_info)); + struct vrend_shader_selector *sel = vrend_create_shader_state(ctx, + &so_info, + false, PIPE_SHADER_TESS_CTRL); + struct vrend_shader *shader; + shader = CALLOC_STRUCT(vrend_shader); + vrend_fill_shader_key(ctx, sel->type, &shader->key); + + shader->sel = sel; + list_inithead(&shader->programs); + strarray_alloc(&shader->glsl_strings, SHADER_MAX_STRINGS); + + vrend_shader_create_passthrough_tcs(ctx, &ctx->shader_cfg, + ctx->sub->shaders[PIPE_SHADER_VERTEX]->tokens, + &shader->key, vrend_state.tess_factors, &sel->sinfo, + &shader->glsl_strings, vertices_per_patch); + // Need to add inject the selected shader to the shader selector and then the code below + // can continue + sel->tokens = NULL; + sel->current = shader; + ctx->sub->shaders[PIPE_SHADER_TESS_CTRL] = sel; + ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]->num_shaders = 1; + + shader->id = glCreateShader(conv_shader_type(shader->sel->type)); + vrend_compile_shader(ctx, shader); +} + int vrend_draw_vbo(struct vrend_context *ctx, const struct pipe_draw_info *info, uint32_t cso, uint32_t indirect_handle, @@ -4040,8 +4078,16 @@ int vrend_draw_vbo(struct vrend_context *ctx, } vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_VERTEX], &vs_dirty); - if (ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]) + + if (ctx->sub->shaders[PIPE_SHADER_TESS_CTRL] && ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]->tokens) vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_TESS_CTRL], &tcs_dirty); + else if (vrend_state.use_gles && ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]) { + VREND_DEBUG(dbg_shader, ctx, "Need to inject a TCS\n"); + vrend_inject_tcs(ctx, info->vertices_per_patch); + + vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_VERTEX], &vs_dirty); + } + if (ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]) vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_TESS_EVAL], &tes_dirty); if (ctx->sub->shaders[PIPE_SHADER_GEOMETRY]) @@ -7092,9 +7138,13 @@ void vrend_set_min_samples(struct vrend_context *ctx, unsigned min_samples) void vrend_set_tess_state(UNUSED struct vrend_context *ctx, const float tess_factors[6]) { - if (has_feature(feat_tessellation) && !vrend_state.use_gles) { - glPatchParameterfv(GL_PATCH_DEFAULT_OUTER_LEVEL, tess_factors); - glPatchParameterfv(GL_PATCH_DEFAULT_INNER_LEVEL, &tess_factors[4]); + if (has_feature(feat_tessellation)) { + if (!vrend_state.use_gles) { + glPatchParameterfv(GL_PATCH_DEFAULT_OUTER_LEVEL, tess_factors); + glPatchParameterfv(GL_PATCH_DEFAULT_INNER_LEVEL, &tess_factors[4]); + } else { + memcpy(vrend_state.tess_factors, tess_factors, 6 * sizeof (float)); + } } } diff --git a/src/vrend_shader.c b/src/vrend_shader.c index a3bb0df..a17df8f 100644 --- a/src/vrend_shader.c +++ b/src/vrend_shader.c @@ -6298,7 +6298,7 @@ static bool allocate_strbuffers(struct dump_ctx* ctx) return true; } -static bool set_strbuffers(struct vrend_context *rctx, struct dump_ctx* ctx, +static void set_strbuffers(struct vrend_context *rctx, struct dump_ctx* ctx, struct vrend_strarray *shader) { strarray_addstrbuf(shader, &ctx->glsl_ver_ext); @@ -6512,3 +6512,209 @@ bool vrend_patch_vertex_shader_interpolants(struct vrend_context *rctx, return true; } + +static boolean +iter_vs_declaration(struct tgsi_iterate_context *iter, + struct tgsi_full_declaration *decl) +{ + struct dump_ctx *ctx = (struct dump_ctx *)iter; + + const char *shader_in_prefix = "vso"; + const char *shader_out_prefix = "tco"; + const char *name_prefix = ""; + unsigned i; + unsigned mask_temp; + + // Generate a shader that passes through all VS outputs + if (decl->Declaration.File == TGSI_FILE_OUTPUT) { + for (uint32_t j = 0; j < ctx->num_inputs; j++) { + if (ctx->inputs[j].name == decl->Semantic.Name && + ctx->inputs[j].sid == decl->Semantic.Index && + ctx->inputs[j].first == decl->Range.First && + ctx->inputs[j].usage_mask == decl->Declaration.UsageMask && + ((!decl->Declaration.Array && ctx->inputs[j].array_id == 0) || + (ctx->inputs[j].array_id == decl->Array.ArrayID))) + return true; + } + i = ctx->num_inputs++; + + ctx->inputs[i].name = decl->Semantic.Name; + ctx->inputs[i].sid = decl->Semantic.Index; + ctx->inputs[i].interpolate = decl->Interp.Interpolate; + ctx->inputs[i].location = decl->Interp.Location; + ctx->inputs[i].first = decl->Range.First; + ctx->inputs[i].layout_location = 0; + ctx->inputs[i].last = decl->Range.Last; + ctx->inputs[i].array_id = decl->Declaration.Array ? decl->Array.ArrayID : 0; + ctx->inputs[i].usage_mask = mask_temp = decl->Declaration.UsageMask; + u_bit_scan_consecutive_range(&mask_temp, &ctx->inputs[i].swizzle_offset, &ctx->inputs[i].num_components); + + ctx->inputs[i].glsl_predefined_no_emit = false; + ctx->inputs[i].glsl_no_index = false; + ctx->inputs[i].override_no_wm = ctx->inputs[i].num_components == 1; + ctx->inputs[i].glsl_gl_block = false; + + switch (ctx->inputs[i].name) { + case TGSI_SEMANTIC_PSIZE: + name_prefix = "gl_PointSize"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->inputs[i].override_no_wm = true; + ctx->inputs[i].glsl_gl_block = true; + ctx->shader_req_bits |= SHADER_REQ_PSIZE; + break; + + case TGSI_SEMANTIC_CLIPDIST: + name_prefix = "gl_ClipDistance"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->inputs[i].glsl_gl_block = true; + ctx->num_in_clip_dist += 4 * (ctx->inputs[i].last - ctx->inputs[i].first + 1); + ctx->shader_req_bits |= SHADER_REQ_CLIP_DISTANCE; + if (ctx->inputs[i].last != ctx->inputs[i].first) + ctx->guest_sent_io_arrays = true; + break; + + case TGSI_SEMANTIC_POSITION: + name_prefix = "gl_Position"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->inputs[i].glsl_gl_block = true; + break; + + case TGSI_SEMANTIC_PATCH: + case TGSI_SEMANTIC_GENERIC: + if (ctx->inputs[i].first != ctx->inputs[i].last || + ctx->inputs[i].array_id > 0) { + ctx->guest_sent_io_arrays = true; + if (!ctx->cfg->use_gles) + ctx->shader_req_bits |= SHADER_REQ_ARRAYS_OF_ARRAYS; + } + break; + default: + break; + } + + memcpy(&ctx->outputs[i], &ctx->inputs[i], sizeof(struct vrend_shader_io)); + + if (ctx->inputs[i].glsl_no_index) { + snprintf(ctx->inputs[i].glsl_name, 128, "%s", name_prefix); + snprintf(ctx->outputs[i].glsl_name, 128, "%s", name_prefix); + } else { + if (ctx->inputs[i].name == TGSI_SEMANTIC_FOG){ + ctx->inputs[i].usage_mask = 0xf; + ctx->inputs[i].num_components = 4; + ctx->inputs[i].swizzle_offset = 0; + ctx->inputs[i].override_no_wm = false; + snprintf(ctx->inputs[i].glsl_name, 64, "%s_f%d", shader_in_prefix, ctx->inputs[i].sid); + snprintf(ctx->outputs[i].glsl_name, 64, "%s_f%d", shader_out_prefix, ctx->inputs[i].sid); + } else if (ctx->inputs[i].name == TGSI_SEMANTIC_COLOR) { + snprintf(ctx->inputs[i].glsl_name, 64, "%s_c%d", shader_in_prefix, ctx->inputs[i].sid); + snprintf(ctx->outputs[i].glsl_name, 64, "%s_c%d", shader_out_prefix, ctx->inputs[i].sid); + } else if (ctx->inputs[i].name == TGSI_SEMANTIC_GENERIC) { + snprintf(ctx->inputs[i].glsl_name, 64, "%s_g%dA%d_%x", + shader_in_prefix, ctx->inputs[i].sid, + ctx->inputs[i].array_id, ctx->inputs[i].usage_mask); + snprintf(ctx->outputs[i].glsl_name, 64, "%s_g%dA%d_%x", + shader_out_prefix, ctx->inputs[i].sid, + ctx->inputs[i].array_id, ctx->inputs[i].usage_mask); + } else if (ctx->inputs[i].name == TGSI_SEMANTIC_PATCH) { + snprintf(ctx->inputs[i].glsl_name, 64, "%s_p%dA%d_%x", + shader_in_prefix, ctx->inputs[i].sid, + ctx->inputs[i].array_id, ctx->inputs[i].usage_mask); + snprintf(ctx->outputs[i].glsl_name, 64, "%s_p%dA%d_%x", + shader_out_prefix, ctx->inputs[i].sid, + ctx->inputs[i].array_id, ctx->inputs[i].usage_mask); + } else { + snprintf(ctx->outputs[i].glsl_name, 64, "%s_%d", shader_in_prefix, ctx->inputs[i].first); + snprintf(ctx->inputs[i].glsl_name, 64, "%s_%d", shader_out_prefix, ctx->inputs[i].first); + } + } + } + return true; +} + +bool vrend_shader_create_passthrough_tcs(struct vrend_context *rctx, + struct vrend_shader_cfg *cfg, + struct tgsi_token *vs_tokens, + struct vrend_shader_key *key, + const float tess_factors[6], + struct vrend_shader_info *sinfo, + struct vrend_strarray *shader, + int vertices_per_patch) +{ + struct dump_ctx ctx; + + memset(&ctx, 0, sizeof(struct dump_ctx)); + + ctx.prog_type = TGSI_PROCESSOR_TESS_CTRL; + ctx.cfg = cfg; + ctx.key = key; + ctx.iter.iterate_declaration = iter_vs_declaration; + ctx.ssbo_array_base = 0xffffffff; + ctx.ssbo_atomic_array_base = 0xffffffff; + ctx.has_sample_input = false; + + if (!allocate_strbuffers(&ctx)) + goto fail; + + tgsi_iterate_shader(vs_tokens, &ctx.iter); + + /* What is the default on GL? */ + ctx.tcs_vertices_out = vertices_per_patch; + + ctx.num_outputs = ctx.num_inputs; + + handle_io_arrays(&ctx); + + emit_header(&ctx); + emit_ios(&ctx); + + emit_buf(&ctx, "void main() {\n"); + + for (unsigned int i = 0; i < ctx.num_inputs; ++i) { + const char *out_prefix = ""; + const char *in_prefix = ""; + + const char *postfix = ""; + + if (ctx.inputs[i].glsl_gl_block) { + out_prefix = "gl_out[gl_InvocationID]."; + in_prefix = "gl_in[gl_InvocationID]."; + } else { + postfix = "[gl_InvocationID]"; + } + + if (ctx.inputs[i].first == ctx.inputs[i].last) { + emit_buff(&ctx, "%s%s%s = %s%s%s;\n", + out_prefix, ctx.outputs[i].glsl_name, postfix, + in_prefix, ctx.inputs[i].glsl_name, postfix); + } else { + unsigned size = ctx.inputs[i].last == ctx.inputs[i].first + 1; + for (unsigned int k = 0; k < size; ++k) { + emit_buff(&ctx, "%s%s%s[%d] = %s%s%s[%d];\n", + out_prefix, ctx.outputs[i].glsl_name, postfix, k, + in_prefix, ctx.inputs[i].glsl_name, postfix, k); + } + } + } + + for (int i = 0; i < 4; ++i) + emit_buff(&ctx, "gl_TessLevelOuter[%d] = %f;\n", i, tess_factors[i]); + + for (int i = 0; i < 2; ++i) + emit_buff(&ctx, "gl_TessLevelInner[%d] = %f;\n", i, tess_factors[i + 4]); + + emit_buf(&ctx, "}\n"); + + fill_sinfo(&ctx, sinfo); + set_strbuffers(rctx, &ctx, shader); + return true; +fail: + strbuf_free(&ctx.glsl_main); + strbuf_free(&ctx.glsl_hdr); + strbuf_free(&ctx.glsl_ver_ext); + free(ctx.so_names); + free(ctx.temp_ranges); + return false; +} \ No newline at end of file diff --git a/src/vrend_shader.h b/src/vrend_shader.h index f286498..53fe737 100644 --- a/src/vrend_shader.h +++ b/src/vrend_shader.h @@ -157,4 +157,13 @@ const char *vrend_shader_samplertypeconv(bool use_gles, int sampler_type, int *i char vrend_shader_samplerreturnconv(enum tgsi_return_type type); int shader_lookup_sampler_array(struct vrend_shader_info *sinfo, int index); + +bool vrend_shader_create_passthrough_tcs(struct vrend_context *ctx, + struct vrend_shader_cfg *cfg, + struct tgsi_token *vs_info, + struct vrend_shader_key *key, + const float tess_factors[6], + struct vrend_shader_info *sinfo, + struct vrend_strarray *shader, + int vertices_per_patch); #endif