summaryrefslogtreecommitdiff
path: root/src/render/renderer_libplacebo.c
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2026-08-26 17:56:52 -0400
committerAndrew Opalach <andrew@akon.city> 2026-08-26 17:56:52 -0400
commit197d41a3e23cab35848f623c6133baccc62aacbe (patch)
tree04943c1de9653de53615e10ced1eab07c2562325 /src/render/renderer_libplacebo.c
parent6094c907b26e21f13373bae6bf3306dbae40af3c (diff)
downloadcamu-197d41a3e23cab35848f623c6133baccc62aacbe.tar.gz
camu-197d41a3e23cab35848f623c6133baccc62aacbe.tar.bz2
camu-197d41a3e23cab35848f623c6133baccc62aacbe.zip
OSD stuff and Android fixes
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/render/renderer_libplacebo.c')
-rw-r--r--src/render/renderer_libplacebo.c300
1 files changed, 265 insertions, 35 deletions
diff --git a/src/render/renderer_libplacebo.c b/src/render/renderer_libplacebo.c
index d6f95a8..8c28162 100644
--- a/src/render/renderer_libplacebo.c
+++ b/src/render/renderer_libplacebo.c
@@ -2,15 +2,21 @@
//#define AL_LOG_ENABLE_TRACE
#include <al/log.h>
#include <al/lib.h>
+#include <al/math.h>
#include <nnwt/file.h>
#include <nnwt/time.h>
+#include <libplacebo/shaders.h>
#include "../screen/screen.h"
#include "../util/color_palette.h"
+#include "../util/print_time.h"
#include "renderer_libplacebo.h"
#include "queue_libplacebo.h"
+#include "shaders/osd_header.h"
+#include "shaders/osd.h"
+#include "shaders/osd_binds.h"
#include "shaders/vr_video.h"
#ifdef AL_DEBUG
@@ -21,14 +27,18 @@
#endif
#define CRT_BACKGROUND
-#define CRT_BACKGROUND_COLOR (1.f / 7.5f)
-#define CRT_BACKGROUND_COLOR_ON (0.025f)
+#define CRT_BACKGROUND_COLOR(c4) do { \
+ c4[0] = 0.1333f; c4[1] = 0.1333f; c4[2] = 0.1333f; \
+} while (0)
+#define CRT_BACKGROUND_COLOR_ON(lr, c4) do { \
+ if (lr->params.background != PL_CLEAR_SKIP) { \
+ c4[0] = 0.025f; c4[1] = 0.025f; c4[2] = 0.025f; \
+ } else { \
+ c4[0] = 0.f; c4[1] = 0.f; c4[2] = 0.f; \
+ } \
+} while (0)
-#ifdef CRT_BACKGROUND
-static f32 clear_color[4] = { CRT_BACKGROUND_COLOR, CRT_BACKGROUND_COLOR, CRT_BACKGROUND_COLOR, 1.f };
-#else
static f32 clear_color[4] = { 0.f, 0.f, 0.f, 1.f };
-#endif
static struct pl_render_params default_params;
@@ -62,13 +72,14 @@ static void renderer_lp_set(struct camu_renderer *renderer, u8 option, u8 value)
{
struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer;
switch (option) {
- case CAMU_RENDERER_FULLSCREEN:
+ case CAMU_RENDERER_FULLSCREEN: {
#ifdef STELA_API_DX11
IDXGISwapChain *d3d_swapchain = pl_d3d11_swapchain_unwrap(lr->swapchain);
IDXGISwapChain_SetFullscreenState(d3d_swapchain, value, NULL);
IDXGISwapChain_Release(d3d_swapchain);
#endif
break;
+ }
case CAMU_RENDERER_SCALING:
switch (value) {
case CAMU_SCALING_DEFAULT:
@@ -106,7 +117,7 @@ static void log_callback(void *userdata, enum pl_log_level level, const char *me
// will be shot anyway so allow it.
log_info(message);
} else {
- log_debug(message);
+ log_info(message);
}
}
@@ -136,7 +147,7 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *wid
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL (*get_vk_proc_address)(VkInstance, const char *),
VkResult (*vk_create_surface)(void *, VkInstance, VkSurfaceKHR *),
VkResult (*vk_destroy_surface)(VkInstance, VkSurfaceKHR),
- const char *const *(*vk_get_extensions)(u32 *),
+ const char * const *(*vk_get_extensions)(u32 *),
#elif defined STELA_API_DX11
HWND win32_window,
#elif defined STELA_API_OPENGL
@@ -174,7 +185,7 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *wid
lr->vk_destroy_surface = vk_destroy_surface;
u32 num;
- const char *const *extensions = vk_get_extensions(&num);
+ const char * const *extensions = vk_get_extensions(&num);
lr->vk_inst = pl_vk_inst_create(lr->logger, pl_vk_inst_params(
.debug = RENDERER_DEBUG,
.get_proc_addr = get_vk_proc_address,
@@ -229,7 +240,7 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *wid
// https://learn.microsoft.com/en-us/windows/win32/api/dxgi/ne-dxgi-dxgi_swap_chain_flag
// https://learn.microsoft.com/en-us/windows/win32/api/dxgi/ne-dxgi-dxgi_swap_effect
char *FLIP = getenv("SINK_DXGI_FLIP");
- if (!FLIP) FLIP = "0";
+ if (!FLIP) FLIP = "1";
lr->swapchain = pl_d3d11_create_swapchain(lr->d3d11, pl_d3d11_swapchain_params(
.window = win32_window,
.blit = al_strscmp(FLIP, "0") == 0,
@@ -280,6 +291,22 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *wid
log_info("Using OpenGL.");
#endif
+#ifdef USE_LIBPLACEBO_CACHE
+ lr->cache = pl_cache_create(pl_cache_params(
+ .log = lr->logger,
+ .max_total_size = MB(50)
+ ));
+ pl_gpu_set_cache(lr->gpu, lr->cache);
+ FILE *cache_file = fopen("sink.cache", "rb");
+ if (cache_file) {
+ pl_cache_load_file(lr->cache, cache_file);
+ //p->cache_sig = pl_cache_signature(p->cache);
+ fclose(cache_file);
+ }
+#endif
+
+ lr->dispatch = pl_dispatch_create(lr->gpu->log, lr->gpu);
+
lr->renderer = pl_renderer_create(lr->logger, lr->gpu);
pl_swapchain_resize_compat(lr->swapchain, width, height);
@@ -291,6 +318,7 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *wid
al_memset(&lr->params, 0, sizeof(struct pl_render_params));
lr->params = default_params;
+
lr->params.deband_params = NULL;
//lr->params.frame_mixer = NULL;
@@ -302,16 +330,79 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *wid
// Prioritize a more consistent image.
lr->params.correct_subpixel_offsets = true;
- //lr->params.distort_params = pl_distort_params(.unscaled = false);
- lr->vr_emulation_hook[0] = pl_mpv_user_shader_parse(lr->gpu, vr_video_shader, sizeof(vr_video_shader) - 1);
- /*
+ lr->osd.vars[0].var = pl_var_uvec2("screen");
+ lr->osd.vars[1].var = pl_var_uint("scale");
+ lr->osd.vars[2].var = pl_var_float("time");
+ lr->osd.vars[3].var = pl_var_uint("show_osd");
+ lr->osd.vars[4].var = pl_var_uint("connecting");
+ lr->osd.vars[5].var = pl_var_uint("paused");
+ lr->osd.vars[6].var = pl_var_uint("bar_mode");
+ lr->osd.vars[7].var = pl_var_float("bar");
+ lr->osd.vars[8].var = pl_var_float("dbar");
+ lr->osd.vars[9].var = pl_var_float("bbar");
+ lr->osd.vars[10].var = pl_var_uint("show_hour");
+ lr->osd.vars[11].var = pl_var_uint("negative");
+ static const char num_names[9][6] = {
+ "num1", "num2", "num3", "num4",
+ "num5", "num6", "num7", "num8",
+ "num9"
+ };
+ for (u32 i = 0; i < 9; i++) {
+ lr->osd.vars[12 + i].var = pl_var_uint(num_names[i]);
+ }
+ for (u32 i = 0; i < 21; i++) {
+ lr->osd.vars[i].dynamic = true;
+ }
+
+ lr->osd.attribs[0] = (struct pl_vertex_attrib){
+ .name = "pos",
+ .offset = 0,
+ .fmt = pl_find_vertex_fmt(lr->gpu, PL_FMT_FLOAT, 2)
+ };
+ lr->osd.attribs[1] = (struct pl_vertex_attrib){
+ .name = "coord",
+ .offset = sizeof(f32) * 2,
+ .fmt = pl_find_vertex_fmt(lr->gpu, PL_FMT_FLOAT, 2)
+ };
+
+ al_memcpy(lr->osd.vertices, (f32[]){
+ 1.f, 1.f, 1.f, 1.f,
+ -1.f, -1.f, 0.f, 0.f,
+ -1.f, 1.f, 0.f, 1.f,
+ -1.f, -1.f, 0.f, 0.f,
+ 1.f, -1.f, 1.f, 0.f,
+ 1.f, 1.f, 1.f, 1.f,
+ }, sizeof(f32) * 24);
+
+ lr->osd.prelude = osd_header_shader;
+ lr->osd.header = osd_shader;
+ lr->osd.binds = osd_binds_shader;
+#if 0
struct nn_file file;
- if (nn_file_open(&file, &al_str_c(""), 0)) {
+ if (nn_file_open(&file, &al_str_c("../src/render/shaders/osd_header.glsl"), 0)) {
+ char *c_str;
+ nn_file_read_as_c_str(&file, &c_str);
+ lr->osd.prelude = c_str;
+ nn_file_close(&file);
+ }
+ if (nn_file_open(&file, &al_str_c("../src/render/shaders/osd.glsl"), 0)) {
char *c_str;
- size_t length = nn_file_read_as_c_str(&file, &c_str);
- const struct pl_hook *hook = pl_mpv_user_shader_parse(lr->gpu, (const char *)c_str, length);
+ nn_file_read_as_c_str(&file, &c_str);
+ lr->osd.header = c_str;
+ nn_file_close(&file);
}
- */
+ if (nn_file_open(&file, &al_str_c("../src/render/shaders/osd_binds.glsl"), 0)) {
+ char *c_str;
+ nn_file_read_as_c_str(&file, &c_str);
+ lr->osd.binds = c_str;
+ nn_file_close(&file);
+ }
+#endif
+ lr->osd.tick_time = -1.0;
+ lr->osd.logged_fail = false;
+
+ lr->vr_init_failed = false;
+ lr->vr_emulation_hook[0] = NULL;
#ifdef CAMU_HAVE_FFMPEG
lr->r.get_buffer2 = pl_get_buffer2;
@@ -385,6 +476,125 @@ static inline intptr_t float_64_hash(f64 value)
return hash;
}
+#define ROUND_SECONDS(h, m, s, n) do { \
+ if (n >= 50) { \
+ if (s == 59) { \
+ if (m == 59) { \
+ h++; \
+ m = 0; \
+ } else m++; \
+ s = 0; \
+ } else s++; \
+ } \
+} while (0)
+
+static inline bool render_osd(struct camu_renderer_lp *lr, struct camu_screen *scr)
+{
+ f64 duration = scr->osd.duration / 1000000.0;
+ f32 bar, dbar, bbar;
+ f32 progress = ((duration != 0) ? scr->osd.pts / duration : 0.f);
+ if (scr->osd.bar_mode != CAMU_OSD_BAR_VOLUME) {
+ dbar = progress;
+ bbar = dbar; // @TODO: Should be buffered, once that's supported in the VCR.
+ } else {
+ dbar = 0.f;
+ bbar = 0.f;
+ }
+ bar = (scr->osd.bar_mode == CAMU_OSD_BAR_PROGRESS) ? progress : scr->osd.bar;
+ lr->osd.screen_size[0] = scr->width;
+ lr->osd.screen_size[1] = scr->height;
+ lr->osd.vars[0].data = lr->osd.screen_size;
+ u32 scale = 1;
+ lr->osd.vars[1].data = &scale;
+ f64 tick = nn_get_tick();
+ if (lr->osd.tick_time == -1.0) lr->osd.tick_time = tick;
+ lr->osd.time = (f32)(tick - lr->osd.tick_time);
+ lr->osd.vars[2].data = &lr->osd.time;
+ lr->osd.vars[3].data = &scr->osd.show;
+ lr->osd.vars[4].data = &scr->osd.connecting;
+ lr->osd.vars[5].data = &scr->osd.paused;
+ lr->osd.vars[6].data = &scr->osd.bar_mode;
+ lr->osd.vars[7].data = &bar;
+ lr->osd.vars[8].data = &dbar;
+ lr->osd.vars[9].data = &bbar;
+ u32 hours1, minutes1, seconds1, hundredths;
+ u32 show_hour;
+ show_hour = camu_split_time(scr->osd.duration, &hours1, &minutes1, &seconds1, &hundredths);
+ ROUND_SECONDS(hours1, minutes1, seconds1, hundredths);
+ lr->osd.vars[10].data = &show_hour;
+ u32 negative;
+ negative = scr->osd.pts < 0.0f;
+ lr->osd.vars[11].data = &negative;
+ lr->osd.vars[15].data = &hours1;
+ lr->osd.vars[16].data = &minutes1;
+ lr->osd.vars[17].data = &seconds1;
+ u32 hours2, minutes2, seconds2;
+ camu_split_time(fabs(scr->osd.pts) * 1000000u, &hours2, &minutes2, &seconds2, &hundredths);
+ // The trade-off of rounding PTS is that if we do, second 0 will be shown for only half a second.
+ // If we don't, more noticeably, PTS will never equal duration if duration rounds up.
+ // There's also the option of not rounding either.
+ ROUND_SECONDS(hours2, minutes2, seconds2, hundredths);
+ lr->osd.vars[12].data = &hours2;
+ lr->osd.vars[13].data = &minutes2;
+ lr->osd.vars[14].data = &seconds2;
+ u32 zero = 0;
+ lr->osd.vars[18].data = &zero;
+ lr->osd.vars[19].data = &zero;
+ lr->osd.vars[20].data = &zero;
+ if (scr->osd.bar_mode == CAMU_OSD_BAR_SEEK) {
+ u32 hours3, minutes3, seconds3;
+ camu_split_time(fabs(duration * bar) * 1000000u, &hours3, &minutes3, &seconds3, &hundredths);
+ ROUND_SECONDS(hours3, minutes3, seconds3, hundredths);
+ lr->osd.vars[18].data = &hours3;
+ lr->osd.vars[19].data = &minutes3;
+ lr->osd.vars[20].data = &seconds3;
+ } else {
+ f64 whole = floor(bar);
+ f64 frac = (bar - whole) * 100.0;
+ u32 num1, num2;
+ num1 = (u32)whole;
+ num2 = (u32)round(frac);
+ lr->osd.vars[19].data = &num1;
+ lr->osd.vars[20].data = &num2;
+ }
+
+ lr->osd.sh = pl_dispatch_begin(lr->dispatch);
+ pl_shader_custom(lr->osd.sh, &(struct pl_custom_shader){
+ .description = "osd",
+ .prelude = lr->osd.prelude,
+ .header = scr->osd.show_binds ? lr->osd.binds : lr->osd.header,
+ .body = "color = (show_osd == 0u) ? vec4(0.) : draw_osd();",
+ .output = PL_SHADER_SIG_COLOR,
+ .num_variables = 21,
+ .variables = lr->osd.vars
+ });
+
+ struct pl_color_repr repr = lr->frame.color_repr;
+ pl_shader_color_map_ex(lr->osd.sh, NULL, pl_color_map_args(
+ .src = pl_color_space_srgb,
+ .dst = lr->frame.color_space
+ ));
+ pl_shader_encode_color(lr->osd.sh, &repr);
+
+ struct pl_dispatch_vertex_params params = { 0 };
+ params = *pl_dispatch_vertex_params(
+ .shader = &lr->osd.sh,
+ .target = lr->frame.fbo,
+ .blend_params = &pl_alpha_overlay,
+ .vertex_attribs = lr->osd.attribs,
+ .num_vertex_attribs = 2,
+ .vertex_stride = sizeof(f32) * 4,
+ .vertex_position_idx = 0,
+ .vertex_coords = PL_COORDS_NORMALIZED,
+ .vertex_flipped = lr->frame.flipped,
+ .vertex_type = PL_PRIM_TRIANGLE_LIST,
+ .vertex_count = 6,
+ .vertex_data = lr->osd.vertices
+ );
+
+ return pl_dispatch_vertex(lr->dispatch, &params);
+}
+
static bool renderer_lp_render(struct camu_renderer *renderer, struct camu_screen *scr, bool force)
{
struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer;
@@ -396,9 +606,8 @@ static bool renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
#endif
if (!lr->have_frame) {
- struct pl_swapchain_frame frame;
- if (pl_swapchain_start_frame(lr->swapchain, &frame)) {
- pl_frame_from_swapchain(&lr->target, &frame);
+ if (pl_swapchain_start_frame(lr->swapchain, &lr->frame)) {
+ pl_frame_from_swapchain(&lr->target, &lr->frame);
lr->have_frame = true;
#ifdef CRT_BACKGROUND
lr->frame_cleared = false;
@@ -413,7 +622,15 @@ static bool renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
}
f64 mouse_x, mouse_y;
- bool vr_emulation = scr->vr_emulation && lr->vr_emulation_hook[0];
+ bool vr_emulation = scr->vr_emulation && !lr->vr_init_failed;
+ if (vr_emulation && !lr->vr_emulation_hook[0]) {
+ lr->vr_emulation_hook[0] = pl_mpv_user_shader_parse(lr->gpu, vr_video_shader, sizeof(vr_video_shader) - 1);
+ lr->vr_init_failed = !lr->vr_emulation_hook[0];
+ if (lr->vr_init_failed) {
+ log_error("Failed to compile VR emulation shader.");
+ vr_emulation = false;
+ }
+ }
if (vr_emulation) {
mouse_x = (scr->last_pointer_x / scr->width) - (scr->vr_calibrate_x - 0.5);
mouse_y = (scr->last_pointer_y / scr->height) - (scr->vr_calibrate_y - 0.5);
@@ -484,8 +701,8 @@ static bool renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
target->crop.x1 += video->view.x_offset;
target->crop.y1 += video->view.y_offset;
target->rotation = mix.frames[0]->rotation - video->view.rotation;
- /*
//lr->params.color_map_params = pl_color_map_params(.inverse_tone_mapping = true);
+ /*
if () {
target->color = pl_color_space_srgb;
target->color.hdr.max_luma = PL_COLOR_SDR_WHITE - 50.f;
@@ -494,15 +711,7 @@ static bool renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
*/
#ifdef CRT_BACKGROUND
if (!lr->frame_cleared) {
- if (lr->params.background != PL_CLEAR_SKIP) {
- clear_color[0] = CRT_BACKGROUND_COLOR_ON;
- clear_color[1] = CRT_BACKGROUND_COLOR_ON;
- clear_color[2] = CRT_BACKGROUND_COLOR_ON;
- } else {
- clear_color[0] = 0.f;
- clear_color[1] = 0.f;
- clear_color[2] = 0.f;
- }
+ CRT_BACKGROUND_COLOR_ON(lr, clear_color);
pl_frame_clear_rgba(lr->gpu, &lr->target, clear_color);
lr->frame_cleared = true;
}
@@ -539,14 +748,20 @@ static bool renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
#ifdef CRT_BACKGROUND
if (!lr->frame_cleared) {
- clear_color[0] = CRT_BACKGROUND_COLOR;
- clear_color[1] = CRT_BACKGROUND_COLOR;
- clear_color[2] = CRT_BACKGROUND_COLOR;
+ CRT_BACKGROUND_COLOR(clear_color);
pl_frame_clear_rgba(lr->gpu, &lr->target, clear_color);
lr->frame_cleared = true;
}
#endif
+ if (scr->osd.show || (result & RESULT_WAIT)) {
+ al_assert(lr->osd.prelude && lr->osd.header && lr->osd.binds);
+ if (!render_osd(lr, scr) && !lr->osd.logged_fail) {
+ log_error("Failed to render OSD.");
+ lr->osd.logged_fail = true;
+ }
+ }
+
if (pl_swapchain_submit_frame(lr->swapchain)) {
lr->have_frame = false;
} else {
@@ -571,10 +786,25 @@ static bool renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
void renderer_lp_free(struct camu_renderer **renderer)
{
struct camu_renderer_lp *lr = (struct camu_renderer_lp *)*renderer;
+ if (lr->have_frame) {
+ pl_swapchain_submit_frame(lr->swapchain);
+ lr->have_frame = false;
+ }
#ifdef CAMU_HAVE_SUBTITLES
if (lr->ass) ass_library_done(lr->ass);
#endif
if (lr->vr_emulation_hook[0]) pl_mpv_user_shader_destroy(&lr->vr_emulation_hook[0]);
+#ifdef USE_LIBPLACEBO_CACHE
+ if (lr->cache) {
+ FILE *cache_file = fopen("sink.cache", "wb");
+ if (cache_file) {
+ pl_cache_save_file(lr->cache, cache_file);
+ fclose(cache_file);
+ }
+ pl_cache_destroy(&lr->cache);
+ }
+#endif
+ if (lr->dispatch) pl_dispatch_destroy(&lr->dispatch);
if (lr->renderer) pl_renderer_destroy(&lr->renderer);
if (lr->swapchain) pl_swapchain_destroy(&lr->swapchain);
#if defined STELA_API_VULKAN