mirror of
https://github.com/CTCaer/RetroArch.git
synced 2024-12-25 12:17:04 +00:00
Merge pull request #649 from libretro/cpu-filter
Revamped soft-filter API
This commit is contained in:
commit
aefb91e2aa
1
Makefile
1
Makefile
@ -299,6 +299,7 @@ endif
|
||||
|
||||
ifeq ($(HAVE_DYLIB), 1)
|
||||
LIBS += $(DYLIB_LIB)
|
||||
OBJ += gfx/filter.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_FREETYPE), 1)
|
||||
|
@ -194,6 +194,7 @@ endif
|
||||
|
||||
ifeq ($(HAVE_DYLIB), 1)
|
||||
DEFINES += -DHAVE_DYLIB
|
||||
OBJ += gfx/filter.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_STDIN_CMD), 1)
|
||||
|
96
driver.c
96
driver.c
@ -1284,28 +1284,15 @@ void uninit_audio(void)
|
||||
#ifdef HAVE_DYLIB
|
||||
void rarch_deinit_filter(void)
|
||||
{
|
||||
g_extern.filter.active = false;
|
||||
|
||||
if (g_extern.filter.lib)
|
||||
dylib_close(g_extern.filter.lib);
|
||||
g_extern.filter.lib = NULL;
|
||||
|
||||
rarch_softfilter_free(g_extern.filter.filter);
|
||||
free(g_extern.filter.buffer);
|
||||
free(g_extern.filter.colormap);
|
||||
free(g_extern.filter.scaler_out);
|
||||
g_extern.filter.buffer = NULL;
|
||||
g_extern.filter.colormap = NULL;
|
||||
g_extern.filter.scaler_out = NULL;
|
||||
|
||||
scaler_ctx_gen_reset(&g_extern.filter.scaler);
|
||||
memset(&g_extern.filter.scaler, 0, sizeof(g_extern.filter.scaler));
|
||||
memset(&g_extern.filter, 0, sizeof(g_extern.filter));
|
||||
}
|
||||
|
||||
void rarch_init_filter(bool rgb32)
|
||||
{
|
||||
unsigned i;
|
||||
if (g_extern.filter.active)
|
||||
return;
|
||||
rarch_deinit_filter();
|
||||
if (!*g_settings.video.filter_path)
|
||||
return;
|
||||
|
||||
@ -1315,14 +1302,6 @@ void rarch_init_filter(bool rgb32)
|
||||
return;
|
||||
}
|
||||
|
||||
RARCH_LOG("Loading bSNES filter from \"%s\"\n", g_settings.video.filter_path);
|
||||
g_extern.filter.lib = dylib_load(g_settings.video.filter_path);
|
||||
if (!g_extern.filter.lib)
|
||||
{
|
||||
RARCH_ERR("Failed to load filter \"%s\"\n", g_settings.video.filter_path);
|
||||
return;
|
||||
}
|
||||
|
||||
struct retro_game_geometry *geom = &g_extern.system.av_info.geometry;
|
||||
unsigned width = geom->max_width;
|
||||
unsigned height = geom->max_height;
|
||||
@ -1330,66 +1309,37 @@ void rarch_init_filter(bool rgb32)
|
||||
unsigned pow2_y = 0;
|
||||
unsigned maxsize = 0;
|
||||
|
||||
g_extern.filter.psize =
|
||||
(void (*)(unsigned*, unsigned*))dylib_proc(g_extern.filter.lib, "filter_size");
|
||||
g_extern.filter.prender =
|
||||
(void (*)(uint32_t*, uint32_t*,
|
||||
unsigned, const uint16_t*,
|
||||
unsigned, unsigned, unsigned))dylib_proc(g_extern.filter.lib, "filter_render");
|
||||
RARCH_LOG("Loading softfilter from \"%s\"\n", g_settings.video.filter_path);
|
||||
g_extern.filter.filter = rarch_softfilter_new(g_settings.video.filter_path,
|
||||
RARCH_SOFTFILTER_THREADS_AUTO,
|
||||
rgb32 ? RETRO_PIXEL_FORMAT_XRGB8888 : RETRO_PIXEL_FORMAT_RGB565,
|
||||
width, height);
|
||||
|
||||
if (!g_extern.filter.psize || !g_extern.filter.prender)
|
||||
if (!g_extern.filter.filter)
|
||||
{
|
||||
RARCH_ERR("Failed to find functions in filter...\n");
|
||||
goto error;
|
||||
RARCH_ERR("Failed to load filter \"%s\"\n", g_settings.video.filter_path);
|
||||
return;
|
||||
}
|
||||
|
||||
g_extern.filter.active = true;
|
||||
g_extern.filter.psize(&width, &height);
|
||||
|
||||
pow2_x = next_pow2(width);
|
||||
pow2_y = next_pow2(height);
|
||||
maxsize = pow2_x > pow2_y ? pow2_x : pow2_y;
|
||||
rarch_softfilter_get_max_output_size(g_extern.filter.filter, &pow2_x, &pow2_y);
|
||||
pow2_x = next_pow2(pow2_x);
|
||||
pow2_y = next_pow2(pow2_y);
|
||||
maxsize = max(pow2_x, pow2_y);
|
||||
g_extern.filter.scale = maxsize / RARCH_SCALE_BASE;
|
||||
|
||||
g_extern.filter.buffer = (uint32_t*)malloc(RARCH_SCALE_BASE * RARCH_SCALE_BASE *
|
||||
g_extern.filter.scale * g_extern.filter.scale * sizeof(uint32_t));
|
||||
g_extern.filter.out_rgb32 = rarch_softfilter_get_output_format(g_extern.filter.filter) == RETRO_PIXEL_FORMAT_XRGB8888;
|
||||
g_extern.filter.out_bpp = g_extern.filter.out_rgb32 ? sizeof(uint32_t) : sizeof(uint16_t);
|
||||
|
||||
// TODO: Aligned output.
|
||||
g_extern.filter.buffer = malloc(width * height * g_extern.filter.out_bpp);
|
||||
if (!g_extern.filter.buffer)
|
||||
goto error;
|
||||
|
||||
g_extern.filter.pitch = RARCH_SCALE_BASE * g_extern.filter.scale * sizeof(uint32_t);
|
||||
|
||||
g_extern.filter.colormap = (uint32_t*)malloc(0x10000 * sizeof(uint32_t));
|
||||
if (!g_extern.filter.colormap)
|
||||
goto error;
|
||||
|
||||
// Set up conversion map from 16-bit XRGB1555 to 32-bit ARGB.
|
||||
for (i = 0; i < 0x10000; i++)
|
||||
{
|
||||
unsigned r = (i >> 10) & 0x1f;
|
||||
unsigned g = (i >> 5) & 0x1f;
|
||||
unsigned b = (i >> 0) & 0x1f;
|
||||
|
||||
r = (r << 3) | (r >> 2);
|
||||
g = (g << 3) | (g >> 2);
|
||||
b = (b << 3) | (b >> 2);
|
||||
g_extern.filter.colormap[i] = (r << 16) | (g << 8) | (b << 0);
|
||||
}
|
||||
|
||||
g_extern.filter.scaler_out = (uint16_t*)malloc(sizeof(uint16_t) * geom->max_width * geom->max_height);
|
||||
if (!g_extern.filter.scaler_out)
|
||||
goto error;
|
||||
|
||||
g_extern.filter.scaler.scaler_type = SCALER_TYPE_POINT;
|
||||
g_extern.filter.scaler.in_fmt = rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565;
|
||||
g_extern.filter.scaler.out_fmt = SCALER_FMT_0RGB1555;
|
||||
|
||||
if (!scaler_ctx_gen_filter(&g_extern.filter.scaler))
|
||||
goto error;
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
RARCH_ERR("CPU filter init failed.\n");
|
||||
RARCH_ERR("Softfilter init failed.\n");
|
||||
rarch_deinit_filter();
|
||||
}
|
||||
#endif
|
||||
@ -1467,7 +1417,7 @@ void init_video_input(void)
|
||||
unsigned scale = next_pow2(max_dim) / RARCH_SCALE_BASE;
|
||||
scale = max(scale, 1);
|
||||
|
||||
if (g_extern.filter.active)
|
||||
if (g_extern.filter.filter)
|
||||
scale = g_extern.filter.scale;
|
||||
|
||||
// Update core-dependent aspect ratio values.
|
||||
@ -1532,7 +1482,7 @@ void init_video_input(void)
|
||||
video.force_aspect = g_settings.video.force_aspect;
|
||||
video.smooth = g_settings.video.smooth;
|
||||
video.input_scale = scale;
|
||||
video.rgb32 = g_extern.filter.active || (g_extern.system.pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888);
|
||||
video.rgb32 = g_extern.filter.filter ? g_extern.filter.out_rgb32 : (g_extern.system.pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888);
|
||||
|
||||
const input_driver_t *tmp = driver.input;
|
||||
find_video_driver(); // Need to grab the "real" video driver interface on a reinit.
|
||||
|
19
general.h
19
general.h
@ -34,6 +34,7 @@
|
||||
#include "performance.h"
|
||||
#include "core_options.h"
|
||||
#include "miscellaneous.h"
|
||||
#include "gfx/filter.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
@ -504,20 +505,12 @@ struct global
|
||||
|
||||
struct
|
||||
{
|
||||
bool active;
|
||||
uint32_t *buffer;
|
||||
uint32_t *colormap;
|
||||
unsigned pitch;
|
||||
dylib_t lib;
|
||||
rarch_softfilter_t *filter;
|
||||
|
||||
void *buffer;
|
||||
unsigned scale;
|
||||
|
||||
void (*psize)(unsigned *width, unsigned *height);
|
||||
void (*prender)(uint32_t *colormap, uint32_t *output, unsigned outpitch,
|
||||
const uint16_t *input, unsigned pitch, unsigned width, unsigned height);
|
||||
|
||||
// CPU filters only work on *XRGB1555*. We have to convert to XRGB1555 first.
|
||||
struct scaler_ctx scaler;
|
||||
uint16_t *scaler_out;
|
||||
unsigned out_bpp;
|
||||
bool out_rgb32;
|
||||
} filter;
|
||||
|
||||
msg_queue_t *msg_queue;
|
||||
|
277
gfx/filter.c
Normal file
277
gfx/filter.c
Normal file
@ -0,0 +1,277 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "filter.h"
|
||||
#include "../dynamic.h"
|
||||
#include "../general.h"
|
||||
#include "../performance.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
#include "../thread.h"
|
||||
|
||||
struct filter_thread_data
|
||||
{
|
||||
sthread_t *thread;
|
||||
const struct softfilter_work_packet *packet;
|
||||
scond_t *cond;
|
||||
slock_t *lock;
|
||||
void *userdata;
|
||||
bool die;
|
||||
bool done;
|
||||
};
|
||||
|
||||
static void filter_thread_loop(void *data)
|
||||
{
|
||||
struct filter_thread_data *thr = (struct filter_thread_data*)data;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
slock_lock(thr->lock);
|
||||
while (thr->done && !thr->die)
|
||||
scond_wait(thr->cond, thr->lock);
|
||||
bool die = thr->die;
|
||||
slock_unlock(thr->lock);
|
||||
|
||||
if (die)
|
||||
break;
|
||||
|
||||
thr->packet->work(thr->userdata, thr->packet->thread_data);
|
||||
|
||||
slock_lock(thr->lock);
|
||||
thr->done = true;
|
||||
scond_signal(thr->cond);
|
||||
slock_unlock(thr->lock);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct rarch_softfilter
|
||||
{
|
||||
dylib_t *lib;
|
||||
|
||||
const struct softfilter_implementation *impl;
|
||||
void *impl_data;
|
||||
|
||||
unsigned max_width, max_height;
|
||||
enum retro_pixel_format pix_fmt, out_pix_fmt;
|
||||
|
||||
struct softfilter_work_packet *packets;
|
||||
unsigned threads;
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
struct filter_thread_data *thread_data;
|
||||
#endif
|
||||
};
|
||||
|
||||
rarch_softfilter_t *rarch_softfilter_new(const char *filter_path,
|
||||
unsigned threads,
|
||||
enum retro_pixel_format in_pixel_format,
|
||||
unsigned max_width, unsigned max_height)
|
||||
{
|
||||
unsigned i;
|
||||
rarch_softfilter_t *filt = (rarch_softfilter_t*)calloc(1, sizeof(*filt));
|
||||
if (!filt)
|
||||
return NULL;
|
||||
|
||||
filt->lib = dylib_load(filter_path);
|
||||
if (!filt->lib)
|
||||
goto error;
|
||||
|
||||
softfilter_get_implementation_t cb = (softfilter_get_implementation_t)dylib_proc(filt->lib, "softfilter_get_implementation");
|
||||
if (!cb)
|
||||
{
|
||||
RARCH_ERR("Couldn't find softfilter symbol.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
unsigned cpu_features = rarch_get_cpu_features();
|
||||
filt->impl = cb(cpu_features);
|
||||
if (!filt->impl)
|
||||
goto error;
|
||||
|
||||
RARCH_LOG("Loaded softfilter \"%s\".\n", filt->impl->ident);
|
||||
|
||||
if (filt->impl->api_version != SOFTFILTER_API_VERSION)
|
||||
{
|
||||
RARCH_ERR("Softfilter ABI mismatch.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Simple assumptions.
|
||||
filt->pix_fmt = in_pixel_format;
|
||||
unsigned input_fmt = in_pixel_format == RETRO_PIXEL_FORMAT_XRGB8888 ?
|
||||
SOFTFILTER_FMT_XRGB8888 : SOFTFILTER_FMT_RGB565;
|
||||
unsigned input_fmts = filt->impl->query_input_formats();
|
||||
if (!(input_fmt & input_fmts))
|
||||
{
|
||||
RARCH_ERR("Softfilter does not support input format.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
unsigned output_fmts = filt->impl->query_output_formats(input_fmt);
|
||||
if (output_fmts & input_fmt) // If we have a match of input/output formats, use that.
|
||||
filt->out_pix_fmt = in_pixel_format;
|
||||
else if (output_fmts & SOFTFILTER_FMT_XRGB8888)
|
||||
filt->out_pix_fmt = RETRO_PIXEL_FORMAT_XRGB8888;
|
||||
else if (output_fmts & SOFTFILTER_FMT_RGB565)
|
||||
filt->out_pix_fmt = RETRO_PIXEL_FORMAT_RGB565;
|
||||
else
|
||||
{
|
||||
RARCH_ERR("Did not find suitable output format for softfilter.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
filt->max_width = max_width;
|
||||
filt->max_height = max_height;
|
||||
|
||||
filt->impl_data = filt->impl->create(input_fmt, input_fmt, max_width, max_height,
|
||||
threads != RARCH_SOFTFILTER_THREADS_AUTO ? threads : 1, cpu_features);
|
||||
if (!filt->impl_data)
|
||||
{
|
||||
RARCH_ERR("Failed to create softfilter state.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
threads = filt->impl->query_num_threads(filt->impl_data);
|
||||
if (!threads)
|
||||
{
|
||||
RARCH_ERR("Invalid number of threads.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
filt->packets = (struct softfilter_work_packet*)calloc(threads, sizeof(*filt->packets));
|
||||
if (!filt->packets)
|
||||
{
|
||||
RARCH_ERR("Failed to allocate softfilter packets.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
filt->thread_data = (struct filter_thread_data*)calloc(threads, sizeof(*filt->thread_data));
|
||||
if (!filt->thread_data)
|
||||
goto error;
|
||||
filt->threads = threads;
|
||||
|
||||
for (i = 0; i < threads; i++)
|
||||
{
|
||||
filt->thread_data[i].userdata = filt->impl_data;
|
||||
filt->thread_data[i].done = true;
|
||||
|
||||
filt->thread_data[i].lock = slock_new();
|
||||
if (!filt->thread_data[i].lock)
|
||||
goto error;
|
||||
filt->thread_data[i].cond = scond_new();
|
||||
if (!filt->thread_data[i].cond)
|
||||
goto error;
|
||||
filt->thread_data[i].thread = sthread_create(filter_thread_loop, &filt->thread_data[i]);
|
||||
if (!filt->thread_data[i].thread)
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
return filt;
|
||||
|
||||
error:
|
||||
rarch_softfilter_free(filt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rarch_softfilter_free(rarch_softfilter_t *filt)
|
||||
{
|
||||
unsigned i;
|
||||
if (!filt)
|
||||
return;
|
||||
|
||||
free(filt->packets);
|
||||
if (filt->impl && filt->impl_data)
|
||||
filt->impl->destroy(filt->impl_data);
|
||||
if (filt->lib)
|
||||
dylib_close(filt->lib);
|
||||
#ifdef HAVE_THREADS
|
||||
for (i = 0; i < filt->threads; i++)
|
||||
{
|
||||
if (!filt->thread_data[i].thread)
|
||||
continue;
|
||||
slock_lock(filt->thread_data[i].lock);
|
||||
filt->thread_data[i].die = true;
|
||||
scond_signal(filt->thread_data[i].cond);
|
||||
slock_unlock(filt->thread_data[i].lock);
|
||||
sthread_join(filt->thread_data[i].thread);
|
||||
slock_free(filt->thread_data[i].lock);
|
||||
scond_free(filt->thread_data[i].cond);
|
||||
}
|
||||
free(filt->thread_data);
|
||||
#endif
|
||||
free(filt);
|
||||
}
|
||||
|
||||
void rarch_softfilter_get_max_output_size(rarch_softfilter_t *filt,
|
||||
unsigned *width, unsigned *height)
|
||||
{
|
||||
rarch_softfilter_get_output_size(filt, width, height, filt->max_width, filt->max_height);
|
||||
}
|
||||
|
||||
void rarch_softfilter_get_output_size(rarch_softfilter_t *filt,
|
||||
unsigned *out_width, unsigned *out_height,
|
||||
unsigned width, unsigned height)
|
||||
{
|
||||
filt->impl->query_output_size(filt->impl_data, out_width, out_height, width, height);
|
||||
}
|
||||
|
||||
enum retro_pixel_format rarch_softfilter_get_output_format(rarch_softfilter_t *filt)
|
||||
{
|
||||
return filt->out_pix_fmt;
|
||||
}
|
||||
|
||||
void rarch_softfilter_process(rarch_softfilter_t *filt,
|
||||
void *output, size_t output_stride,
|
||||
const void *input, unsigned width, unsigned height, size_t input_stride)
|
||||
{
|
||||
unsigned i;
|
||||
filt->impl->get_work_packets(filt->impl_data, filt->packets,
|
||||
output, output_stride, input, width, height, input_stride);
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
// Fire off workers
|
||||
for (i = 0; i < filt->threads; i++)
|
||||
{
|
||||
//RARCH_LOG("Firing off filter thread %u ...\n", i);
|
||||
filt->thread_data[i].packet = &filt->packets[i];
|
||||
slock_lock(filt->thread_data[i].lock);
|
||||
filt->thread_data[i].done = false;
|
||||
scond_signal(filt->thread_data[i].cond);
|
||||
slock_unlock(filt->thread_data[i].lock);
|
||||
}
|
||||
|
||||
// Wait for workers
|
||||
for (i = 0; i < filt->threads; i++)
|
||||
{
|
||||
//RARCH_LOG("Waiting for filter thread %u ...\n", i);
|
||||
slock_lock(filt->thread_data[i].lock);
|
||||
while (!filt->thread_data[i].done)
|
||||
scond_wait(filt->thread_data[i].cond, filt->thread_data[i].lock);
|
||||
slock_unlock(filt->thread_data[i].lock);
|
||||
}
|
||||
#else
|
||||
for (i = 0; i < filt->threads; i++)
|
||||
filt->packets[i].work(filt->impl_data, filt->packets[i].thread_data);
|
||||
#endif
|
||||
}
|
||||
|
48
gfx/filter.h
Normal file
48
gfx/filter.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RARCH_FILTER_H__
|
||||
#define RARCH_FILTER_H__
|
||||
|
||||
#include "../libretro.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#include "softfilter.h"
|
||||
|
||||
#define RARCH_SOFTFILTER_THREADS_AUTO 0
|
||||
typedef struct rarch_softfilter rarch_softfilter_t;
|
||||
|
||||
rarch_softfilter_t *rarch_softfilter_new(const char *filter_path,
|
||||
unsigned threads,
|
||||
enum retro_pixel_format in_pixel_format,
|
||||
unsigned max_width, unsigned max_height);
|
||||
|
||||
void rarch_softfilter_free(rarch_softfilter_t *filt);
|
||||
|
||||
void rarch_softfilter_get_max_output_size(rarch_softfilter_t *filt,
|
||||
unsigned *width, unsigned *height);
|
||||
|
||||
void rarch_softfilter_get_output_size(rarch_softfilter_t *filt,
|
||||
unsigned *out_width, unsigned *out_height,
|
||||
unsigned width, unsigned height);
|
||||
|
||||
enum retro_pixel_format rarch_softfilter_get_output_format(rarch_softfilter_t *filt);
|
||||
|
||||
void rarch_softfilter_process(rarch_softfilter_t *filt,
|
||||
void *output, size_t output_stride,
|
||||
const void *input, unsigned width, unsigned height, size_t input_stride);
|
||||
|
||||
#endif
|
||||
|
119
gfx/softfilter.h
Normal file
119
gfx/softfilter.h
Normal file
@ -0,0 +1,119 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SOFTFILTER_API_H__
|
||||
#define SOFTFILTER_API_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SOFTFILTER_SIMD_SSE (1 << 0)
|
||||
#define SOFTFILTER_SIMD_SSE2 (1 << 1)
|
||||
#define SOFTFILTER_SIMD_VMX (1 << 2)
|
||||
#define SOFTFILTER_SIMD_VMX128 (1 << 3)
|
||||
#define SOFTFILTER_SIMD_AVX (1 << 4)
|
||||
#define SOFTFILTER_SIMD_NEON (1 << 5)
|
||||
#define SOFTFILTER_SIMD_SSE3 (1 << 6)
|
||||
#define SOFTFILTER_SIMD_SSSE3 (1 << 7)
|
||||
#define SOFTFILTER_SIMD_MMX (1 << 8)
|
||||
#define SOFTFILTER_SIMD_MMXEXT (1 << 9)
|
||||
#define SOFTFILTER_SIMD_SSE4 (1 << 10)
|
||||
#define SOFTFILTER_SIMD_SSE42 (1 << 11)
|
||||
#define SOFTFILTER_SIMD_AVX2 (1 << 12)
|
||||
#define SOFTFILTER_SIMD_VFPU (1 << 13)
|
||||
#define SOFTFILTER_SIMD_PS (1 << 14)
|
||||
|
||||
// A bit-mask of all supported SIMD instruction sets.
|
||||
// Allows an implementation to pick different softfilter_implementation structs.
|
||||
typedef unsigned softfilter_simd_mask_t;
|
||||
|
||||
// Dynamic library entrypoint.
|
||||
typedef const struct softfilter_implementation *(*softfilter_get_implementation_t)(softfilter_simd_mask_t);
|
||||
// The same SIMD mask argument is forwarded to create() callback as well to avoid having to keep lots of state around.
|
||||
const struct softfilter_implementation *softfilter_get_implementation(softfilter_simd_mask_t simd);
|
||||
|
||||
#define SOFTFILTER_API_VERSION 1
|
||||
|
||||
#define SOFTFILTER_FMT_NONE 0
|
||||
#define SOFTFILTER_FMT_RGB565 (1 << 0)
|
||||
#define SOFTFILTER_FMT_XRGB8888 (1 << 1)
|
||||
|
||||
// Softfilter implementation.
|
||||
// Returns a bitmask of supported input formats.
|
||||
typedef unsigned (*softfilter_query_input_formats_t)(void);
|
||||
|
||||
// Returns a bitmask of supported output formats for a given input format.
|
||||
typedef unsigned (*softfilter_query_output_formats_t)(unsigned input_format);
|
||||
|
||||
// In softfilter_process_t, the softfilter implementation submits work units to a worker thread pool.
|
||||
typedef void (*softfilter_work_t)(void *data, void *thread_data);
|
||||
struct softfilter_work_packet
|
||||
{
|
||||
softfilter_work_t work;
|
||||
void *thread_data;
|
||||
};
|
||||
|
||||
// Create a filter with given input and output formats as well as maximum possible input size.
|
||||
// Input sizes can very per call to softfilter_process_t, but they will never be larger than the maximum.
|
||||
typedef void *(*softfilter_create_t)(unsigned in_fmt, unsigned out_fmt,
|
||||
unsigned max_width, unsigned max_height,
|
||||
unsigned threads, softfilter_simd_mask_t simd);
|
||||
typedef void (*softfilter_destroy_t)(void *data);
|
||||
|
||||
// Given an input size, query the output size of the filter.
|
||||
// If width and height == max_width/max_height, no other combination of width/height must return a larger size in any dimension.
|
||||
typedef void (*softfilter_query_output_size_t)(void *data,
|
||||
unsigned *out_width, unsigned *out_height,
|
||||
unsigned width, unsigned height);
|
||||
|
||||
// First step of processing a frame. The filter submits work by filling in the packets array.
|
||||
// The number of elements in the array is as returned by query_num_threads.
|
||||
// The processing itself happens in worker threads after this returns.
|
||||
typedef void (*softfilter_get_work_packets_t)(void *data,
|
||||
struct softfilter_work_packet *packets,
|
||||
void *output, size_t output_stride,
|
||||
const void *input, unsigned width, unsigned height, size_t input_stride);
|
||||
|
||||
// Returns the number of worker threads the filter will use.
|
||||
// This can differ from the value passed to create() instead the filter cannot be parallelized, etc. The number of threads must be less-or-equal compared to the value passed to create().
|
||||
typedef unsigned (*softfilter_query_num_threads_t)(void *data);
|
||||
/////
|
||||
|
||||
struct softfilter_implementation
|
||||
{
|
||||
softfilter_query_input_formats_t query_input_formats;
|
||||
softfilter_query_output_formats_t query_output_formats;
|
||||
|
||||
softfilter_create_t create;
|
||||
softfilter_destroy_t destroy;
|
||||
|
||||
softfilter_query_num_threads_t query_num_threads;
|
||||
softfilter_query_output_size_t query_output_size;
|
||||
softfilter_get_work_packets_t get_work_packets;
|
||||
|
||||
const char *ident; // Human readable identifier of implementation.
|
||||
unsigned api_version; // Must be SOFTFILTER_API_VERSION
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -473,6 +473,9 @@ DYNAMIC
|
||||
============================================================ */
|
||||
#include "../dynamic.c"
|
||||
#include "../dynamic_dummy.c"
|
||||
#ifdef HAVE_DYLIB
|
||||
#include "../gfx/filter.c"
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
FILE
|
||||
|
39
retroarch.c
39
retroarch.c
@ -297,7 +297,7 @@ static void video_frame(const void *data, unsigned width, unsigned height, size_
|
||||
// Slightly messy code,
|
||||
// but we really need to do processing before blocking on VSync for best possible scheduling.
|
||||
#ifdef HAVE_FFMPEG
|
||||
if (g_extern.recording && (!g_extern.filter.active || !g_settings.video.post_filter_record || !data || g_extern.record_gpu_buffer))
|
||||
if (g_extern.recording && (!g_extern.filter.filter || !g_settings.video.post_filter_record || !data || g_extern.record_gpu_buffer))
|
||||
recording_dump_frame(data, width, height, pitch);
|
||||
#endif
|
||||
|
||||
@ -305,28 +305,26 @@ static void video_frame(const void *data, unsigned width, unsigned height, size_
|
||||
driver.current_msg = msg;
|
||||
|
||||
#ifdef HAVE_DYLIB
|
||||
if (g_extern.filter.active && data)
|
||||
if (g_extern.filter.filter && data)
|
||||
{
|
||||
struct scaler_ctx *scaler = &g_extern.filter.scaler;
|
||||
scaler->in_width = scaler->out_width = width;
|
||||
scaler->in_height = scaler->out_height = height;
|
||||
scaler->in_stride = pitch;
|
||||
scaler->out_stride = width * sizeof(uint16_t);
|
||||
unsigned owidth = 0;
|
||||
unsigned oheight = 0;
|
||||
unsigned opitch = 0;
|
||||
rarch_softfilter_get_output_size(g_extern.filter.filter,
|
||||
&owidth, &oheight, width, height);
|
||||
|
||||
scaler_ctx_scale(scaler, g_extern.filter.scaler_out, data);
|
||||
opitch = owidth * g_extern.filter.out_bpp;
|
||||
|
||||
unsigned owidth = width;
|
||||
unsigned oheight = height;
|
||||
g_extern.filter.psize(&owidth, &oheight);
|
||||
g_extern.filter.prender(g_extern.filter.colormap, g_extern.filter.buffer,
|
||||
g_extern.filter.pitch, g_extern.filter.scaler_out, scaler->out_stride, width, height);
|
||||
rarch_softfilter_process(g_extern.filter.filter,
|
||||
g_extern.filter.buffer, opitch,
|
||||
data, width, height, pitch);
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
if (g_extern.recording && g_settings.video.post_filter_record)
|
||||
recording_dump_frame(g_extern.filter.buffer, owidth, oheight, g_extern.filter.pitch);
|
||||
recording_dump_frame(g_extern.filter.buffer, owidth, oheight, opitch);
|
||||
#endif
|
||||
|
||||
if (!video_frame_func(g_extern.filter.buffer, owidth, oheight, g_extern.filter.pitch, msg))
|
||||
if (!video_frame_func(g_extern.filter.buffer, owidth, oheight, opitch, msg))
|
||||
g_extern.video_active = false;
|
||||
}
|
||||
else if (!video_frame_func(data, width, height, pitch, msg))
|
||||
@ -1381,14 +1379,13 @@ void rarch_init_recording(void)
|
||||
else
|
||||
params.aspect_ratio = (float)params.out_width / params.out_height;
|
||||
|
||||
if (g_settings.video.post_filter_record && g_extern.filter.active)
|
||||
if (g_settings.video.post_filter_record && g_extern.filter.filter)
|
||||
{
|
||||
g_extern.filter.psize(¶ms.out_width, ¶ms.out_height);
|
||||
params.pix_fmt = FFEMU_PIX_ARGB8888;
|
||||
params.pix_fmt = g_extern.filter.out_rgb32 ? FFEMU_PIX_ARGB8888 : FFEMU_PIX_RGB565;
|
||||
|
||||
unsigned max_width = params.fb_width;
|
||||
unsigned max_height = params.fb_height;
|
||||
g_extern.filter.psize(&max_width, &max_height);
|
||||
unsigned max_width = 0;
|
||||
unsigned max_height = 0;
|
||||
rarch_softfilter_get_max_output_size(g_extern.filter.filter, &max_width, &max_height);
|
||||
params.fb_width = next_pow2(max_width);
|
||||
params.fb_height = next_pow2(max_height);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user