xref: /aosp_15_r20/external/libdav1d/examples/dp_renderer_placebo.c (revision c09093415860a1c2373dacd84c4fde00c507cdfd)
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 #if HAVE_RENDERER_PLACEBO
30 #include <assert.h>
31 
32 #include <libplacebo/renderer.h>
33 #include <libplacebo/utils/dav1d.h>
34 
35 #if HAVE_PLACEBO_VULKAN
36 # include <libplacebo/vulkan.h>
37 # include <SDL_vulkan.h>
38 #endif
39 #if 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 #if 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 #if HAVE_PLACEBO_OPENGL
65     // Placebo OpenGL handle
66     pl_opengl gl;
67     // SDL OpenGL context
68     SDL_GLContext gl_context;
69 #endif
70     // Placebo GPU
71     pl_gpu gpu;
72     // Placebo swapchain
73     pl_swapchain swapchain;
74     // Lock protecting access to the texture
75     SDL_mutex *lock;
76     // Image to render, and planes backing them
77     struct pl_frame image;
78     pl_tex plane_tex[3];
79 } Dav1dPlayRendererPrivateContext;
80 
81 static Dav1dPlayRendererPrivateContext*
placebo_renderer_create_common(const Dav1dPlaySettings * settings,int window_flags)82     placebo_renderer_create_common(const Dav1dPlaySettings *settings, int window_flags)
83 {
84     if (settings->fullscreen)
85         window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
86 
87     // Create Window
88     SDL_Window *sdlwin = dp_create_sdl_window(window_flags | SDL_WINDOW_RESIZABLE);
89     if (sdlwin == NULL)
90         return NULL;
91 
92     SDL_ShowCursor(0);
93 
94     // Alloc
95     Dav1dPlayRendererPrivateContext *const rd_priv_ctx =
96         calloc(1, sizeof(Dav1dPlayRendererPrivateContext));
97     if (rd_priv_ctx == NULL)
98         return NULL;
99 
100     rd_priv_ctx->win = sdlwin;
101 
102     // Init libplacebo
103     rd_priv_ctx->log = pl_log_create(PL_API_VER, pl_log_params(
104         .log_cb     = pl_log_color,
105 #ifndef NDEBUG
106         .log_level  = PL_LOG_DEBUG,
107 #else
108         .log_level  = PL_LOG_WARN,
109 #endif
110     ));
111     if (rd_priv_ctx->log == NULL) {
112         free(rd_priv_ctx);
113         return NULL;
114     }
115 
116     // Create Mutex
117     rd_priv_ctx->lock = SDL_CreateMutex();
118     if (rd_priv_ctx->lock == NULL) {
119         fprintf(stderr, "SDL_CreateMutex failed: %s\n", SDL_GetError());
120         pl_log_destroy(&rd_priv_ctx->log);
121         free(rd_priv_ctx);
122         return NULL;
123     }
124 
125     return rd_priv_ctx;
126 }
127 
128 #if HAVE_PLACEBO_OPENGL
placebo_renderer_create_gl(const Dav1dPlaySettings * settings)129 static void *placebo_renderer_create_gl(const Dav1dPlaySettings *settings)
130 {
131     SDL_Window *sdlwin = NULL;
132     SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
133 
134     // Common init
135     Dav1dPlayRendererPrivateContext *rd_priv_ctx =
136         placebo_renderer_create_common(settings, SDL_WINDOW_OPENGL);
137 
138     if (rd_priv_ctx == NULL)
139         return NULL;
140     sdlwin = rd_priv_ctx->win;
141 
142     rd_priv_ctx->gl_context = SDL_GL_CreateContext(sdlwin);
143     SDL_GL_MakeCurrent(sdlwin, rd_priv_ctx->gl_context);
144 
145     rd_priv_ctx->gl = pl_opengl_create(rd_priv_ctx->log, pl_opengl_params(
146         .allow_software = true,
147 #ifndef NDEBUG
148         .debug = true,
149 #endif
150     ));
151     if (!rd_priv_ctx->gl) {
152         fprintf(stderr, "Failed creating opengl device!\n");
153         exit(2);
154     }
155 
156     rd_priv_ctx->swapchain = pl_opengl_create_swapchain(rd_priv_ctx->gl,
157         pl_opengl_swapchain_params(
158             .swap_buffers = (void (*)(void *)) SDL_GL_SwapWindow,
159             .priv = sdlwin,
160         ));
161 
162     if (!rd_priv_ctx->swapchain) {
163         fprintf(stderr, "Failed creating opengl swapchain!\n");
164         exit(2);
165     }
166 
167     int w = WINDOW_WIDTH, h = WINDOW_HEIGHT;
168     SDL_GL_GetDrawableSize(sdlwin, &w, &h);
169 
170     if (!pl_swapchain_resize(rd_priv_ctx->swapchain, &w, &h)) {
171         fprintf(stderr, "Failed resizing vulkan swapchain!\n");
172         exit(2);
173     }
174 
175     rd_priv_ctx->gpu = rd_priv_ctx->gl->gpu;
176 
177     if (w != WINDOW_WIDTH || h != WINDOW_HEIGHT)
178         printf("Note: window dimensions differ (got %dx%d)\n", w, h);
179 
180     return rd_priv_ctx;
181 }
182 #endif
183 
184 #if HAVE_PLACEBO_VULKAN
placebo_renderer_create_vk(const Dav1dPlaySettings * settings)185 static void *placebo_renderer_create_vk(const Dav1dPlaySettings *settings)
186 {
187     SDL_Window *sdlwin = NULL;
188 
189     // Common init
190     Dav1dPlayRendererPrivateContext *rd_priv_ctx =
191         placebo_renderer_create_common(settings, SDL_WINDOW_VULKAN);
192 
193     if (rd_priv_ctx == NULL)
194         return NULL;
195     sdlwin = rd_priv_ctx->win;
196 
197     // Init Vulkan
198     unsigned num = 0;
199     if (!SDL_Vulkan_GetInstanceExtensions(sdlwin, &num, NULL)) {
200         fprintf(stderr, "Failed enumerating Vulkan extensions: %s\n", SDL_GetError());
201         exit(1);
202     }
203 
204     const char **extensions = malloc(num * sizeof(const char *));
205     assert(extensions);
206 
207     SDL_bool ok = SDL_Vulkan_GetInstanceExtensions(sdlwin, &num, extensions);
208     if (!ok) {
209         fprintf(stderr, "Failed getting Vk instance extensions\n");
210         exit(1);
211     }
212 
213     if (num > 0) {
214         printf("Requesting %d additional Vulkan extensions:\n", num);
215         for (unsigned i = 0; i < num; i++)
216             printf("    %s\n", extensions[i]);
217     }
218 
219     rd_priv_ctx->vk_inst = pl_vk_inst_create(rd_priv_ctx->log, pl_vk_inst_params(
220         .extensions = extensions,
221         .num_extensions = num,
222     ));
223     if (!rd_priv_ctx->vk_inst) {
224         fprintf(stderr, "Failed creating Vulkan instance!\n");
225         exit(1);
226     }
227     free(extensions);
228 
229     if (!SDL_Vulkan_CreateSurface(sdlwin, rd_priv_ctx->vk_inst->instance, &rd_priv_ctx->surf)) {
230         fprintf(stderr, "Failed creating vulkan surface: %s\n", SDL_GetError());
231         exit(1);
232     }
233 
234     rd_priv_ctx->vk = pl_vulkan_create(rd_priv_ctx->log, pl_vulkan_params(
235         .instance = rd_priv_ctx->vk_inst->instance,
236         .surface = rd_priv_ctx->surf,
237         .allow_software = true,
238     ));
239     if (!rd_priv_ctx->vk) {
240         fprintf(stderr, "Failed creating vulkan device!\n");
241         exit(2);
242     }
243 
244     // Create swapchain
245     rd_priv_ctx->swapchain = pl_vulkan_create_swapchain(rd_priv_ctx->vk,
246         pl_vulkan_swapchain_params(
247             .surface = rd_priv_ctx->surf,
248             .present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR,
249         ));
250 
251     if (!rd_priv_ctx->swapchain) {
252         fprintf(stderr, "Failed creating vulkan swapchain!\n");
253         exit(2);
254     }
255 
256     int w = WINDOW_WIDTH, h = WINDOW_HEIGHT;
257     if (!pl_swapchain_resize(rd_priv_ctx->swapchain, &w, &h)) {
258         fprintf(stderr, "Failed resizing vulkan swapchain!\n");
259         exit(2);
260     }
261 
262     rd_priv_ctx->gpu = rd_priv_ctx->vk->gpu;
263 
264     if (w != WINDOW_WIDTH || h != WINDOW_HEIGHT)
265         printf("Note: window dimensions differ (got %dx%d)\n", w, h);
266 
267     return rd_priv_ctx;
268 }
269 #endif
270 
placebo_renderer_destroy(void * cookie)271 static void placebo_renderer_destroy(void *cookie)
272 {
273     Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
274     assert(rd_priv_ctx != NULL);
275 
276     pl_renderer_destroy(&(rd_priv_ctx->renderer));
277     pl_swapchain_destroy(&(rd_priv_ctx->swapchain));
278     for (int i = 0; i < 3; i++)
279         pl_tex_destroy(rd_priv_ctx->gpu, &(rd_priv_ctx->plane_tex[i]));
280 
281 #if HAVE_PLACEBO_VULKAN
282     if (rd_priv_ctx->vk) {
283         pl_vulkan_destroy(&(rd_priv_ctx->vk));
284         vkDestroySurfaceKHR(rd_priv_ctx->vk_inst->instance, rd_priv_ctx->surf, NULL);
285         pl_vk_inst_destroy(&(rd_priv_ctx->vk_inst));
286     }
287 #endif
288 #if HAVE_PLACEBO_OPENGL
289     if (rd_priv_ctx->gl)
290         pl_opengl_destroy(&(rd_priv_ctx->gl));
291     if (rd_priv_ctx->gl_context)
292         SDL_GL_DeleteContext(rd_priv_ctx->gl_context);
293 #endif
294 
295     SDL_DestroyWindow(rd_priv_ctx->win);
296 
297     pl_log_destroy(&rd_priv_ctx->log);
298 }
299 
placebo_render(void * cookie,const Dav1dPlaySettings * settings)300 static void placebo_render(void *cookie, const Dav1dPlaySettings *settings)
301 {
302     Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
303     assert(rd_priv_ctx != NULL);
304 
305     SDL_LockMutex(rd_priv_ctx->lock);
306     if (!rd_priv_ctx->image.num_planes) {
307         SDL_UnlockMutex(rd_priv_ctx->lock);
308         return;
309     }
310 
311     // Prepare rendering
312     if (rd_priv_ctx->renderer == NULL) {
313         rd_priv_ctx->renderer = pl_renderer_create(rd_priv_ctx->log, rd_priv_ctx->gpu);
314     }
315 
316     struct pl_swapchain_frame frame;
317     bool ok = pl_swapchain_start_frame(rd_priv_ctx->swapchain, &frame);
318     if (!ok) {
319         SDL_UnlockMutex(rd_priv_ctx->lock);
320         return;
321     }
322 
323     struct pl_frame target;
324     pl_frame_from_swapchain(&target, &frame);
325     pl_rect2df_aspect_copy(&target.crop, &rd_priv_ctx->image.crop, 0.0);
326     if (pl_frame_is_cropped(&target))
327         pl_tex_clear(rd_priv_ctx->gpu, frame.fbo, (float[4]){ 0.0 });
328 
329     if (!pl_render_image(rd_priv_ctx->renderer, &rd_priv_ctx->image, &target,
330                          settings->highquality ? &pl_render_default_params
331                                                : &pl_render_fast_params))
332     {
333         fprintf(stderr, "Failed rendering frame!\n");
334         pl_tex_clear(rd_priv_ctx->gpu, frame.fbo, (float[4]){ 1.0 });
335     }
336 
337     ok = pl_swapchain_submit_frame(rd_priv_ctx->swapchain);
338     if (!ok) {
339         fprintf(stderr, "Failed submitting frame!\n");
340         SDL_UnlockMutex(rd_priv_ctx->lock);
341         return;
342     }
343 
344     pl_swapchain_swap_buffers(rd_priv_ctx->swapchain);
345     SDL_UnlockMutex(rd_priv_ctx->lock);
346 }
347 
placebo_upload_image(void * cookie,Dav1dPicture * dav1d_pic,const Dav1dPlaySettings * settings)348 static int placebo_upload_image(void *cookie, Dav1dPicture *dav1d_pic,
349                                 const Dav1dPlaySettings *settings)
350 {
351     Dav1dPlayRendererPrivateContext *p = cookie;
352     assert(p != NULL);
353     int ret = 0;
354 
355     if (!dav1d_pic)
356         return ret;
357 
358     SDL_LockMutex(p->lock);
359     if (!pl_upload_dav1dpicture(p->gpu, &p->image, p->plane_tex, pl_dav1d_upload_params(
360         .picture = dav1d_pic,
361         .film_grain = settings->gpugrain,
362         .gpu_allocated = settings->zerocopy,
363         .asynchronous = true,
364     )))
365     {
366         fprintf(stderr, "Failed uploading planes!\n");
367         p->image = (struct pl_frame) {0};
368         ret = -1;
369     }
370     SDL_UnlockMutex(p->lock);
371     return ret;
372 }
373 
placebo_alloc_pic(Dav1dPicture * const pic,void * cookie)374 static int placebo_alloc_pic(Dav1dPicture *const pic, void *cookie)
375 {
376     Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
377     assert(rd_priv_ctx != NULL);
378 
379     SDL_LockMutex(rd_priv_ctx->lock);
380     int ret = pl_allocate_dav1dpicture(pic, (void *) rd_priv_ctx->gpu);
381     SDL_UnlockMutex(rd_priv_ctx->lock);
382     return ret;
383 }
384 
placebo_release_pic(Dav1dPicture * pic,void * cookie)385 static void placebo_release_pic(Dav1dPicture *pic, void *cookie)
386 {
387     Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
388     assert(rd_priv_ctx != NULL);
389 
390     SDL_LockMutex(rd_priv_ctx->lock);
391     pl_release_dav1dpicture(pic, (void *) rd_priv_ctx->gpu);
392     SDL_UnlockMutex(rd_priv_ctx->lock);
393 }
394 
395 #if HAVE_PLACEBO_VULKAN
396 const Dav1dPlayRenderInfo rdr_placebo_vk = {
397     .name = "placebo-vk",
398     .create_renderer = placebo_renderer_create_vk,
399     .destroy_renderer = placebo_renderer_destroy,
400     .render = placebo_render,
401     .update_frame = placebo_upload_image,
402     .alloc_pic = placebo_alloc_pic,
403     .release_pic = placebo_release_pic,
404     .supports_gpu_grain = 1,
405 };
406 #else
407 const Dav1dPlayRenderInfo rdr_placebo_vk = { NULL };
408 #endif
409 
410 #if HAVE_PLACEBO_OPENGL
411 const Dav1dPlayRenderInfo rdr_placebo_gl = {
412     .name = "placebo-gl",
413     .create_renderer = placebo_renderer_create_gl,
414     .destroy_renderer = placebo_renderer_destroy,
415     .render = placebo_render,
416     .update_frame = placebo_upload_image,
417     .supports_gpu_grain = 1,
418 };
419 #else
420 const Dav1dPlayRenderInfo rdr_placebo_gl = { NULL };
421 #endif
422 
423 #else
424 const Dav1dPlayRenderInfo rdr_placebo_vk = { NULL };
425 const Dav1dPlayRenderInfo rdr_placebo_gl = { NULL };
426 #endif
427