summaryrefslogtreecommitdiff
path: root/src/render
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2023-11-06 11:54:08 -0500
committerAndrew Opalach <andrew@akon.city> 2023-11-06 11:54:08 -0500
commita48a68cfb04b2020737c0adfb0e7667451a22b5c (patch)
treea92121897f84298c3798a4f2ba56de45cd100eab /src/render
downloadcamu-a48a68cfb04b2020737c0adfb0e7667451a22b5c.tar.gz
camu-a48a68cfb04b2020737c0adfb0e7667451a22b5c.tar.bz2
camu-a48a68cfb04b2020737c0adfb0e7667451a22b5c.zip
Add camu
- Subprojects temporarily omitted Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/render')
-rw-r--r--src/render/meson.build35
-rw-r--r--src/render/queue_libplacebo.c195
-rw-r--r--src/render/queue_libplacebo.h17
-rw-r--r--src/render/queue_tiger.c92
-rw-r--r--src/render/queue_tiger.h13
-rw-r--r--src/render/renderer.h45
-rw-r--r--src/render/renderer_libplacebo.c237
-rw-r--r--src/render/renderer_libplacebo.h38
-rw-r--r--src/render/renderer_tiger.c305
-rw-r--r--src/render/renderer_tiger.h31
10 files changed, 1008 insertions, 0 deletions
diff --git a/src/render/meson.build b/src/render/meson.build
new file mode 100644
index 0000000..cc11eea
--- /dev/null
+++ b/src/render/meson.build
@@ -0,0 +1,35 @@
+render_src = []
+render_deps = [common_deps]
+
+#linmath = subproject('linmath.h').get_variable('linmath')
+#libm = compiler.find_library('m', required: false)
+#if libm.found()
+# render_deps += [libm]
+#endif
+#render_src += ['renderer_tiger.c', 'queue_tiger.c']
+#render_deps += [linmath]
+
+libplacebo = dependency('libplacebo',
+ default_options: [
+ 'warning_level=2',
+ 'demos=false',
+ 'tests=false',
+ 'bench=false',
+ 'fuzz=false',
+ 'shaderc=enabled',
+ 'glslang=disabled',
+ 'opengl=enabled',
+ 'vulkan=disabled',
+ 'd3d11=disabled',
+ 'gl-proc-addr=disabled',
+ 'vk-proc-addr=disabled',
+ 'lcms=disabled',
+ 'dovi=disabled',
+ 'libdovi=disabled'
+ ], required: false, allow_fallback: true)
+if libplacebo.found()
+ render_src += ['renderer_libplacebo.c', 'queue_libplacebo.c']
+ render_deps += [libplacebo]
+endif
+
+render = declare_dependency(sources: render_src, dependencies: render_deps)
diff --git a/src/render/queue_libplacebo.c b/src/render/queue_libplacebo.c
new file mode 100644
index 0000000..ee64920
--- /dev/null
+++ b/src/render/queue_libplacebo.c
@@ -0,0 +1,195 @@
+#include <al/lib.h>
+#include <al/log.h>
+
+#include "../buffer/video.h"
+
+#include "queue_libplacebo.h"
+
+static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src,
+ struct pl_frame *out_frame)
+{
+ struct camu_frame *frame = src->frame_data;
+
+ u64 masks[4] = { 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 };
+ s32 pixel_stride = 4;
+
+ /*
+ switch (frame->fmt) {
+ case CAMU_PIXEL_FMT_RGBA:
+ break;
+ default:
+ al_log_error("frame_queue_libplacebo", "Unsuppored pixel format.");
+ return false;
+ }
+ */
+
+ struct pl_plane_data data = {
+ .type = PL_FMT_UNORM,
+ .width = frame->width,
+ .height = frame->height,
+ .pixel_stride = pixel_stride,
+ .row_stride = frame->width * pixel_stride,
+ .pixels = frame->data
+ };
+
+ pl_plane_data_from_mask(&data, masks);
+
+ struct pl_plane plane = { 0 };
+ bool ok = pl_upload_plane(gpu, &plane, tex, &data);
+
+ al_free(frame);
+
+ if (!ok) {
+ al_log_error("frame_queue_libplacebo", "Failed to upload texture.");
+ return false;
+ }
+
+ out_frame->num_planes = 1;
+ out_frame->planes[0] = plane;
+ out_frame->repr = pl_color_repr_unknown;
+ out_frame->color = pl_color_space_unknown;
+ out_frame->crop = (pl_rect2df){ 0, 0, frame->width, frame->height };
+ //out_frame->profile =
+
+ return true;
+}
+
+static void unmap_frame(pl_gpu gpu, struct pl_frame *frame, const struct pl_source_frame *src)
+{
+ (void)gpu;
+ (void)frame;
+ (void)src;
+}
+
+static void discard_frame(const struct pl_source_frame *src)
+{
+ struct camu_frame *frame = src->frame_data;
+ al_free(frame);
+}
+
+static void queue_lp_push(struct camu_frame_queue *queue, struct camu_frame *frame, f64 pts)
+{
+ struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
+ pl_queue_push(lq->queue, &(struct pl_source_frame){
+ .pts = pts,
+ .duration = 0.0,
+ .map = map_frame,
+ .unmap = unmap_frame,
+ .discard = discard_frame,
+ .frame_data = frame,
+ });
+}
+
+#ifdef HAVE_FFMPEG
+static bool map_av_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src,
+ struct pl_frame *out_frame)
+{
+ AVFrame *frame = src->frame_data;
+ struct camu_video_buffer *buf = frame->opaque;
+ AVStream *stream = buf->stream->av.stream;
+
+ bool ok = pl_map_avframe_ex(gpu, out_frame, pl_avframe_params(
+ .frame = frame,
+ .tex = tex,
+ .map_dovi = false
+ ));
+
+ av_frame_free(&frame);
+
+ if (!ok) {
+ al_log_error("frame_queue_libplacebo", "Failed to map AVFrame.");
+ return false;
+ }
+
+ pl_frame_copy_stream_props(out_frame, stream);
+
+ return true;
+}
+
+static void unmap_av_frame(pl_gpu gpu, struct pl_frame *frame, const struct pl_source_frame *src)
+{
+ (void)src;
+ pl_unmap_avframe(gpu, frame);
+}
+
+static void discard_av_frame(const struct pl_source_frame *src)
+{
+ AVFrame *frame = src->frame_data;
+ av_frame_free(&frame);
+ al_log_info("frame_queue_libplacebo", "Dropped frame with PTS %.3f.", src->pts);
+}
+
+static void queue_lp_push_av_frame(struct camu_frame_queue *queue, AVFrame *frame, f64 pts)
+{
+ struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
+ struct camu_video_buffer *buf = frame->opaque;
+ pl_queue_push(lq->queue, &(struct pl_source_frame){
+ .pts = pts,
+ .duration = buf->avg_frame_duration,
+ .map = map_av_frame,
+ .unmap = unmap_av_frame,
+ .discard = discard_av_frame,
+ .frame_data = frame,
+ .first_field = pl_field_from_avframe(frame)
+ });
+}
+#endif
+
+static void queue_lp_flush(struct camu_frame_queue *queue)
+{
+ struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
+ pl_queue_push(lq->queue, NULL);
+}
+
+static s32 queue_lp_count(struct camu_frame_queue *queue)
+{
+ struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
+ return pl_queue_num_frames(lq->queue);
+}
+
+static u8 queue_lp_read(struct camu_frame_queue *queue, f64 pts, void *out)
+{
+ struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
+ lq->params.pts = pts;
+ switch (pl_queue_update(lq->queue, (struct pl_frame_mix *)out, &lq->params)) {
+ case PL_QUEUE_OK:
+ return CAMU_QUEUE_OK;
+ case PL_QUEUE_MORE:
+ return CAMU_QUEUE_MORE;
+ case PL_QUEUE_EOF:
+ return CAMU_QUEUE_EOF;
+ case PL_QUEUE_ERR:
+ default:
+ return CAMU_QUEUE_ERR;
+ }
+}
+
+static void queue_lp_reset(struct camu_frame_queue *queue)
+{
+ struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
+ pl_queue_push(lq->queue, NULL);
+ pl_queue_reset(lq->queue);
+}
+
+static void queue_lp_free(struct camu_frame_queue **queue)
+{
+ struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)*queue;
+ pl_queue_destroy(&lq->queue);
+ al_free(lq);
+ *queue = NULL;
+}
+
+struct camu_frame_queue *camu_frame_queue_lp_create(void)
+{
+ struct camu_frame_queue_lp *lq = al_alloc_object(struct camu_frame_queue_lp);
+ lq->q.push = queue_lp_push;
+#ifdef HAVE_FFMPEG
+ lq->q.push_av_frame = queue_lp_push_av_frame;
+#endif
+ lq->q.flush = queue_lp_flush;
+ lq->q.count = queue_lp_count;
+ lq->q.read = queue_lp_read;
+ lq->q.reset = queue_lp_reset;
+ lq->q.free = queue_lp_free;
+ return (struct camu_frame_queue *)lq;
+}
diff --git a/src/render/queue_libplacebo.h b/src/render/queue_libplacebo.h
new file mode 100644
index 0000000..7da99ca
--- /dev/null
+++ b/src/render/queue_libplacebo.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "../buffer/frame_queue.h"
+
+#include <libplacebo/utils/frame_queue.h>
+#include <libplacebo/utils/upload.h>
+#ifdef HAVE_FFMPEG
+#include <libplacebo/utils/libav.h>
+#endif
+
+struct camu_frame_queue_lp {
+ struct camu_frame_queue q;
+ pl_queue queue;
+ struct pl_queue_params params;
+};
+
+struct camu_frame_queue *camu_frame_queue_lp_create(void);
diff --git a/src/render/queue_tiger.c b/src/render/queue_tiger.c
new file mode 100644
index 0000000..575b826
--- /dev/null
+++ b/src/render/queue_tiger.c
@@ -0,0 +1,92 @@
+#include <al/lib.h>
+
+#include "renderer_tiger.h"
+#include "queue_tiger.h"
+
+static void upload_texture(struct camu_frame_queue_tiger *tq, s32 width, s32 height,
+ u8 *data, s32 linesize)
+{
+ glGenTextures(1, &tq->texture_id);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, tq->texture_id);
+ //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+ //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ //if (frame->flags & CAMU_FRAME_DEVICE_ALLOCATED) {
+ // glBindBuffer(GL_PIXEL_UNPACK_BUFFER, tq->tr->texture_buffer);
+ //}
+ //if (linesize) {
+ // glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
+ //}
+ (void)linesize;
+ //glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+ //glGenerateMipmap(GL_TEXTURE_2D);
+}
+
+static void queue_tiger_push(struct camu_frame_queue *queue, struct camu_frame *frame, f64 pts)
+{
+ struct camu_frame_queue_tiger *tq = (struct camu_frame_queue_tiger *)queue;
+ (void)pts;
+ upload_texture(tq, frame->width, frame->height, frame->data, 0);
+}
+
+#ifdef HAVE_FFMPEG
+static void queue_tiger_push_av_frame(struct camu_frame_queue *queue, AVFrame *frame, f64 pts)
+{
+ struct camu_frame_queue_tiger *tq = (struct camu_frame_queue_tiger *)queue;
+ (void)pts;
+ upload_texture(tq, frame->width, frame->height, frame->data[0], frame->linesize[0]);
+}
+#endif
+
+static void queue_tiger_flush(struct camu_frame_queue *queue)
+{
+ (void)queue;
+}
+
+static s32 queue_tiger_count(struct camu_frame_queue *queue)
+{
+ struct camu_frame_queue_tiger *tq = (struct camu_frame_queue_tiger *)queue;
+ (void)tq;
+ return 1;
+}
+
+static u8 queue_tiger_read(struct camu_frame_queue *queue, f64 pts, void *out)
+{
+ struct camu_frame_queue_tiger *tq = (struct camu_frame_queue_tiger *)queue;
+ (void)pts;
+ *((GLuint *)out) = tq->texture_id;
+ return CAMU_QUEUE_OK;
+}
+
+static void queue_tiger_reset(struct camu_frame_queue *queue)
+{
+ struct camu_frame_queue_tiger *tq = (struct camu_frame_queue_tiger *)queue;
+ (void)tq;
+}
+
+static void queue_tiger_free(struct camu_frame_queue **queue)
+{
+ struct camu_frame_queue_tiger *tq = (struct camu_frame_queue_tiger *)*queue;
+ glDeleteTextures(1, &tq->texture_id);
+ al_free(tq);
+ *queue = NULL;
+}
+
+struct camu_frame_queue *camu_frame_queue_tiger_create(void)
+{
+ struct camu_frame_queue_tiger *tq = al_alloc_object(struct camu_frame_queue_tiger);
+ tq->q.push = queue_tiger_push;
+#ifdef HAVE_FFMPEG
+ tq->q.push_av_frame = queue_tiger_push_av_frame;
+#endif
+ tq->q.flush = queue_tiger_flush;
+ tq->q.count = queue_tiger_count;
+ tq->q.read = queue_tiger_read;
+ tq->q.reset = queue_tiger_reset;
+ tq->q.free = queue_tiger_free;
+ return (struct camu_frame_queue *)tq;
+}
diff --git a/src/render/queue_tiger.h b/src/render/queue_tiger.h
new file mode 100644
index 0000000..1524a29
--- /dev/null
+++ b/src/render/queue_tiger.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <skh/egl.h>
+
+#include "../buffer/frame_queue.h"
+
+struct camu_frame_queue_tiger {
+ struct camu_frame_queue q;
+ GLuint texture_id;
+ struct camu_renderer_tiger *tr;
+};
+
+struct camu_frame_queue *camu_frame_queue_tiger_create(void);
diff --git a/src/render/renderer.h b/src/render/renderer.h
new file mode 100644
index 0000000..61965ed
--- /dev/null
+++ b/src/render/renderer.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <al/types.h>
+#include <skh/window.h>
+#ifdef HAVE_FFMPEG
+#include <libavcodec/avcodec.h>
+#endif
+
+#include "../buffer/frame_queue.h"
+
+#if defined SEKIHI_API_OPENGL
+#define CAMU_RENDERER_OPENGL
+#elif defined SEKIHI_API_VULKAN
+#define CAMU_RENDERER_VULKAN
+#endif
+
+struct camu_screen;
+struct camu_renderer {
+ bool (*create_renderer)(
+ struct camu_renderer *, s32 *, s32 *,
+ bool (*poll)(struct skh_window *, bool),
+#if defined CAMU_RENDERER_VULKAN
+ VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL (*get_vk_proc_address)(VkInstance, const char *),
+ VkResult (*vk_create_surface)(void *, VkInstance, VkSurfaceKHR *),
+ const char *const *(*vk_get_extensions)(u32 *),
+#elif defined CAMU_RENDERER_OPENGL
+ void (*get_gl_proc_address(char const *procname))(void),
+ bool (*gl_load_loader)(void *),
+ bool (*gl_make_current)(void *),
+ void (*gl_release_current)(void *),
+ void (*gl_swap_buffers)(void *),
+#elif defined CAMU_RENDERER_DX11
+#endif
+ void *priv);
+ struct camu_frame_queue *(*create_queue)(struct camu_renderer *);
+ void (*resize)(struct camu_renderer *, s32 *, s32 *);
+ void (*render)(struct camu_renderer *, struct camu_screen *);
+#ifdef HAVE_FFMPEG
+ s32 (*get_buffer2)(struct AVCodecContext *s, AVFrame *frame, s32 flags);
+ void *opaque;
+#endif
+ void *(*get_device_ptr)(struct camu_renderer *, size_t);
+ void (*commit_device_ptr)(struct camu_renderer *);
+ void (*free)(struct camu_renderer **);
+};
diff --git a/src/render/renderer_libplacebo.c b/src/render/renderer_libplacebo.c
new file mode 100644
index 0000000..019a5ce
--- /dev/null
+++ b/src/render/renderer_libplacebo.c
@@ -0,0 +1,237 @@
+#include <al/lib.h>
+#include <al/log.h>
+#include <aki/file.h>
+
+#include "../screen/screen.h"
+#include "../util/color_palette.h"
+
+#include "renderer_libplacebo.h"
+#include "queue_libplacebo.h"
+
+static f32 clear_color[4] = { 0.0f, 0.0f, 0.0f, 1.f };
+
+static void renderer_lp_resize(struct camu_renderer *renderer, s32 *width, s32 *height)
+{
+ struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer;
+ if (lr->swapchain) pl_swapchain_resize(lr->swapchain, width, height);
+}
+
+static bool renderer_lp_create_renderer(struct camu_renderer *renderer, s32 *width, s32 *height,
+ bool (*poll)(struct skh_window *, bool),
+#if defined CAMU_RENDERER_VULKAN
+ VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL (*get_vk_proc_address)(VkInstance, const char *),
+ VkResult (*vk_create_surface)(void *, VkInstance, VkSurfaceKHR *),
+ const char *const *(*vk_get_extensions)(u32 *),
+#elif defined CAMU_RENDERER_OPENGL
+ void (*get_gl_proc_address(char const *procname))(void),
+ bool (*gl_load_loader)(void *),
+ bool (*gl_make_current)(void *),
+ void (*gl_release_current)(void *),
+ void (*gl_swap_buffers)(void *),
+#endif
+ void *priv)
+{
+ struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer;
+
+ clear_color[0] = ((global_color_palette.colors[0] >> 16) & 0xff) / 255.f;
+ clear_color[1] = ((global_color_palette.colors[0] >> 8) & 0xff) / 255.f;
+ clear_color[2] = ((global_color_palette.colors[0]) & 0xff) / 255.f;
+
+ lr->logger = pl_log_create(PL_API_VER, pl_log_params(
+ .log_cb = pl_log_color,
+ .log_level = PL_LOG_NONE,
+// .log_level = PL_LOG_INFO,
+ ));
+
+#if defined CAMU_RENDERER_VULKAN
+ VkResult err;
+
+ u32 num;
+ lr->vk_inst = pl_vk_inst_create(lr->logger, pl_vk_inst_params(
+ .get_proc_addr = get_vk_proc_address,
+ .debug = false,
+ .extensions = vk_get_extensions(&num),
+ .num_extensions = num,
+ ));
+
+ if (!lr->vk_inst) {
+ al_log_error("render_libplacebo", "Failed to create vulkan instance.");
+ }
+
+ err = vk_create_surface(priv, lr->vk_inst->instance, &lr->surface);
+ if (err != VK_SUCCESS) {
+ al_log_error("render_libplacebo", "Failed to create vulkan surface.");
+ }
+
+ lr->vk = pl_vulkan_create(lr->logger, pl_vulkan_params(
+ .allow_software = true,
+ .instance = lr->vk_inst->instance,
+ .get_proc_addr = lr->vk_inst->get_proc_addr,
+ .surface = lr->surface
+ ));
+
+ if (!lr->vk) {
+ al_log_error("render_libplacebo", "Failed to create vulkan device.");
+ }
+
+ lr->swapchain = pl_vulkan_create_swapchain(lr->vk, pl_vulkan_swapchain_params(
+ .surface = lr->surface,
+ .present_mode = VK_PRESENT_MODE_FIFO_KHR
+ ));
+
+ if (!lr->swapchain) {
+ al_log_error("render_libplacebo", "Failed to create vulkan swapchain.");
+ }
+
+ lr->gpu = lr->vk->gpu;
+#elif defined CAMU_RENDERER_OPENGL
+ (void)gl_load_loader;
+ lr->gl = pl_opengl_create(lr->logger, pl_opengl_params(
+ .allow_software = true,
+ .debug = false,
+ .make_current = gl_make_current,
+ .release_current = gl_release_current,
+ .get_proc_addr = get_gl_proc_address,
+ .get_proc_addr_ex = NULL,
+ .priv = priv,
+ ));
+
+ if (!lr->gl) {
+ al_log_error("render_libplacebo", "Failed to create GL device.");
+ }
+
+ lr->swapchain = pl_opengl_create_swapchain(lr->gl, pl_opengl_swapchain_params(
+ .swap_buffers = gl_swap_buffers,
+ .priv = priv
+ ));
+
+ if (!lr->swapchain) {
+ al_log_error("render_libplacebo", "Failed to create GL swapchain.");
+ }
+
+ lr->gpu = lr->gl->gpu;
+#endif
+
+ lr->renderer = pl_renderer_create(lr->logger, lr->gpu);
+
+ renderer_lp_resize(renderer, width, height);
+
+#define PRE_SWAPS 2
+ // See renderer_tiger.c:122.
+ for (u32 i = 0; i < PRE_SWAPS; i++) {
+ poll(priv, true);
+ struct pl_swapchain_frame frame;
+ pl_swapchain_start_frame(lr->swapchain, &frame);
+ struct pl_frame target;
+ pl_frame_from_swapchain(&target, &frame);
+ pl_frame_clear_rgba(lr->gpu, &target, clear_color);
+ pl_swapchain_submit_frame(lr->swapchain);
+ pl_swapchain_swap_buffers(lr->swapchain);
+ }
+
+ al_memset(&lr->params, 0, sizeof(struct pl_render_params));
+ lr->params = pl_render_default_params;
+ //lr->params = pl_render_high_quality_params;
+ lr->params.upscaler = &pl_filter_bicubic;
+ // Clear manually so we can draw multiple images per frame.
+ lr->params.skip_target_clearing = true;
+ lr->params.skip_caching_single_frame = true;
+
+#if 0
+ struct aki_file file;
+ if (aki_file_open(&file, al_str_c(""))) {
+ char *str;
+ size_t len = aki_file_read_as_c_str(&file, &str);
+ const struct pl_hook *hook = pl_mpv_user_shader_parse(lr->gpu, (const char *)str, len);
+ const struct pl_hook **hooks = al_malloc(sizeof(struct pl_hook *));
+ hooks[0] = al_malloc(sizeof(struct pl_hook));
+ al_memcpy((void *)hooks[0], (void *)hook, sizeof(struct pl_hook));
+ lr->params.hooks = hooks;
+ lr->params.num_hooks = 1;
+ }
+#endif
+
+#ifdef HAVE_FFMPEG
+ //lr->r.get_buffer2 = pl_get_buffer2;
+ //lr->r.opaque = &lr->gpu;
+#endif
+
+ return true;
+}
+
+static struct camu_frame_queue *renderer_lp_create_queue(struct camu_renderer *renderer)
+{
+ struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer;
+ struct camu_frame_queue *queue = camu_frame_queue_lp_create();
+ struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
+ lq->queue = pl_queue_create(lr->gpu);
+ al_memset(&lq->params, 0, sizeof(struct pl_queue_params));
+ lq->params.interpolation_threshold = 0.01;
+ lq->params.radius = pl_frame_mix_radius(&lr->params);
+ lq->params.timeout = 0; // vsync
+ return queue;
+}
+
+static void renderer_lp_render(struct camu_renderer *renderer, struct camu_screen *scr)
+{
+ struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer;
+
+ struct pl_swapchain_frame frame;
+ pl_swapchain_start_frame(lr->swapchain, &frame);
+
+ struct pl_frame target;
+ pl_frame_from_swapchain(&target, &frame);
+
+#ifdef CAMU_SCREEN_THREADED
+ if (al_atomic_bool_load(&scr->queued, AL_ATOMIC_RELAXED)) {
+ camu_screen_run_queue(scr);
+ }
+#endif
+
+ pl_frame_clear_rgba(lr->gpu, &target, clear_color);
+
+ struct pl_frame_mix mix;
+ if (scr->videos.size > 0) {
+ struct camu_screen_video *video = &al_array_last(scr->videos);
+ //al_array_foreach_ptr(scr->videos, i, video) {
+ if (camu_video_buffer_read(video->buf, &mix) && mix.frames) {
+ target.crop = mix.frames[0]->crop;
+ target.crop.x1 *= video->view.zoom;
+ target.crop.y1 *= video->view.zoom;
+ target.crop.x0 += video->view.x_offset;
+ target.crop.y0 += video->view.y_offset;
+ target.crop.x1 += video->view.x_offset;
+ target.crop.y1 += video->view.y_offset;
+ pl_render_image_mix(lr->renderer, &mix, &target, &lr->params);
+ }
+ //}
+ }
+
+ pl_swapchain_submit_frame(lr->swapchain);
+ pl_swapchain_swap_buffers(lr->swapchain);
+}
+
+static void renderer_lp_free(struct camu_renderer **renderer)
+{
+ struct camu_renderer_lp *lr = (struct camu_renderer_lp *)*renderer;
+ if (lr->renderer) pl_renderer_destroy(&lr->renderer);
+ if (lr->swapchain) pl_swapchain_destroy(&lr->swapchain);
+#if defined CAMU_RENDERER_VULKAN
+#elif defined CAMU_RENDERER_OPENGL
+ if (lr->gl) pl_opengl_destroy(&lr->gl);
+#endif
+ if (lr->logger) pl_log_destroy(&lr->logger);
+ al_free(lr);
+ *renderer = NULL;
+}
+
+struct camu_renderer *camu_renderer_lp_create(void)
+{
+ struct camu_renderer_lp *lr = al_alloc_object(struct camu_renderer_lp);
+ lr->r.create_renderer = renderer_lp_create_renderer;
+ lr->r.create_queue = renderer_lp_create_queue;
+ lr->r.resize = renderer_lp_resize;
+ lr->r.render = renderer_lp_render;
+ lr->r.free = renderer_lp_free;
+ return (struct camu_renderer *)lr;
+}
diff --git a/src/render/renderer_libplacebo.h b/src/render/renderer_libplacebo.h
new file mode 100644
index 0000000..661faff
--- /dev/null
+++ b/src/render/renderer_libplacebo.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <libplacebo/common.h>
+#include <libplacebo/opengl.h>
+#include <libplacebo/renderer.h>
+#include <libplacebo/shaders/lut.h>
+#include <libplacebo/colorspace.h>
+
+#include "renderer.h"
+
+#if defined CAMU_RENDERER_VULKAN
+#include <libplacebo/vulkan.h>
+#endif
+
+struct camu_renderer_lp_resource {
+ pl_tex tex;
+ struct pl_plane plane;
+};
+
+struct camu_renderer_lp {
+ struct camu_renderer r;
+#if defined CAMU_RENDERER_VULKAN
+ VkSurfaceKHR surface;
+ pl_vulkan vk;
+ pl_vk_inst vk_inst;
+#elif defined CAMU_RENDERER_OPENGL
+ pl_opengl gl;
+#endif
+ pl_gpu gpu;
+ pl_log logger;
+ pl_swapchain swapchain;
+ pl_renderer renderer;
+ struct pl_render_params params;
+ struct pl_overlay overlays[10];
+ s32 overlay_count;
+};
+
+struct camu_renderer *camu_renderer_lp_create(void);
diff --git a/src/render/renderer_tiger.c b/src/render/renderer_tiger.c
new file mode 100644
index 0000000..7cbbbb9
--- /dev/null
+++ b/src/render/renderer_tiger.c
@@ -0,0 +1,305 @@
+#include <al/lib.h>
+#include <al/log.h>
+#include <linmath.h>
+
+#include "../screen/view.h"
+#include "../screen/screen.h"
+#include "../util/color_palette.h"
+
+#include "renderer_tiger.h"
+#include "queue_tiger.h"
+
+#ifdef CAMU_RENDERER_OPENGL
+static const GLfloat default_vertices[] = {
+ 0.f, 0.f, 0.f, 0.f, 0.f,
+ 0.f, 1.f, 0.f, 0.f, 1.f,
+ 1.f, 0.f, 0.f, 1.f, 0.f,
+ 1.f, 0.f, 0.f, 1.f, 0.f,
+ 0.f, 1.f, 0.f, 0.f, 1.f,
+ 1.f, 1.f, 0.f, 1.f, 1.f
+};
+
+static f32 clear_color[4] = { 0.0f, 0.0f, 0.0f, 1.f };
+
+static const GLchar *default_vs = \
+ "#version 100\n" \
+ "attribute vec3 a_Position;\n" \
+ "attribute vec2 a_TexCoord;\n" \
+ "varying vec2 v_TexCoord;\n" \
+ "uniform mat4 g_ModelViewProjectionMatrix;\n" \
+ "void main() {\n" \
+ " gl_Position = g_ModelViewProjectionMatrix * vec4(a_Position, 1.0);\n" \
+ " v_TexCoord = a_TexCoord;\n" \
+ "}";
+
+static const GLchar *default_fs = \
+ "#version 100\n" \
+ "precision mediump float;\n" \
+ "varying vec2 v_TexCoord;\n" \
+ "uniform sampler2D g_Texture0;\n" \
+ "void main() {\n" \
+ " gl_FragColor = texture2D(g_Texture0, v_TexCoord);\n" \
+ "}";
+
+static bool compile(GLuint shader, const GLchar *source, GLchar *error)
+{
+ glShaderSource(shader, 1, &source, 0);
+ glCompileShader(shader);
+
+ GLint status;
+
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+
+ if (status == GL_FALSE) {
+ glGetShaderInfoLog(shader, 256, NULL, error);
+ return false;
+ }
+
+ return true;
+}
+
+static bool compile_shaders(struct camu_renderer_tiger *tr)
+{
+ tr->program = glCreateProgram();
+
+ GLchar error[256];
+
+ GLuint vs_shader = glCreateShader(GL_VERTEX_SHADER);
+ if (!compile(vs_shader, default_vs, error)) {
+ al_log_error("render_tiger", "Failed to compile vertex shader (%.*s)",
+ sizeof(error), error);
+ return false;
+ }
+
+ GLuint fs_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ if (!compile(fs_shader, default_fs, error)) {
+ al_log_error("render_tiger", "Failed to compile fragment shader (%.*s)",
+ sizeof(error), error);
+ return false;
+ }
+
+ glAttachShader(tr->program, vs_shader);
+ glAttachShader(tr->program, fs_shader);
+ glLinkProgram(tr->program);
+
+ return true;
+}
+#endif
+
+static void renderer_tiger_resize(struct camu_renderer *renderer, s32 *width, s32 *height)
+{
+ struct camu_renderer_tiger *tr = (struct camu_renderer_tiger *)renderer;
+ (void)tr;
+#ifdef CAMU_RENDERER_OPENGL
+ glViewport(0, 0, *width, *height);
+#endif
+}
+
+static bool renderer_tiger_create_renderer(struct camu_renderer *renderer, s32 *width, s32 *height,
+ bool (*poll)(struct skh_window *, bool),
+#if defined CAMU_RENDERER_VULKAN
+ VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL (*get_vk_proc_address)(VkInstance, const char *),
+ void (*vk_create_surface)(void *, VkInstance, VkSurfaceKHR *),
+ const char *const *(*vk_get_extensions)(u32 *),
+#elif defined CAMU_RENDERER_OPENGL
+ void (*get_gl_proc_address(char const *procname))(void),
+ bool (*gl_load_loader)(void *),
+ bool (*gl_make_current)(void *),
+ void (*gl_release_current)(void *),
+ void (*gl_swap_buffers)(void *),
+#endif
+ void *priv)
+{
+ struct camu_renderer_tiger *tr = (struct camu_renderer_tiger *)renderer;
+#ifdef CAMU_RENDERER_OPENGL
+ (void)get_gl_proc_address;
+
+ tr->load_loader = gl_load_loader;
+ tr->make_current = gl_make_current;
+ tr->release_current = gl_release_current;
+ tr->swap_buffers = gl_swap_buffers;
+ tr->priv = priv;
+
+ tr->make_current(tr->priv);
+
+ if (!tr->load_loader(tr->priv)) return false;
+
+ renderer_tiger_resize(renderer, width, height);
+
+ clear_color[0] = ((global_color_palette.colors[0] >> 16) & 0xff) / 255.f;
+ clear_color[1] = ((global_color_palette.colors[0] >> 8) & 0xff) / 255.f;
+ clear_color[2] = ((global_color_palette.colors[0]) & 0xff) / 255.f;
+
+ glClearColor(clear_color[0], clear_color[1], clear_color[2], 1.f);
+
+#define PRE_SWAPS 2
+ // Hack? to make the window appear quicker. Only tested on linux/wayland.
+ for (u32 i = 0; i < PRE_SWAPS; i++) {
+ poll(priv, true);
+ glClear(GL_COLOR_BUFFER_BIT);
+ tr->swap_buffers(tr->priv);
+ }
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glGenBuffers(1, &tr->vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, tr->vbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(default_vertices), default_vertices, GL_STATIC_DRAW);
+
+ if (!compile_shaders(tr)) return false;
+
+ glUseProgram(tr->program);
+
+ GLint pos = glGetAttribLocation(tr->program, "a_Position");
+ glVertexAttribPointer(pos, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void *)0);
+ glEnableVertexAttribArray(pos);
+ GLint texcoord = glGetAttribLocation(tr->program, "a_TexCoord");
+ glVertexAttribPointer(texcoord, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void *)12);
+ glEnableVertexAttribArray(texcoord);
+
+ //glGenBuffers(1, &tr->texture_buffer);
+#endif
+
+#ifdef HAVE_FFMPEG
+ tr->r.get_buffer2 = NULL;
+ tr->r.opaque = NULL;
+#endif
+
+ return true;
+}
+
+static struct camu_frame_queue *renderer_tiger_create_queue(struct camu_renderer *renderer)
+{
+#ifdef CAMU_RENDERER_OPENGL
+ struct camu_renderer_tiger *tr = (struct camu_renderer_tiger *)renderer;
+ struct camu_frame_queue *q = camu_frame_queue_tiger_create();
+ struct camu_frame_queue_tiger *tq = (struct camu_frame_queue_tiger *)q;
+ tq->tr = tr;
+ return q;
+#endif
+}
+
+static void set_uniform(struct camu_renderer_tiger *tr, const GLchar *name,
+ u8 type, void *value)
+{
+#ifdef CAMU_RENDERER_OPENGL
+ GLint loc = glGetUniformLocation(tr->program, name);
+ if (loc == -1) return;
+
+ switch (type)
+ {
+ case TYPE_FLOAT:
+ glUniform1f(loc, *((GLfloat *)value));
+ break;
+ case TYPE_SAMPLER2D:
+ glUniform1i(loc, (GLuint)(*((GLfloat *)value)));
+ break;
+ case TYPE_VEC2:
+ glUniform2fv(loc, 1, (GLfloat *)value);
+ break;
+ case TYPE_VEC3:
+ glUniform3fv(loc, 1, (GLfloat *)value);
+ break;
+ case TYPE_VEC4:
+ glUniform4fv(loc, 1, (GLfloat *)value);
+ break;
+ case TYPE_MAT4:
+ glUniformMatrix4fv(loc, 1, GL_FALSE, (GLfloat *)value);
+ break;
+ case TYPE_MAT3:
+ glUniformMatrix3fv(loc, 1, GL_FALSE, (GLfloat *)value);
+ break;
+ case TYPE_INVALID:
+ break;
+ }
+#endif
+}
+
+#ifdef CAMU_RENDERER_OPENGL
+static mat4x4 model;
+static GLuint texture0 = 0;
+#endif
+
+static void renderer_tiger_render(struct camu_renderer *renderer, struct camu_screen *scr)
+{
+#ifdef CAMU_RENDERER_OPENGL
+ struct camu_renderer_tiger *tr = (struct camu_renderer_tiger *)renderer;
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glUseProgram(tr->program);
+ glBindBuffer(GL_ARRAY_BUFFER, tr->vbo);
+ glActiveTexture(GL_TEXTURE0);
+
+#ifdef CAMU_SCREEN_THREADED
+ if (al_atomic_bool_load(&scr->queued, AL_ATOMIC_RELAXED)) {
+ camu_screen_run_queue(scr);
+ }
+#endif
+
+ if (scr->videos.size > 0) {
+ struct camu_screen_video *video = &al_array_last(scr->videos);
+
+ GLuint texture_id;
+ camu_video_buffer_read(video->buf, &texture_id);
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+
+ mat4x4_ortho(model, 0.f, (f32)scr->width, (f32)scr->height, 0.f, -1.f, 1.f);
+ mat4x4_translate_in_place(model, (f32)video->view.x_offset, (f32)video->view.y_offset, 0.f);
+ mat4x4_scale_aniso(model, model, (f32)video->view.zoom, (f32)video->view.zoom, 1.f);
+ mat4x4_scale_aniso(model, model, (f32)video->view.width, (f32)video->view.height, 1.f);
+ set_uniform(tr, "g_ModelViewProjectionMatrix", TYPE_MAT4, model);
+ set_uniform(tr, "g_Texture0", TYPE_SAMPLER2D, &texture0);
+
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+ }
+
+ tr->swap_buffers(tr->priv);
+#endif
+}
+
+static void *renderer_tiger_get_device_ptr(struct camu_renderer *renderer, size_t length)
+{
+ (void)renderer;
+ (void)length;
+ return NULL;
+#ifdef CAMU_RENDERER_OPENGL
+ //struct camu_renderer_tiger *tr = (struct camu_renderer_tiger *)renderer;
+ //glBindBuffer(GL_PIXEL_UNPACK_BUFFER, tr->texture_buffer);
+ //glBufferData(GL_PIXEL_UNPACK_BUFFER, length, NULL, GL_STREAM_DRAW);
+ //return glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, length, GL_MAP_WRITE_BIT);
+#endif
+}
+
+static void renderer_tiger_commit_device_ptr(struct camu_renderer *renderer)
+{
+ (void)renderer;
+#ifdef CAMU_RENDERER_OPENGL
+ //struct camu_renderer_tiger *tr = (struct camu_renderer_tiger *)renderer;
+ //glBindBuffer(GL_PIXEL_UNPACK_BUFFER, tr->texture_buffer);
+ //glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
+#endif
+}
+
+static void renderer_tiger_free(struct camu_renderer **renderer)
+{
+ struct camu_renderer_tiger *tr = (struct camu_renderer_tiger *)*renderer;
+ glDeleteProgram(tr->program);
+ glDeleteBuffers(1, &tr->vbo);
+ al_free(tr);
+ *renderer = NULL;
+}
+
+struct camu_renderer *camu_renderer_tiger_create(void)
+{
+ struct camu_renderer_tiger *tr = al_alloc_object(struct camu_renderer_tiger);
+ tr->r.create_renderer = renderer_tiger_create_renderer;
+ tr->r.create_queue = renderer_tiger_create_queue;
+ tr->r.resize = renderer_tiger_resize;
+ tr->r.render = renderer_tiger_render;
+ tr->r.get_device_ptr = renderer_tiger_get_device_ptr;
+ tr->r.commit_device_ptr = renderer_tiger_commit_device_ptr;
+ tr->r.free = renderer_tiger_free;
+ return (struct camu_renderer *)tr;
+}
diff --git a/src/render/renderer_tiger.h b/src/render/renderer_tiger.h
new file mode 100644
index 0000000..e9a7eb0
--- /dev/null
+++ b/src/render/renderer_tiger.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <skh/egl.h>
+
+#include "renderer.h"
+
+enum {
+ TYPE_INVALID = 0,
+ TYPE_FLOAT,
+ TYPE_VEC4,
+ TYPE_VEC3,
+ TYPE_VEC2,
+ TYPE_MAT4,
+ TYPE_MAT3,
+ TYPE_SAMPLER2D
+};
+
+struct camu_renderer_tiger {
+ struct camu_renderer r;
+ GLuint vao;
+ GLuint vbo;
+ GLuint program;
+ GLuint texture_buffer;
+ bool (*load_loader)(void *);
+ bool (*make_current)(void *);
+ void (*release_current)(void *);
+ void (*swap_buffers)(void *);
+ void *priv;
+};
+
+struct camu_renderer *camu_renderer_tiger_create(void);