diff --git a/src/virgl_protocol.h b/src/virgl_protocol.h index c1797d9..dc1bafa 100644 --- a/src/virgl_protocol.h +++ b/src/virgl_protocol.h @@ -116,6 +116,7 @@ enum virgl_context_cmd { VIRGL_CCMD_PIPE_RESOURCE_SET_TYPE, VIRGL_CCMD_GET_MEMORY_INFO, VIRGL_CCMD_SEND_STRING_MARKER, + VIRGL_CCMD_LINK_SHADER, VIRGL_MAX_COMMANDS }; @@ -673,4 +674,13 @@ enum vrend_tweak_type { #define VIRGL_SEND_STRING_MARKER_STRING_SIZE 1 #define VIRGL_SEND_STRING_MARKER_OFFSET 2 +/* link shader program */ +#define VIRGL_LINK_SHADER_SIZE 6 +#define VIRGL_LINK_SHADER_VERTEX_HANDLE 1 +#define VIRGL_LINK_SHADER_FRAGMENT_HANDLE 2 +#define VIRGL_LINK_SHADER_GEOMETRY_HANDLE 3 +#define VIRGL_LINK_SHADER_TESS_CTRL_HANDLE 4 +#define VIRGL_LINK_SHADER_TESS_EVAL_HANDLE 5 +#define VIRGL_LINK_SHADER_COMPUTE_HANDLE 6 + #endif diff --git a/src/vrend_debug.c b/src/vrend_debug.c index a0ab7bc..8c1cd93 100644 --- a/src/vrend_debug.c +++ b/src/vrend_debug.c @@ -81,6 +81,7 @@ static const char *command_names[VIRGL_MAX_COMMANDS] = { "PIPE_RESOURCE_SET_TYPE", "GET_MEMORY_INFO", "SEND_STRING_MARKER", + "LINK_SHADER", }; static const char *object_type_names[VIRGL_MAX_OBJECTS] = { diff --git a/src/vrend_decode.c b/src/vrend_decode.c index 934c8b0..a55d542 100644 --- a/src/vrend_decode.c +++ b/src/vrend_decode.c @@ -1155,6 +1155,23 @@ static int vrend_decode_destroy_sub_ctx(struct vrend_context *ctx, const uint32_ return 0; } +static int vrend_decode_link_shader(struct vrend_context *ctx, const uint32_t *buf, uint32_t length) +{ + if (length != VIRGL_LINK_SHADER_SIZE) + return EINVAL; + + uint32_t handles[PIPE_SHADER_TYPES]; + handles[PIPE_SHADER_VERTEX] = get_buf_entry(buf, VIRGL_LINK_SHADER_VERTEX_HANDLE); + handles[PIPE_SHADER_FRAGMENT] = get_buf_entry(buf, VIRGL_LINK_SHADER_FRAGMENT_HANDLE); + handles[PIPE_SHADER_GEOMETRY] = get_buf_entry(buf, VIRGL_LINK_SHADER_GEOMETRY_HANDLE); + handles[PIPE_SHADER_TESS_CTRL] = get_buf_entry(buf, VIRGL_LINK_SHADER_TESS_CTRL_HANDLE); + handles[PIPE_SHADER_TESS_EVAL] = get_buf_entry(buf, VIRGL_LINK_SHADER_TESS_EVAL_HANDLE); + handles[PIPE_SHADER_COMPUTE] = get_buf_entry(buf, VIRGL_LINK_SHADER_COMPUTE_HANDLE); + + vrend_link_program(ctx, handles); + return 0; +} + static int vrend_decode_bind_shader(struct vrend_context *ctx, const uint32_t *buf, uint32_t length) { uint32_t handle, type; @@ -1654,6 +1671,7 @@ static const vrend_decode_callback decode_table[VIRGL_MAX_COMMANDS] = { [VIRGL_CCMD_PIPE_RESOURCE_SET_TYPE] = vrend_decode_pipe_resource_set_type, [VIRGL_CCMD_GET_MEMORY_INFO] = vrend_decode_get_memory_info, [VIRGL_CCMD_SEND_STRING_MARKER] = vrend_decode_send_string_marker, + [VIRGL_CCMD_LINK_SHADER] = vrend_decode_link_shader, }; static int vrend_decode_ctx_submit_cmd(struct virgl_context *ctx, diff --git a/src/vrend_renderer.c b/src/vrend_renderer.c index a5a66b9..355a76f 100644 --- a/src/vrend_renderer.c +++ b/src/vrend_renderer.c @@ -4798,6 +4798,35 @@ vrend_select_program(struct vrend_sub_context *sub_ctx, ubyte vertices_per_patch return new_program; } +void vrend_link_program(struct vrend_context *ctx, uint32_t *handles) +{ + /* Pre-compiling compute shaders needs some additional work */ + if (handles[PIPE_SHADER_COMPUTE]) + return; + + struct vrend_shader_selector *prev_handles[PIPE_SHADER_TYPES]; + memset(prev_handles, 0, sizeof(prev_handles)); + uint32_t prev_shader_ids[PIPE_SHADER_TYPES]; + memcpy(prev_shader_ids, ctx->sub->prog_ids, PIPE_SHADER_TYPES * sizeof(uint32_t)); + struct vrend_linked_shader_program *prev_prog = ctx->sub->prog; + + for (uint32_t type = 0; type < PIPE_SHADER_TYPES; ++type) { + vrend_shader_state_reference(&prev_handles[type], ctx->sub->shaders[type]); + vrend_bind_shader(ctx, handles[type], type); + } + + ctx->sub->shader_dirty = true; + ctx->sub->cs_shader_dirty = true; + + /* undo state changes */ + for (uint32_t type = 0; type < PIPE_SHADER_TYPES; ++type) { + vrend_shader_state_reference(&ctx->sub->shaders[type], prev_handles[type]); + vrend_shader_state_reference(&prev_handles[type], NULL); + } + memcpy(ctx->sub->prog_ids, prev_shader_ids, PIPE_SHADER_TYPES * sizeof(uint32_t)); + ctx->sub->prog = prev_prog; +} + int vrend_draw_vbo(struct vrend_context *ctx, const struct pipe_draw_info *info, uint32_t cso, uint32_t indirect_handle, @@ -10536,7 +10565,7 @@ static void vrend_renderer_fill_caps_v2(int gl_ver, int gles_ver, union virgl_c * this value to avoid regressions when a guest with a new mesa version is * run on an old virgl host. Use it also to indicate non-cap fixes on the * host that help enable features in the guest. */ - caps->v2.host_feature_check_version = 6; + caps->v2.host_feature_check_version = 7; /* Forward host GL_RENDERER to the guest. */ strncpy(caps->v2.renderer, renderer, sizeof(caps->v2.renderer) - 1); diff --git a/src/vrend_renderer.h b/src/vrend_renderer.h index 69f17d8..e6f84cc 100644 --- a/src/vrend_renderer.h +++ b/src/vrend_renderer.h @@ -145,6 +145,8 @@ int vrend_create_shader(struct vrend_context *ctx, const char *shd_text, uint32_t offlen, uint32_t num_tokens, uint32_t type, uint32_t pkt_length); +void vrend_link_program(struct vrend_context *ctx, uint32_t *handles); + void vrend_bind_shader(struct vrend_context *ctx, uint32_t type, uint32_t handle); diff --git a/tests/test_virgl_cmd.c b/tests/test_virgl_cmd.c index 3c2fd24..4c8d689 100644 --- a/tests/test_virgl_cmd.c +++ b/tests/test_virgl_cmd.c @@ -372,6 +372,15 @@ START_TEST(virgl_test_render_simple) virgl_encode_bind_shader(&ctx, fs_handle, PIPE_SHADER_FRAGMENT); } + /* link shader */ + { + uint32_t handles[PIPE_SHADER_TYPES]; + memset(handles, 0, sizeof(handles)); + handles[PIPE_SHADER_VERTEX] = vs_handle; + handles[PIPE_SHADER_FRAGMENT] = fs_handle; + virgl_encode_link_shader(&ctx, handles); + } + /* set blend state */ { struct pipe_blend_state blend; diff --git a/tests/testvirgl_encode.c b/tests/testvirgl_encode.c index f44a640..38ad5e1 100644 --- a/tests/testvirgl_encode.c +++ b/tests/testvirgl_encode.c @@ -942,6 +942,18 @@ int virgl_encoder_destroy_sub_ctx(struct virgl_context *ctx, uint32_t sub_ctx_id return 0; } +int virgl_encode_link_shader(struct virgl_context *ctx, uint32_t *handles) +{ + virgl_encoder_write_cmd_dword(ctx, VIRGL_CMD0(VIRGL_CCMD_LINK_SHADER, 0, VIRGL_LINK_SHADER_SIZE)); + virgl_encoder_write_dword(ctx->cbuf, handles[PIPE_SHADER_VERTEX]); + virgl_encoder_write_dword(ctx->cbuf, handles[PIPE_SHADER_FRAGMENT]); + virgl_encoder_write_dword(ctx->cbuf, handles[PIPE_SHADER_GEOMETRY]); + virgl_encoder_write_dword(ctx->cbuf, handles[PIPE_SHADER_TESS_CTRL]); + virgl_encoder_write_dword(ctx->cbuf, handles[PIPE_SHADER_TESS_EVAL]); + virgl_encoder_write_dword(ctx->cbuf, handles[PIPE_SHADER_COMPUTE]); + return 0; +} + int virgl_encode_bind_shader(struct virgl_context *ctx, uint32_t handle, uint32_t type) { diff --git a/tests/testvirgl_encode.h b/tests/testvirgl_encode.h index c5bd9c1..cd1ab2b 100644 --- a/tests/testvirgl_encode.h +++ b/tests/testvirgl_encode.h @@ -258,6 +258,7 @@ int virgl_encoder_render_condition(struct virgl_context *ctx, int virgl_encoder_set_sub_ctx(struct virgl_context *ctx, uint32_t sub_ctx_id); int virgl_encoder_create_sub_ctx(struct virgl_context *ctx, uint32_t sub_ctx_id); int virgl_encoder_destroy_sub_ctx(struct virgl_context *ctx, uint32_t sub_ctx_id); +int virgl_encode_link_shader(struct virgl_context *ctx, uint32_t *handles); int virgl_encode_bind_shader(struct virgl_context *ctx, uint32_t handle, uint32_t type); #endif