• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2020, VideoLAN and dav1d authors
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this
9  *    list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  *    this list of conditions and the following disclaimer in the documentation
13  *    and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "dp_renderer.h"
28 
29 #ifdef HAVE_RENDERER_PLACEBO
30 #include <assert.h>
31 
32 #include <libplacebo/renderer.h>
33 #include <libplacebo/utils/dav1d.h>
34 
35 #ifdef HAVE_PLACEBO_VULKAN
36 # include <libplacebo/vulkan.h>
37 # include <SDL_vulkan.h>
38 #endif
39 #ifdef HAVE_PLACEBO_OPENGL
40 # include <libplacebo/opengl.h>
41 # include <SDL_opengl.h>
42 #endif
43 
44 
45 /**
46  * Renderer context for libplacebo
47  */
48 typedef struct renderer_priv_ctx
49 {
50     // SDL window
51     SDL_Window *win;
52     // Placebo log
53     pl_log log;
54     // Placebo renderer
55     pl_renderer renderer;
56 #ifdef HAVE_PLACEBO_VULKAN
57     // Placebo Vulkan handle
58     pl_vulkan vk;
59     // Placebo Vulkan instance
60     pl_vk_inst vk_inst;
61     // Vulkan surface
62     VkSurfaceKHR surf;
63 #endif
64 #ifdef HAVE_PLACEBO_OPENGL
65     // Placebo OpenGL handle
66     pl_opengl gl;
67 #endif
68     // Placebo GPU
69     pl_gpu gpu;
70     // Placebo swapchain
71     pl_swapchain swapchain;
72     // Lock protecting access to the texture
73     SDL_mutex *lock;
74     // Image to render, and planes backing them
75     struct pl_frame image;
76     pl_tex plane_tex[3];
77 } Dav1dPlayRendererPrivateContext;
78 
79 static Dav1dPlayRendererPrivateContext*
placebo_renderer_create_common(int window_flags)80     placebo_renderer_create_common(int window_flags)
81 {
82     // Create Window
83     SDL_Window *sdlwin = dp_create_sdl_window(window_flags | SDL_WINDOW_RESIZABLE);
84     if (sdlwin == NULL)
85         return NULL;
86 
87     // Alloc
88     Dav1dPlayRendererPrivateContext *const rd_priv_ctx =
89         calloc(1, sizeof(Dav1dPlayRendererPrivateContext));
90     if (rd_priv_ctx == NULL)
91         return NULL;
92 
93     rd_priv_ctx->win = sdlwin;
94 
95     // Init libplacebo
96     rd_priv_ctx->log = pl_log_create(PL_API_VER, pl_log_params(
97         .log_cb     = pl_log_color,
98 #ifndef NDEBUG
99         .log_level  = PL_LOG_DEBUG,
100 #else
101         .log_level  = PL_LOG_WARN,
102 #endif
103     ));
104     if (rd_priv_ctx->log == NULL) {
105         free(rd_priv_ctx);
106         return NULL;
107     }
108 
109     // Create Mutex
110     rd_priv_ctx->lock = SDL_CreateMutex();
111     if (rd_priv_ctx->lock == NULL) {
112         fprintf(stderr, "SDL_CreateMutex failed: %s\n", SDL_GetError());
113         pl_log_destroy(&rd_priv_ctx->log);
114         free(rd_priv_ctx);
115         return NULL;
116     }
117 
118     return rd_priv_ctx;
119 }
120 
121 #ifdef HAVE_PLACEBO_OPENGL
placebo_renderer_create_gl(void)122 static void *placebo_renderer_create_gl(void)
123 {
124     SDL_Window *sdlwin = NULL;
125     SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
126 
127     // Common init
128     Dav1dPlayRendererPrivateContext *rd_priv_ctx =
129         placebo_renderer_create_common(SDL_WINDOW_OPENGL);
130 
131     if (rd_priv_ctx == NULL)
132         return NULL;
133     sdlwin = rd_priv_ctx->win;
134 
135     SDL_GLContext glcontext = SDL_GL_CreateContext(sdlwin);
136     SDL_GL_MakeCurrent(sdlwin, glcontext);
137 
138     rd_priv_ctx->gl = pl_opengl_create(rd_priv_ctx->log, pl_opengl_params(
139 #ifndef NDEBUG
140         .debug = true,
141 #endif
142     ));
143     if (!rd_priv_ctx->gl) {
144         fprintf(stderr, "Failed creating opengl device!\n");
145         exit(2);
146     }
147 
148     rd_priv_ctx->swapchain = pl_opengl_create_swapchain(rd_priv_ctx->gl,
149         pl_opengl_swapchain_params(
150             .swap_buffers = (void (*)(void *)) SDL_GL_SwapWindow,
151             .priv = sdlwin,
152         ));
153 
154     if (!rd_priv_ctx->swapchain) {
155         fprintf(stderr, "Failed creating opengl swapchain!\n");
156         exit(2);
157     }
158 
159     int w = WINDOW_WIDTH, h = WINDOW_HEIGHT;
160     SDL_GL_GetDrawableSize(sdlwin, &w, &h);
161 
162     if (!pl_swapchain_resize(rd_priv_ctx->swapchain, &w, &h)) {
163         fprintf(stderr, "Failed resizing vulkan swapchain!\n");
164         exit(2);
165     }
166 
167     rd_priv_ctx->gpu = rd_priv_ctx->gl->gpu;
168 
169     if (w != WINDOW_WIDTH || h != WINDOW_HEIGHT)
170         printf("Note: window dimensions differ (got %dx%d)\n", w, h);
171 
172     return rd_priv_ctx;
173 }
174 #endif
175 
176 #ifdef HAVE_PLACEBO_VULKAN
placebo_renderer_create_vk(void)177 static void *placebo_renderer_create_vk(void)
178 {
179     SDL_Window *sdlwin = NULL;
180 
181     // Common init
182     Dav1dPlayRendererPrivateContext *rd_priv_ctx =
183         placebo_renderer_create_common(SDL_WINDOW_VULKAN);
184 
185     if (rd_priv_ctx == NULL)
186         return NULL;
187     sdlwin = rd_priv_ctx->win;
188 
189     // Init Vulkan
190     unsigned num = 0;
191     if (!SDL_Vulkan_GetInstanceExtensions(sdlwin, &num, NULL)) {
192         fprintf(stderr, "Failed enumerating Vulkan extensions: %s\n", SDL_GetError());
193         exit(1);
194     }
195 
196     const char **extensions = malloc(num * sizeof(const char *));
197     assert(extensions);
198 
199     SDL_bool ok = SDL_Vulkan_GetInstanceExtensions(sdlwin, &num, extensions);
200     if (!ok) {
201         fprintf(stderr, "Failed getting Vk instance extensions\n");
202         exit(1);
203     }
204 
205     if (num > 0) {
206         printf("Requesting %d additional Vulkan extensions:\n", num);
207         for (unsigned i = 0; i < num; i++)
208             printf("    %s\n", extensions[i]);
209     }
210 
211     rd_priv_ctx->vk_inst = pl_vk_inst_create(rd_priv_ctx->log, pl_vk_inst_params(
212         .extensions = extensions,
213         .num_extensions = num,
214     ));
215     if (!rd_priv_ctx->vk_inst) {
216         fprintf(stderr, "Failed creating Vulkan instance!\n");
217         exit(1);
218     }
219     free(extensions);
220 
221     if (!SDL_Vulkan_CreateSurface(sdlwin, rd_priv_ctx->vk_inst->instance, &rd_priv_ctx->surf)) {
222         fprintf(stderr, "Failed creating vulkan surface: %s\n", SDL_GetError());
223         exit(1);
224     }
225 
226     rd_priv_ctx->vk = pl_vulkan_create(rd_priv_ctx->log, pl_vulkan_params(
227         .instance = rd_priv_ctx->vk_inst->instance,
228         .surface = rd_priv_ctx->surf,
229         .allow_software = true,
230     ));
231     if (!rd_priv_ctx->vk) {
232         fprintf(stderr, "Failed creating vulkan device!\n");
233         exit(2);
234     }
235 
236     // Create swapchain
237     rd_priv_ctx->swapchain = pl_vulkan_create_swapchain(rd_priv_ctx->vk,
238         pl_vulkan_swapchain_params(
239             .surface = rd_priv_ctx->surf,
240             .present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR,
241         ));
242 
243     if (!rd_priv_ctx->swapchain) {
244         fprintf(stderr, "Failed creating vulkan swapchain!\n");
245         exit(2);
246     }
247 
248     int w = WINDOW_WIDTH, h = WINDOW_HEIGHT;
249     if (!pl_swapchain_resize(rd_priv_ctx->swapchain, &w, &h)) {
250         fprintf(stderr, "Failed resizing vulkan swapchain!\n");
251         exit(2);
252     }
253 
254     rd_priv_ctx->gpu = rd_priv_ctx->vk->gpu;
255 
256     if (w != WINDOW_WIDTH || h != WINDOW_HEIGHT)
257         printf("Note: window dimensions differ (got %dx%d)\n", w, h);
258 
259     return rd_priv_ctx;
260 }
261 #endif
262 
placebo_renderer_destroy(void * cookie)263 static void placebo_renderer_destroy(void *cookie)
264 {
265     Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
266     assert(rd_priv_ctx != NULL);
267 
268     pl_renderer_destroy(&(rd_priv_ctx->renderer));
269     pl_swapchain_destroy(&(rd_priv_ctx->swapchain));
270     for (int i = 0; i < 3; i++)
271         pl_tex_destroy(rd_priv_ctx->gpu, &(rd_priv_ctx->plane_tex[i]));
272 
273 #ifdef HAVE_PLACEBO_VULKAN
274     if (rd_priv_ctx->vk) {
275         pl_vulkan_destroy(&(rd_priv_ctx->vk));
276         vkDestroySurfaceKHR(rd_priv_ctx->vk_inst->instance, rd_priv_ctx->surf, NULL);
277         pl_vk_inst_destroy(&(rd_priv_ctx->vk_inst));
278     }
279 #endif
280 #ifdef HAVE_PLACEBO_OPENGL
281     if (rd_priv_ctx->gl)
282         pl_opengl_destroy(&(rd_priv_ctx->gl));
283 #endif
284 
285     SDL_DestroyWindow(rd_priv_ctx->win);
286 
287     pl_log_destroy(&rd_priv_ctx->log);
288 }
289 
placebo_render(void * cookie,const Dav1dPlaySettings * settings)290 static void placebo_render(void *cookie, const Dav1dPlaySettings *settings)
291 {
292     Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
293     assert(rd_priv_ctx != NULL);
294 
295     SDL_LockMutex(rd_priv_ctx->lock);
296     if (!rd_priv_ctx->image.num_planes) {
297         SDL_UnlockMutex(rd_priv_ctx->lock);
298         return;
299     }
300 
301     // Prepare rendering
302     if (rd_priv_ctx->renderer == NULL) {
303         rd_priv_ctx->renderer = pl_renderer_create(rd_priv_ctx->log, rd_priv_ctx->gpu);
304     }
305 
306     struct pl_swapchain_frame frame;
307     bool ok = pl_swapchain_start_frame(rd_priv_ctx->swapchain, &frame);
308     if (!ok) {
309         SDL_UnlockMutex(rd_priv_ctx->lock);
310         return;
311     }
312 
313     struct pl_frame target;
314     pl_frame_from_swapchain(&target, &frame);
315     pl_rect2df_aspect_copy(&target.crop, &rd_priv_ctx->image.crop, 0.0);
316     if (pl_frame_is_cropped(&target))
317         pl_tex_clear(rd_priv_ctx->gpu, frame.fbo, (float[4]){ 0.0 });
318 
319     if (!pl_render_image(rd_priv_ctx->renderer, &rd_priv_ctx->image, &target,
320                          settings->highquality ? &pl_render_default_params
321                                                : &pl_render_fast_params))
322     {
323         fprintf(stderr, "Failed rendering frame!\n");
324         pl_tex_clear(rd_priv_ctx->gpu, frame.fbo, (float[4]){ 1.0 });
325     }
326 
327     ok = pl_swapchain_submit_frame(rd_priv_ctx->swapchain);
328     if (!ok) {
329         fprintf(stderr, "Failed submitting frame!\n");
330         SDL_UnlockMutex(rd_priv_ctx->lock);
331         return;
332     }
333 
334     pl_swapchain_swap_buffers(rd_priv_ctx->swapchain);
335     SDL_UnlockMutex(rd_priv_ctx->lock);
336 }
337 
placebo_upload_image(void * cookie,Dav1dPicture * dav1d_pic,const Dav1dPlaySettings * settings)338 static int placebo_upload_image(void *cookie, Dav1dPicture *dav1d_pic,
339                                 const Dav1dPlaySettings *settings)
340 {
341     Dav1dPlayRendererPrivateContext *p = cookie;
342     assert(p != NULL);
343     int ret = 0;
344 
345     if (!dav1d_pic)
346         return ret;
347 
348     SDL_LockMutex(p->lock);
349     if (!pl_upload_dav1dpicture(p->gpu, &p->image, p->plane_tex, pl_dav1d_upload_params(
350         .picture = dav1d_pic,
351         .film_grain = settings->gpugrain,
352         .gpu_allocated = settings->zerocopy,
353         .asynchronous = true,
354     )))
355     {
356         fprintf(stderr, "Failed uploading planes!\n");
357         p->image = (struct pl_frame) {0};
358         ret = -1;
359     }
360     SDL_UnlockMutex(p->lock);
361     return ret;
362 }
363 
placebo_alloc_pic(Dav1dPicture * const pic,void * cookie)364 static int placebo_alloc_pic(Dav1dPicture *const pic, void *cookie)
365 {
366     Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
367     assert(rd_priv_ctx != NULL);
368 
369     SDL_LockMutex(rd_priv_ctx->lock);
370     int ret = pl_allocate_dav1dpicture(pic, (void *) rd_priv_ctx->gpu);
371     SDL_UnlockMutex(rd_priv_ctx->lock);
372     return ret;
373 }
374 
placebo_release_pic(Dav1dPicture * pic,void * cookie)375 static void placebo_release_pic(Dav1dPicture *pic, void *cookie)
376 {
377     Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
378     assert(rd_priv_ctx != NULL);
379 
380     SDL_LockMutex(rd_priv_ctx->lock);
381     pl_release_dav1dpicture(pic, (void *) rd_priv_ctx->gpu);
382     SDL_UnlockMutex(rd_priv_ctx->lock);
383 }
384 
385 #ifdef HAVE_PLACEBO_VULKAN
386 const Dav1dPlayRenderInfo rdr_placebo_vk = {
387     .name = "placebo-vk",
388     .create_renderer = placebo_renderer_create_vk,
389     .destroy_renderer = placebo_renderer_destroy,
390     .render = placebo_render,
391     .update_frame = placebo_upload_image,
392     .alloc_pic = placebo_alloc_pic,
393     .release_pic = placebo_release_pic,
394     .supports_gpu_grain = 1,
395 };
396 #else
397 const Dav1dPlayRenderInfo rdr_placebo_vk = { NULL };
398 #endif
399 
400 #ifdef HAVE_PLACEBO_OPENGL
401 const Dav1dPlayRenderInfo rdr_placebo_gl = {
402     .name = "placebo-gl",
403     .create_renderer = placebo_renderer_create_gl,
404     .destroy_renderer = placebo_renderer_destroy,
405     .render = placebo_render,
406     .update_frame = placebo_upload_image,
407     .supports_gpu_grain = 1,
408 };
409 #else
410 const Dav1dPlayRenderInfo rdr_placebo_gl = { NULL };
411 #endif
412 
413 #else
414 const Dav1dPlayRenderInfo rdr_placebo_vk = { NULL };
415 const Dav1dPlayRenderInfo rdr_placebo_gl = { NULL };
416 #endif
417