|
@@ -0,0 +1,253 @@
|
|
|
|
+/* RetroArch - A frontend for libretro.
|
|
|
|
+ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
|
|
|
+ * Copyright (C) 2011-2018 - Daniel De Matteis
|
|
|
|
+ *
|
|
|
|
+ * 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/>.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+/* Compile: gcc -o normal5x.so -shared normal5x.c -std=c99 -O3 -Wall -pedantic -fPIC */
|
|
|
|
+
|
|
|
|
+#include "softfilter.h"
|
|
|
|
+#include <stdlib.h>
|
|
|
|
+#include <string.h>
|
|
|
|
+
|
|
|
|
+#ifdef RARCH_INTERNAL
|
|
|
|
+#define softfilter_get_implementation normal5x_get_implementation
|
|
|
|
+#define softfilter_thread_data normal5x_softfilter_thread_data
|
|
|
|
+#define filter_data normal5x_filter_data
|
|
|
|
+#define generic_input_fmts normal5x_generic_input_fmts
|
|
|
|
+#define generic_output_fmts normal5x_generic_output_fmts
|
|
|
|
+#define generic_threads normal5x_generic_threads
|
|
|
|
+#define generic_create normal5x_generic_create
|
|
|
|
+#define generic_output normal5x_generic_output
|
|
|
|
+#define generic_destroy normal5x_generic_destroy
|
|
|
|
+#define work_cb_xrgb8888 normal5x_work_cb_xrgb8888
|
|
|
|
+#define work_cb_rgb565 normal5x_work_cb_rgb565
|
|
|
|
+#define generic_packets normal5x_generic_packets
|
|
|
|
+#define generic normal5x_generic
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+struct softfilter_thread_data {
|
|
|
|
+ void *out_data;
|
|
|
|
+ const void *in_data;
|
|
|
|
+ size_t out_pitch;
|
|
|
|
+ size_t in_pitch;
|
|
|
|
+ unsigned colfmt;
|
|
|
|
+ unsigned width;
|
|
|
|
+ unsigned height;
|
|
|
|
+ int first;
|
|
|
|
+ int last;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+struct filter_data {
|
|
|
|
+ unsigned threads;
|
|
|
|
+ struct softfilter_thread_data *workers;
|
|
|
|
+ unsigned in_fmt;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static unsigned generic_input_fmts(void) {
|
|
|
|
+ return SOFTFILTER_FMT_XRGB8888 | SOFTFILTER_FMT_RGB565;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static unsigned generic_output_fmts(unsigned input_fmts) {
|
|
|
|
+ return input_fmts;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static unsigned generic_threads(void *data) {
|
|
|
|
+ struct filter_data *filt = (struct filter_data *) data;
|
|
|
|
+ return filt->threads;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void *generic_create(
|
|
|
|
+ const struct softfilter_config *config,
|
|
|
|
+ unsigned in_fmt, unsigned out_fmt,
|
|
|
|
+ unsigned max_width, unsigned max_height,
|
|
|
|
+ unsigned threads, softfilter_simd_mask_t simd,
|
|
|
|
+ void *userdata
|
|
|
|
+) {
|
|
|
|
+ struct filter_data *filt = (struct filter_data *) calloc(1, sizeof(*filt));
|
|
|
|
+ if (!filt)
|
|
|
|
+ return NULL;
|
|
|
|
+ if (!(filt->workers = (struct softfilter_thread_data *) calloc(
|
|
|
|
+ 1,
|
|
|
|
+ sizeof(struct softfilter_thread_data)))) {
|
|
|
|
+ free(filt);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+ /* Apparently the code is not thread-safe,
|
|
|
|
+ * so force single threaded operation... */
|
|
|
|
+ filt->threads = 1;
|
|
|
|
+ filt->in_fmt = in_fmt;
|
|
|
|
+ return filt;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void generic_output(
|
|
|
|
+ void *data,
|
|
|
|
+ unsigned *out_width, unsigned *out_height,
|
|
|
|
+ unsigned width, unsigned height
|
|
|
|
+) {
|
|
|
|
+ *out_width = width * 5;
|
|
|
|
+ *out_height = height * 5;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void generic_destroy(void *data) {
|
|
|
|
+ struct filter_data *filt = (struct filter_data *) data;
|
|
|
|
+ if (!filt)
|
|
|
|
+ return;
|
|
|
|
+ free(filt->workers);
|
|
|
|
+ free(filt);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void work_cb_xrgb8888(void *data, void *thread_data) {
|
|
|
|
+ struct softfilter_thread_data *thr = (struct softfilter_thread_data *) thread_data;
|
|
|
|
+ const uint32_t *input = (const uint32_t *) thr->in_data;
|
|
|
|
+ uint32_t *output = (uint32_t *) thr->out_data;
|
|
|
|
+ uint32_t in_stride = (uint32_t) (thr->in_pitch >> 2);
|
|
|
|
+ uint32_t out_stride = (uint32_t) (thr->out_pitch >> 2);
|
|
|
|
+ uint32_t x, y;
|
|
|
|
+
|
|
|
|
+ for (y = 0; y < thr->height; ++y) {
|
|
|
|
+ uint32_t *out_ptr = output;
|
|
|
|
+
|
|
|
|
+ /* Row 1 */
|
|
|
|
+ for (x = 0; x < thr->width; ++x) {
|
|
|
|
+ out_ptr[0] = out_ptr[1] = out_ptr[2] = out_ptr[4] = out_ptr[5] = *(input + x);
|
|
|
|
+ out_ptr += 5;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Row 2 */
|
|
|
|
+ memcpy(out_ptr, output, out_stride);
|
|
|
|
+ out_ptr += out_stride;
|
|
|
|
+
|
|
|
|
+ /* Row 3 */
|
|
|
|
+ memcpy(out_ptr, output, out_stride);
|
|
|
|
+ out_ptr += out_stride;
|
|
|
|
+
|
|
|
|
+ /* Row 4 */
|
|
|
|
+ memcpy(out_ptr, output, out_stride);
|
|
|
|
+ out_ptr += out_stride;
|
|
|
|
+
|
|
|
|
+ /* Row 5 */
|
|
|
|
+ memcpy(out_ptr, output, out_stride);
|
|
|
|
+
|
|
|
|
+ input += in_stride;
|
|
|
|
+ output += out_stride * 5;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void work_cb_rgb565(void *data, void *thread_data) {
|
|
|
|
+ struct softfilter_thread_data *thr = (struct softfilter_thread_data *) thread_data;
|
|
|
|
+ const uint16_t *input = (const uint16_t *) thr->in_data;
|
|
|
|
+ uint16_t *output = (uint16_t *) thr->out_data;
|
|
|
|
+ uint16_t in_stride = (uint16_t) (thr->in_pitch >> 1);
|
|
|
|
+ uint16_t out_stride = (uint16_t) (thr->out_pitch >> 1);
|
|
|
|
+ uint16_t x, y;
|
|
|
|
+
|
|
|
|
+ for (y = 0; y < thr->height; ++y) {
|
|
|
|
+ uint16_t *out_ptr = output;
|
|
|
|
+
|
|
|
|
+ /* Row 1 */
|
|
|
|
+ for (x = 0; x < thr->width; ++x) {
|
|
|
|
+ out_ptr[0] = out_ptr[1] = out_ptr[2] = out_ptr[4] = out_ptr[5] = *(input + x);
|
|
|
|
+ out_ptr += 5;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Row 2 */
|
|
|
|
+ memcpy(out_ptr, output, out_stride);
|
|
|
|
+ out_ptr += out_stride;
|
|
|
|
+
|
|
|
|
+ /* Row 3 */
|
|
|
|
+ memcpy(out_ptr, output, out_stride);
|
|
|
|
+ out_ptr += out_stride;
|
|
|
|
+
|
|
|
|
+ /* Row 4 */
|
|
|
|
+ memcpy(out_ptr, output, out_stride);
|
|
|
|
+ out_ptr += out_stride;
|
|
|
|
+
|
|
|
|
+ /* Row 5 */
|
|
|
|
+ memcpy(out_ptr, output, out_stride);
|
|
|
|
+
|
|
|
|
+ input += in_stride;
|
|
|
|
+ output += out_stride * 5;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void generic_packets(
|
|
|
|
+ void *data,
|
|
|
|
+ struct softfilter_work_packet *packets,
|
|
|
|
+ void *output, size_t output_stride,
|
|
|
|
+ const void *input, unsigned width, unsigned height,
|
|
|
|
+ size_t input_stride
|
|
|
|
+) {
|
|
|
|
+ /* We are guaranteed single threaded operation
|
|
|
|
+ * (filt->threads = 1) so we don't need to loop
|
|
|
|
+ * over threads and can cull some code. This only
|
|
|
|
+ * makes the tiniest performance difference, but
|
|
|
|
+ * every little helps when running on an o3DS... */
|
|
|
|
+ struct filter_data *filt = (struct filter_data *) data;
|
|
|
|
+ struct softfilter_thread_data *thr = (struct softfilter_thread_data *) &filt->workers[0];
|
|
|
|
+
|
|
|
|
+ thr->out_data = (uint8_t *) output;
|
|
|
|
+ thr->in_data = (const uint8_t *) input;
|
|
|
|
+ thr->out_pitch = output_stride;
|
|
|
|
+ thr->in_pitch = input_stride;
|
|
|
|
+ thr->width = width;
|
|
|
|
+ thr->height = height;
|
|
|
|
+
|
|
|
|
+ if (filt->in_fmt == SOFTFILTER_FMT_XRGB8888)
|
|
|
|
+ packets[0].work = work_cb_xrgb8888;
|
|
|
|
+ else if (filt->in_fmt == SOFTFILTER_FMT_RGB565)
|
|
|
|
+ packets[0].work = work_cb_rgb565;
|
|
|
|
+ packets[0].thread_data = thr;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const struct softfilter_implementation generic = {
|
|
|
|
+ generic_input_fmts,
|
|
|
|
+ generic_output_fmts,
|
|
|
|
+
|
|
|
|
+ generic_create,
|
|
|
|
+ generic_destroy,
|
|
|
|
+
|
|
|
|
+ generic_threads,
|
|
|
|
+ generic_output,
|
|
|
|
+ generic_packets,
|
|
|
|
+
|
|
|
|
+ SOFTFILTER_API_VERSION,
|
|
|
|
+ "Normal5x",
|
|
|
|
+ "normal5x",
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const struct softfilter_implementation *
|
|
|
|
+softfilter_get_implementation(
|
|
|
|
+ softfilter_simd_mask_t simd
|
|
|
|
+) {
|
|
|
|
+ return &generic;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#ifdef RARCH_INTERNAL
|
|
|
|
+#undef softfilter_get_implementation
|
|
|
|
+#undef softfilter_thread_data
|
|
|
|
+#undef filter_data
|
|
|
|
+#undef softfilter_get_implementation
|
|
|
|
+#undef softfilter_thread_data
|
|
|
|
+#undef filter_data
|
|
|
|
+#undef generic_input_fmts
|
|
|
|
+#undef generic_output_fmts
|
|
|
|
+#undef generic_threads
|
|
|
|
+#undef generic_create
|
|
|
|
+#undef generic_output
|
|
|
|
+#undef generic_destroy
|
|
|
|
+#undef work_cb_xrgb8888
|
|
|
|
+#undef work_cb_rgb565
|
|
|
|
+#undef generic_packets
|
|
|
|
+#undef generic
|
|
|
|
+#endif
|