1 /*
2 * Copyright 2021 Google LLC
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "render_virgl.h"
7
8 #include "virglrenderer.h"
9
10 #include "render_context.h"
11
12 struct render_virgl render_virgl_internal = {
13 #ifdef ENABLE_RENDER_SERVER_WORKER_THREAD
14 .struct_mutex = _MTX_INITIALIZER_NP,
15 .dispatch_mutex = _MTX_INITIALIZER_NP,
16 #endif
17 .init_count = 0,
18 };
19
20 static struct render_virgl *
render_virgl_lock_struct(void)21 render_virgl_lock_struct(void)
22 {
23 #ifdef ENABLE_RENDER_SERVER_WORKER_THREAD
24 mtx_lock(&render_virgl_internal.struct_mutex);
25 #endif
26 return &render_virgl_internal;
27 }
28
29 static void
render_virgl_unlock_struct(void)30 render_virgl_unlock_struct(void)
31 {
32 #ifdef ENABLE_RENDER_SERVER_WORKER_THREAD
33 mtx_unlock(&render_virgl_internal.struct_mutex);
34 #endif
35 }
36
37 static struct render_context *
render_virgl_lookup_context(uint32_t ctx_id)38 render_virgl_lookup_context(uint32_t ctx_id)
39 {
40 const struct render_virgl *virgl = render_virgl_lock_struct();
41 struct render_context *ctx = NULL;
42
43 #ifdef ENABLE_RENDER_SERVER_WORKER_THREAD
44 list_for_each_entry (struct render_context, iter, &virgl->contexts, head) {
45 if (iter->ctx_id == ctx_id) {
46 ctx = iter;
47 break;
48 }
49 }
50 #else
51 assert(list_is_singular(&virgl->contexts));
52 ctx = list_first_entry(&virgl->contexts, struct render_context, head);
53 assert(ctx->ctx_id == ctx_id);
54 (void)ctx_id;
55 #endif
56
57 render_virgl_unlock_struct();
58
59 return ctx;
60 }
61
62 static void
render_virgl_debug_callback(const char * fmt,va_list ap)63 render_virgl_debug_callback(const char *fmt, va_list ap)
64 {
65 char buf[1024];
66 vsnprintf(buf, sizeof(buf), fmt, ap);
67 render_log(buf);
68 }
69
70 static void
render_virgl_cb_write_context_fence(UNUSED void * cookie,uint32_t ctx_id,uint32_t ring_idx,uint64_t fence_id)71 render_virgl_cb_write_context_fence(UNUSED void *cookie,
72 uint32_t ctx_id,
73 uint32_t ring_idx,
74 uint64_t fence_id)
75 {
76 struct render_context *ctx = render_virgl_lookup_context(ctx_id);
77 assert(ctx);
78
79 const uint32_t seqno = (uint32_t)fence_id;
80 render_context_update_timeline(ctx, ring_idx, seqno);
81 }
82
83 static const struct virgl_renderer_callbacks render_virgl_cbs = {
84 .version = VIRGL_RENDERER_CALLBACKS_VERSION,
85 .write_context_fence = render_virgl_cb_write_context_fence,
86 };
87
88 void
render_virgl_add_context(struct render_context * ctx)89 render_virgl_add_context(struct render_context *ctx)
90 {
91 struct render_virgl *virgl = render_virgl_lock_struct();
92 list_addtail(&ctx->head, &virgl->contexts);
93 render_virgl_unlock_struct();
94 }
95
96 void
render_virgl_remove_context(struct render_context * ctx)97 render_virgl_remove_context(struct render_context *ctx)
98 {
99 render_virgl_lock_struct();
100 list_del(&ctx->head);
101 render_virgl_unlock_struct();
102 }
103
104 void
render_virgl_fini(void)105 render_virgl_fini(void)
106 {
107 struct render_virgl *virgl = render_virgl_lock_struct();
108
109 if (virgl->init_count) {
110 virgl->init_count--;
111 if (!virgl->init_count) {
112 render_virgl_lock_dispatch();
113 virgl_renderer_cleanup(virgl);
114 render_virgl_unlock_dispatch();
115 }
116 }
117
118 render_virgl_unlock_struct();
119 }
120
121 bool
render_virgl_init(uint32_t init_flags)122 render_virgl_init(uint32_t init_flags)
123 {
124 /* we only care if virgl and/or venus are enabled */
125 init_flags &= VIRGL_RENDERER_VENUS | VIRGL_RENDERER_NO_VIRGL;
126
127 /* always use sync thread and async fence cb for low latency */
128 init_flags |= VIRGL_RENDERER_THREAD_SYNC | VIRGL_RENDERER_ASYNC_FENCE_CB |
129 VIRGL_RENDERER_USE_EXTERNAL_BLOB;
130
131 struct render_virgl *virgl = render_virgl_lock_struct();
132
133 if (virgl->init_count) {
134 if (virgl->init_flags != init_flags) {
135 render_log("failed to re-initialize with flags 0x%x", init_flags);
136 goto fail;
137 }
138 } else {
139 render_virgl_lock_dispatch();
140 virgl_set_debug_callback(render_virgl_debug_callback);
141 int ret = virgl_renderer_init(virgl, init_flags,
142 (struct virgl_renderer_callbacks *)&render_virgl_cbs);
143 render_virgl_unlock_dispatch();
144 if (ret) {
145 render_log("failed to initialize virglrenderer");
146 goto fail;
147 }
148
149 list_inithead(&virgl->contexts);
150 virgl->init_flags = init_flags;
151 }
152
153 virgl->init_count++;
154
155 render_virgl_unlock_struct();
156
157 return true;
158
159 fail:
160 render_virgl_unlock_struct();
161 return false;
162 }
163