From 7cdb08b804de8de784da08df65e77dbfce2dbd87 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Fri, 24 Jan 2014 15:15:55 -0600 Subject: [PATCH 01/10] Add lima video driver Documentation is provided in README-lima. --- Makefile | 5 + README-lima.md | 41 +++ driver.c | 3 + driver.h | 1 + gfx/lima_gfx.c | 793 ++++++++++++++++++++++++++++++++++++++++++++ qb/config.libs.sh | 7 +- qb/config.params.sh | 1 + 7 files changed, 850 insertions(+), 1 deletion(-) create mode 100644 README-lima.md create mode 100644 gfx/lima_gfx.c diff --git a/Makefile b/Makefile index 4a10ac881a..f9f54f02be 100644 --- a/Makefile +++ b/Makefile @@ -195,6 +195,11 @@ ifeq ($(HAVE_SDL), 1) LIBS += $(SDL_LIBS) endif +ifeq ($(HAVE_LIMA), 1) + OBJ += gfx/lima_gfx.o + LIBS += -llimare +endif + ifeq ($(HAVE_OMAP), 1) OBJ += gfx/omap_gfx.o endif diff --git a/README-lima.md b/README-lima.md new file mode 100644 index 0000000000..c5a135c166 --- /dev/null +++ b/README-lima.md @@ -0,0 +1,41 @@ +# RetroArch Lima video driver + +The Lima video driver for RetroArch uses the open-source Lima driver, which implements the userspace code to enable the Mali GPU contained in a lot of ARM SoC. At the time of writing (24/01/2014) the Lima driver supports GPUs of the type Mali-200 and Mali-400. The full driver stack to enable the Mali GPU is comprised of a part in kernelspace, which is available as open-source from ARM itself, and the aforementioned userspace part, which ARM only supplies as a binary blob. + +## Reasons to use the driver + +The original binary blob provides hardware-accelerated GLES 2.0 rendering through EGL. Depending on which blob one uses, rendering is either done to a framebuffer provided by a fbdev device or a framebuffer provided by a X11 window. None of these choices are particular good and are also not very performant. + +The author uses a Hardkernel ODROID-X2, which is an developer board powered by an Exynos4412 SoC. This SoC incorporates a Mali-400 GPU and dedicated blocks for 2D acceleration and HDMI interfacing. The non-Mali graphics blocks functionality of the SoC is exported through a DRM driver (Exynos DRM). + +The DRM exposes a fbdev device through an emulation layer. The layer introduces overhead and also doesn't provide any decent support for proper vertical synchronisation, ruining the experience with lots of tearing artifacts. Switching back and forth between fbdev and X11 solved neither the vsync nor the performance issue. + +Users with similar experiences on a Exynos4-based hardware (coupled with a Mali GPU supported by the Lima driver) are invited to try this driver. + +## Configuration + +The original Lima driver suffers from a similar problem as the blob, since it can only render into a framebuffer provided by a fbdev device. In the repository mentioned below you can find a modified Lima version, which can utilize the Exynos DRM directly. + +[lima-drm repository](https://gitorious.org/lima-drm/lima-drm) + +The Lima video driver for RetroArch only works with this version. Proceed with the usual steps to install limare from the repository onto your system. After that compile RetroArch against the resulting limare library. + +The video driver name is 'lima'. It honors the following video settings: + + - video\_monitor\_index + - video\_fullscreen\_x and video\_fullscreen\_y + +The monitor index maps to the DRM connector index. If it's zero, then it just selects the first "sane" connector, which means that it is connected to a display device and it provides at least one useable mode. If the value is non-zero, it forces the selection of this connector. For example, on the ODROID-X2 the HDMI connector has index 2. + +The two fullscreen parameters select the mode the DRM should select. If zero, the native connector mode is selected. If non-zero, the DRM tries to select the wanted mode. This might fail if the mode is not available from the connector. + +## Issues and TODOs + +The driver still suffers from some issues. + + - The aspect ratio is wrong. The emulator framebuffer is always stretched over the entire screen. + - Font rendering is non-operational. Implement it by copying code from OMAP and doing software blitting in the temp buffer. This shouldn't be so bad, since fonts aren't displayed a lot. + - Sometimes opening RGUI the second time results in broken rendering (flickering artifacts, blending not done properly). I don't know where these artifacts come from and they also don't appear again when closing RGUI and opening it again. This needs further investigation (silent memory corruption?). + - Swizzling for XRGB32 input is probably broken. Test this with a core that uses this format and apply correct swizzling. + - Restarting (for example when choosing a different ROM in RGUI) the driver does not work. Maybe some deinitialization in the limare driver is missing? + - Limare should be able to handle a custom pitch, when uploading texture pixel data. This would save some memcpy for emulator cores which don't provide the framebuffer with full pitch. diff --git a/driver.c b/driver.c index 329dd1d393..27b9052530 100644 --- a/driver.c +++ b/driver.c @@ -128,6 +128,9 @@ static const video_driver_t *video_drivers[] = { #ifdef HAVE_NULLVIDEO &video_null, #endif +#ifdef HAVE_LIMA + &video_lima, +#endif #ifdef HAVE_OMAP &video_omap, #endif diff --git a/driver.h b/driver.h index c5ad0ac908..9da6011e96 100644 --- a/driver.h +++ b/driver.h @@ -663,6 +663,7 @@ extern const video_driver_t video_xdk_d3d; extern const video_driver_t video_sdl; extern const video_driver_t video_vg; extern const video_driver_t video_null; +extern const video_driver_t video_lima; extern const video_driver_t video_omap; extern const input_driver_t input_android; extern const input_driver_t input_sdl; diff --git a/gfx/lima_gfx.c b/gfx/lima_gfx.c new file mode 100644 index 0000000000..41060f81be --- /dev/null +++ b/gfx/lima_gfx.c @@ -0,0 +1,793 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013-2014 - Tobias Jakobi + * Copyright (C) 2013-2014 - Daniel Mehrwald + * + * 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 . + */ + +#include +#include +#include +#include + +#include "../general.h" +#include "gfx_common.h" +#include "fonts/fonts.h" + +/* Rename to LIMA_GFX_DEBUG to enable debugging code.*/ +#define NO_LIMA_GFX_DEBUG 1 + +/* Current limare only natively supports a limited amount of formats for texture * + * data. We compensate for this limitation by swizzling the texture data in the * + * pixel shader. */ + +#define LIMA_TEXEL_FORMAT_BGR_565 0x0e +#define LIMA_TEXEL_FORMAT_RGBA_5551 0x0f +#define LIMA_TEXEL_FORMAT_RGBA_4444 0x10 +#define LIMA_TEXEL_FORMAT_RGBA_8888 0x16 + +/* Limare is currently unable to deallocate individual texture objects and * + * only allows to destroy all objects at once. * + * We only create a maximum of 12 objects, before doing a full "reset", or * + * sooner, under the condition that limare's texture memory runs out. */ +static const unsigned num_max_textures = 12; + +typedef struct limare_state limare_state_t; + +typedef struct limare_texture { + unsigned width; + unsigned height; + + int handle; + unsigned format; + + bool rgui; +} limare_texture_t; + +typedef struct vec2f { + float x,y; +} vec2f_t; + +typedef struct vec3f { + float x, y, z; +} vec3f_t; + +/* Create three shader programs. One is for displaying only the emulator core pixel data. * + * The other two are for displaying the RGUI, where the pixel data can be provided in * + * two different formats. Current RetroArch only seems to ever use a single format, but * + * this is not set in stone, therefore making two programs necessary. */ + +typedef struct limare_data { + limare_state_t *state; + + int program; + + int program_rgui_rgba16; + int program_rgui_rgba32; + + float screen_aspect; + + unsigned upload_format; + unsigned upload_bpp; /* bytes per pixel */ + + vec3f_t *vertices; + vec2f_t *coords; + + /* Generic buffer to create contiguous pixel data for limare + * or to use for font blitting. */ + void *buffer; + unsigned buffer_size; + + limare_texture_t **textures; + unsigned texture_slots; + limare_texture_t *cur_texture; + limare_texture_t *cur_texture_rgui; +} limare_data_t; + +/* Header for simple vertex shader. */ +static const char *vshader_src = + "attribute vec4 in_vertex;\n" + "attribute vec2 in_coord;\n" + "\n" + "varying vec2 coord;\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = in_vertex;\n" + " coord = in_coord;\n" + "}\n"; + +/* Header for simple fragment shader. */ +static const char *fshader_header_src = + "precision highp float;\n" + "\n" + "varying vec2 coord;\n" + "\n" + "uniform sampler2D in_texture;\n" + "\n"; + +/* Main (template) for simple fragment shader. */ +static const char *fshader_main_src = + "void main()\n" + "{\n" + " vec3 pixel = texture2D(in_texture, coord)%s;\n" + " gl_FragColor = vec4(pixel, 1.0);\n" + "}\n"; + +/* Header for RGUI fragment shader. */ +/* Use mediump, which makes uColor into a (single-precision) float[4]. */ +static const char *fshader_rgui_header_src = + "precision mediump float;\n" + "\n" + "varying vec2 coord;\n" + "uniform vec4 uColor;\n" + "\n" + "uniform sampler2D in_texture;\n" + "\n"; + +/* Main (template) for RGUI fragment shader. */ +static const char *fshader_rgui_main_src = + "void main()\n" + "{\n" + " vec4 pixel = texture2D(in_texture, coord)%s;\n" + " gl_FragColor = pixel * uColor;\n" + "}\n"; + +float get_screen_aspect(limare_state_t *state) { + unsigned w = 0, h = 0; + + limare_buffer_size(state, &w, &h); + + if (w != 0 && h != 0) { + return (float)w / (float)h; + } + + return 0.0f; +} + +void apply_aspect(limare_data_t *pdata, float ratio) { + vec3f_t *vertices = pdata->vertices; + float value; + + if (ratio == 0.0f) return; + value = 1.0f / ratio; + + vertices[0].x = vertices[2].x = -value; + vertices[1].x = vertices[3].x = value; +} + +int destroy_textures(limare_data_t *pdata) { + unsigned i; + int ret; + + pdata->cur_texture = NULL; + pdata->cur_texture_rgui = NULL; + + for (i = 0; i < pdata->texture_slots; ++i) { + free(pdata->textures[i]); + pdata->textures[i] = NULL; + } + + ret = limare_texture_cleanup(pdata->state); + pdata->texture_slots = 0; + + return ret; +} + +static limare_texture_t *get_texture_handle(limare_data_t *pdata, + unsigned width, unsigned height, unsigned format) { + unsigned i; + + format = (format == 0) ? pdata->upload_format : format; + + for (i = 0; i < pdata->texture_slots; ++i) { + if (pdata->textures[i]->width == width && + pdata->textures[i]->height == height && + pdata->textures[i]->format == format) return pdata->textures[i]; + } + + if (pdata->texture_slots == num_max_textures) { + /* All texture slots are used, do a reset. */ + if (destroy_textures(pdata)) { + RARCH_ERR("video_lima: failed to reset texture storage\n"); + } + } + + return NULL; +} + +static limare_texture_t *add_texture(limare_data_t *pdata, + unsigned width, unsigned height, + const void *pixels, unsigned format) { + int texture = -1; + unsigned retries = 2; + const unsigned i = pdata->texture_slots; + + format = (format == 0) ? pdata->upload_format : format; + + /* limare_texture_upload returns -1 when the upload fails for some reason. */ + while (texture == -1 && retries > 0) { + texture = limare_texture_upload(pdata->state, pixels, width, height, format, 0); + + if (texture != -1) break; + + destroy_textures(pdata); + retries--; + } + + if (texture == -1) return NULL; + + pdata->textures[i] = calloc(1, sizeof(limare_texture_t)); + + pdata->textures[i]->width = width; + pdata->textures[i]->height = height; + pdata->textures[i]->handle = texture; + pdata->textures[i]->format = format; + + pdata->texture_slots++; + + return pdata->textures[i]; +} + +static const void *make_contiguous(limare_data_t *pdata, + unsigned width, unsigned height, + const void *pixels, unsigned bpp, + unsigned pitch) { + unsigned i; + unsigned full_pitch; + + bpp = (bpp == 0) ? pdata->upload_bpp : bpp; + full_pitch = width * bpp; + + if (full_pitch == pitch) return pixels; + + RARCH_LOG("video_lima: input buffer not contiguous\n"); + + /* Enlarge our buffer, if it is currently too small. */ + if (pdata->buffer_size < full_pitch * height) { + free(pdata->buffer); + pdata->buffer = NULL; + + pdata->buffer = malloc(full_pitch * height); + if (pdata->buffer == NULL) { + RARCH_ERR("video_lima: failed to allocate buffer to make pixel data contiguous\n"); + return NULL; + } + } + + for (i = 0; i < height; ++i) { + memcpy(pdata->buffer + i * full_pitch, pixels + i * pitch, full_pitch); + } + + return pdata->buffer; +} + +#ifdef LIMA_GFX_DEBUG +void print_status(limare_data_t *pdata) { + unsigned i; + + RARCH_LOG("video_lima: upload format = 0x%x, upload bpp = %u\n", pdata->upload_format, pdata->upload_bpp); + RARCH_LOG("video_lima: buffer at %p, buffer size = %u\n", pdata->buffer, pdata->buffer_size); + RARCH_LOG("video_lima: used texture slots = %u (from %u)\n", pdata->texture_slots, num_max_textures); + + for (i = 0; i < pdata->texture_slots; ++i) { + RARCH_LOG("video_lima: texture slot %u, width = %u, height = %u, handle = %u, format = 0x%x\n", + i, pdata->textures[i]->width, pdata->textures[i]->height, + pdata->textures[i]->handle, pdata->textures[i]->format); + } +} +#endif + +void destroy_data(limare_data_t *pdata) { + free(pdata->vertices); + free(pdata->coords); +} + +static int setup_data(limare_data_t *pdata) { + static const unsigned num_verts = 4; + static const unsigned num_coords = 4 * 4; + unsigned i; + + static const vec3f_t vertices[4] = { + {-1.0f, -1.0f, 0.0f}, + { 1.0f, -1.0f, 0.0f}, + {-1.0f, 1.0f, 0.0f}, + { 1.0f, 1.0f, 0.0f} + }; + + static const vec2f_t coords[16] = { + {0.0f, 1.0f}, {1.0f, 1.0f}, /* 0 degrees */ + {0.0f, 0.0f}, {1.0f, 0.0f}, + {0.0f, 0.0f}, {0.0f, 1.0f}, /* 90 degrees */ + {1.0f, 0.0f}, {1.0f, 1.0f}, + {1.0f, 0.0f}, {0.0f, 0.0f}, /* 180 degrees */ + {1.0f, 1.0f}, {0.0f, 1.0f}, + {1.0f, 1.0f}, {1.0f, 0.0f}, /* 270 degrees */ + {0.0f, 1.0f}, {0.0f, 0.0f} + }; + + pdata->vertices = calloc(num_verts, sizeof(vec3f_t)); + if (pdata->vertices == NULL) goto fail; + + pdata->coords = calloc(num_coords, sizeof(vec2f_t)); + if (pdata->coords == NULL) goto fail; + + for (i = 0; i < num_verts; ++i) { + pdata->vertices[i] = vertices[i]; + } + + for (i = 0; i < num_coords; ++i) { + pdata->coords[i] = coords[i]; + } + + return 0; + +fail: + return -1; +} + +static int create_programs(limare_data_t *pdata) { + char tmpbufm[1024]; /* temp buffer for main function */ + char tmpbuf[1024]; /* temp buffer for whole program */ + + const char* swz = (pdata->upload_bpp == 4) ? ".rgb" : ".rgb"; /* TODO / XRGB8888 */ + + /* Create shader program for regular operation first. */ + pdata->program = limare_program_new(pdata->state); + if (pdata->program < 0) goto fail; + + snprintf(tmpbufm, 1024, fshader_main_src, swz); + strncpy(tmpbuf, fshader_header_src, 1024); + strcat(tmpbuf, tmpbufm); + + if (vertex_shader_attach(pdata->state, pdata->program, vshader_src)) goto fail; + if (fragment_shader_attach(pdata->state, pdata->program, tmpbuf)) goto fail; + if (limare_link(pdata->state)) goto fail; + + /* Create shader program for RGUI with RGBA4444 pixel data. */ + pdata->program_rgui_rgba16 = limare_program_new(pdata->state); + if (pdata->program_rgui_rgba16 < 0) goto fail; + + snprintf(tmpbufm, 1024, fshader_rgui_main_src, ".abgr"); + strncpy(tmpbuf, fshader_rgui_header_src, 1024); + strcat(tmpbuf, tmpbufm); + + if (vertex_shader_attach(pdata->state, pdata->program_rgui_rgba16, vshader_src)) goto fail; + if (fragment_shader_attach(pdata->state, pdata->program_rgui_rgba16, tmpbuf)) goto fail; + if (limare_link(pdata->state)) goto fail; + + /* Create shader program for RGUI with RGBA8888 pixel data. */ + pdata->program_rgui_rgba32 = limare_program_new(pdata->state); + if (pdata->program_rgui_rgba32 < 0) goto fail; + + snprintf(tmpbufm, 1024, fshader_rgui_main_src, ".rgba"); + strncpy(tmpbuf, fshader_rgui_header_src, 1024); + strcat(tmpbuf, tmpbufm); + + if (vertex_shader_attach(pdata->state, pdata->program_rgui_rgba32, vshader_src)) goto fail; + if (fragment_shader_attach(pdata->state, pdata->program_rgui_rgba32, tmpbuf)) goto fail; + if (limare_link(pdata->state)) goto fail; + + return 0; + +fail: + return -1; +} + +typedef struct lima_video { + limare_data_t *lima; + + void *font; + const font_renderer_driver_t *font_driver; + uint8_t font_rgb[4]; + + unsigned bytes_per_pixel; + + /* current dimensions */ + unsigned width; + unsigned height; + + /* RGUI data */ + void *rgui_buffer; + int rgui_rotation; + float rgui_alpha; + bool rgui_active; + bool rgui_rgb32; + + bool aspect_changed; + +} lima_video_t; + +static void lima_gfx_free(void *data) { + lima_video_t *vid = data; + if (!vid) return; + + if (vid->lima && vid->lima->state) limare_finish(vid->lima->state); + if (vid->font) vid->font_driver->free(vid->font); + + destroy_data(vid->lima); + destroy_textures(vid->lima); + free(vid->lima->textures); + + free(vid->rgui_buffer); + free(vid->lima); + free(vid); +} + +static void *lima_gfx_init(const video_info_t *video, const input_driver_t **input, void **input_data) { + lima_video_t *vid = NULL; + limare_data_t *lima = NULL; + void *lima_input = NULL; + struct limare_windowsys_drm limare_config = { 0 }; + + vid = calloc(1, sizeof(lima_video_t)); + if (!vid) return NULL; + + vid->rgui_alpha = 1.0f; + + lima = calloc(1, sizeof(limare_data_t)); + if (!lima) return NULL; + + vid->bytes_per_pixel = video->rgb32 ? 4 : 2; + + /* Request the Exynos DRM backend for rendering. */ + limare_config.type = LIMARE_WINDOWSYS_DRM; + limare_config.connector_index = g_settings.video.monitor_index; + + lima->state = limare_init(&limare_config); + + if (!lima->state) { + RARCH_ERR("video_lima: limare initialization failed\n"); + goto fail; + } + + limare_buffer_clear(lima->state); + + if (limare_state_setup(lima->state, g_settings.video.fullscreen_x, + g_settings.video.fullscreen_y, 0xff000000)) { + RARCH_ERR("video_lima: limare state setup failed\n"); + goto fail_lima; + } + + lima->screen_aspect = get_screen_aspect(lima->state); + + lima->upload_format = (vid->bytes_per_pixel == 4) ? + LIMA_TEXEL_FORMAT_RGBA_8888 : LIMA_TEXEL_FORMAT_BGR_565; + lima->upload_bpp = vid->bytes_per_pixel; + + limare_enable(lima->state, GL_DEPTH_TEST); + limare_depth_func(lima->state, GL_ALWAYS); + limare_depth_mask(lima->state, GL_TRUE); + + limare_enable(lima->state, GL_CULL_FACE); + + limare_blend_func(lima->state, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if (setup_data(lima)) { + RARCH_ERR("video_lima: data setup failed\n"); + goto fail_lima; + } + + if (create_programs(lima)) { + RARCH_ERR("video_lima: creating shader programs failed\n"); + goto fail_lima; + } + + lima->textures = calloc(num_max_textures, sizeof(limare_texture_t*)); + + if (input && input_data) { +#ifdef HAVE_UDEV + lima_input = input_udev.init(); + if (lima_input) { + *input = lima_input ? &input_udev : NULL; + *input_data = lima_input; + } +#else + lima_input = input_linuxraw.init(); + if (lima_input) { + *input = lima_input ? &input_linuxraw : NULL; + *input_data = lima_input; + } +#endif + else { + *input = NULL; + *input_data = NULL; + } + } + + vid->lima = lima; + + /*lima_init_font(vid, g_settings.video.font_path, g_settings.video.font_size);*/ + + return vid; + +fail_lima: + limare_finish(lima->state); +fail: + free(lima); + free(vid); + + return NULL; +} + +static bool lima_gfx_frame(void *data, const void *frame, + unsigned width, unsigned height, + unsigned pitch, const char *msg) { + lima_video_t *vid; + const void *pixels; + limare_data_t *lima; + bool upload_frame = true; + + vid = data; + + /* Check if neither RGUI nor emulator framebuffer is to be displayed. */ + if (!vid->rgui_active && frame == NULL) return true; + + lima = vid->lima; + + if (frame != NULL) { + + /* Handle resolution changes from the emulation core. */ + if (width != vid->width || height != vid->height) { + limare_texture_t* tex; + + if (width == 0 || height == 0) return true; + + RARCH_LOG("video_lima: resolution was changed by core to %ux%u\n", width, height); + tex = get_texture_handle(lima, width, height, 0); + + if (tex == NULL) { + pixels = make_contiguous(lima, width, height, frame, 0, pitch); + + tex = add_texture(lima, width, height, pixels, 0); + + if (tex == NULL) { + RARCH_ERR("video_lima: failed to allocate new texture with dimensions %ux%u\n", + width, height); + return false; + } + + upload_frame = false; /* pixel data already got uploaded during texture allocation */ + } + + lima->cur_texture = tex; + vid->width = width; + vid->height = height; + } + + if (upload_frame) { + pixels = make_contiguous(lima, width, height, frame, 0, pitch); + limare_texture_mipmap_upload(lima->state, lima->cur_texture->handle, 0, pixels); + } + } + + /*if (msg) lima_render_msg(vid, vid->screen, msg, vid->screen->w, vid->screen->h); + + char buffer[128], buffer_fps[128]; + bool fps_draw = g_settings.fps_show; + if (fps_draw) + { + gfx_get_fps(buffer, sizeof(buffer), fps_draw ? buffer_fps : NULL, sizeof(buffer_fps)); + msg_queue_push(g_extern.msg_queue, buffer_fps, 1, 1); + }*/ + + if (vid->aspect_changed) { + apply_aspect(lima, g_extern.system.aspect_ratio); + vid->aspect_changed = false; + } + + limare_frame_new(lima->state); + + if (lima->cur_texture != NULL) { + limare_program_current(lima->state, lima->program); + + limare_attribute_pointer(lima->state, "in_vertex", LIMARE_ATTRIB_FLOAT, + 3, 0, 4, lima->vertices); + limare_attribute_pointer(lima->state, "in_coord", LIMARE_ATTRIB_FLOAT, + 2, 0, 4, lima->coords + vid->rgui_rotation * 4); + + limare_texture_attach(lima->state, "in_texture", lima->cur_texture->handle); + + if (limare_draw_arrays(lima->state, GL_TRIANGLE_STRIP, 0, 4)) return false; + } + + if (vid->rgui_active && lima->cur_texture_rgui != NULL) { + float color[4] = {1.0f, 1.0f, 1.0f, vid->rgui_alpha}; + + if (vid->rgui_rgb32) + limare_program_current(lima->state, lima->program_rgui_rgba32); + else + limare_program_current(lima->state, lima->program_rgui_rgba16); + + limare_attribute_pointer(lima->state, "in_vertex", LIMARE_ATTRIB_FLOAT, + 3, 0, 4, lima->vertices); + limare_attribute_pointer(lima->state, "in_coord", LIMARE_ATTRIB_FLOAT, + 2, 0, 4, lima->coords + vid->rgui_rotation * 4); + + limare_texture_attach(lima->state, "in_texture", lima->cur_texture_rgui->handle); + limare_uniform_attach(lima->state, "uColor", 4, color); + + limare_enable(lima->state, GL_BLEND); + if (limare_draw_arrays(lima->state, GL_TRIANGLE_STRIP, 0, 4)) return false; + limare_disable(lima->state, GL_BLEND); + } + + if (limare_frame_flush(lima->state)) return false; + + limare_buffer_swap(lima->state); + + g_extern.frame_count++; + +#ifdef LIMA_GFX_DEBUG + print_status(lima); +#endif + + return true; +} + +static void lima_gfx_set_nonblock_state(void *data, bool state) { + (void)data; /* limare doesn't export vsync control yet */ + (void)state; +} + +static bool lima_gfx_alive(void *data) { + (void)data; + return true; /* always alive */ +} + +static bool lima_gfx_focus(void *data){ + (void)data; + return true; /* limare doesn't use windowing, so we always have focus */ +} + +static void lima_gfx_set_rotation(void *data, unsigned rotation) { + lima_video_t *vid = data; + + vid->rgui_rotation = rotation; +} + +static void lima_gfx_viewport_info(void *data, struct rarch_viewport *vp){ + lima_video_t *vid = data; + vp->x = vp->y = 0; + + vp->width = vp->full_width = vid->width; + vp->height = vp->full_height = vid->height; +} + +static void lima_set_aspect_ratio(void *data, unsigned aspect_ratio_idx) { + lima_video_t *vid = data; + + printf("debug: lima_set_aspect_ratio = %u\n", aspect_ratio_idx); + + switch (aspect_ratio_idx) { + case ASPECT_RATIO_SQUARE: + gfx_set_square_pixel_viewport(g_extern.system.av_info.geometry.base_width, g_extern.system.av_info.geometry.base_height); + break; + + case ASPECT_RATIO_CORE: + gfx_set_core_viewport(); + break; + + case ASPECT_RATIO_CONFIG: + gfx_set_config_viewport(); + break; + + default: + break; + } + + g_extern.system.aspect_ratio = aspectratio_lut[aspect_ratio_idx].value; + vid->aspect_changed = true; +} + +static void lima_apply_state_changes(void *data) { + (void)data; +} + +static void lima_set_texture_frame(void *data, const void *frame, bool rgb32, + unsigned width, unsigned height, float alpha) { + lima_video_t *vid = data; + limare_texture_t* tex; + const unsigned format = rgb32 ? LIMA_TEXEL_FORMAT_RGBA_8888 : + LIMA_TEXEL_FORMAT_RGBA_4444; + + vid->rgui_rgb32 = rgb32; + vid->rgui_alpha = alpha; + + tex = vid->lima->cur_texture_rgui; + + /* Current RGUI doesn't change dimensions, so we should hit this most of the time. */ + if (tex != NULL && tex->width == width && + tex->height == height && tex->format == format) goto upload; + + if (tex == NULL) { + tex = get_texture_handle(vid->lima, width, height, format); + if (tex == NULL) { + tex = add_texture(vid->lima, width, height, frame, format); + + if (tex != NULL) { + vid->lima->cur_texture_rgui = tex; + goto upload; + } + + RARCH_ERR("video_lima: failed to allocate new RGUI texture with dimensions %ux%u\n", + width, height); + } + } + + return; + +upload: + limare_texture_mipmap_upload(vid->lima->state, tex->handle, 0, frame); +} + +static void lima_set_texture_enable(void *data, bool state, bool full_screen) { + lima_video_t *vid = data; + vid->rgui_active = state; +} + +static void lima_set_osd_msg(void *data, const char *msg, void *userdata) { + lima_video_t *vid = data; + + /* TODO: what does this do? */ + (void)msg; + (void)userdata; +} + +static void lima_show_mouse(void *data, bool state) { + (void)data; +} + +static const video_poke_interface_t lima_poke_interface = { + NULL, /* set_filtering */ +#ifdef HAVE_FBO + NULL, /* get_current_framebuffer */ + NULL, /* get_proc_address */ +#endif + lima_set_aspect_ratio, + lima_apply_state_changes, +#if defined(HAVE_RGUI) || defined(HAVE_RMENU) /* TODO: only HAVE_MENU i think */ + lima_set_texture_frame, + lima_set_texture_enable, +#endif + lima_set_osd_msg, + lima_show_mouse +}; + +static void lima_gfx_get_poke_interface(void *data, const video_poke_interface_t **iface) { + (void)data; + *iface = &lima_poke_interface; +} + +const video_driver_t video_lima = { + lima_gfx_init, + lima_gfx_frame, + lima_gfx_set_nonblock_state, + lima_gfx_alive, + lima_gfx_focus, + NULL, /* set_shader */ + lima_gfx_free, + "lima", + +#ifdef HAVE_MENU + NULL, /* restart */ +#endif + + lima_gfx_set_rotation, + lima_gfx_viewport_info, + NULL, /* read_viewport */ + +#ifdef HAVE_OVERLAY + NULL, /* overlay_interface */ +#endif + lima_gfx_get_poke_interface +}; diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 4deecd6061..e675fd1348 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -173,6 +173,11 @@ check_pkgconf SDL_IMAGE SDL_image check_pkgconf ZLIB zlib +if [ "$HAVE_LIMA" = "yes" ]; then + check_lib LIMA -llimare limare_init + LIMA_LIBS="-llimare" +fi + if [ "$HAVE_THREADS" != 'no' ]; then if [ "$HAVE_FFMPEG" != 'no' ]; then check_pkgconf AVCODEC libavcodec 54 @@ -268,6 +273,6 @@ add_define_make OS "$OS" # Creates config.mk and config.h. add_define_make GLOBAL_CONFIG_DIR "$GLOBAL_CONFIG_DIR" -VARS="RGUI ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL OMAP GLES GLES3 VG EGL KMS GBM DRM DYLIB GETOPT_LONG THREADS CG LIBXML2 SDL_IMAGE ZLIB DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE FREETYPE XKBCOMMON XVIDEO X11 XEXT XF86VM XINERAMA NETPLAY NETWORK_CMD STDIN_CMD COMMAND SOCKET_LEGACY FBO STRL STRCASESTR MMAP PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 BSV_MOVIE VIDEOCORE NEON FLOATHARD FLOATSOFTFP UDEV V4L2" +VARS="RGUI ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL LIMA OMAP GLES GLES3 VG EGL KMS GBM DRM DYLIB GETOPT_LONG THREADS CG LIBXML2 SDL_IMAGE ZLIB DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE FREETYPE XKBCOMMON XVIDEO X11 XEXT XF86VM XINERAMA NETPLAY NETWORK_CMD STDIN_CMD COMMAND SOCKET_LEGACY FBO STRL STRCASESTR MMAP PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 BSV_MOVIE VIDEOCORE NEON FLOATHARD FLOATSOFTFP UDEV V4L2" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/qb/config.params.sh b/qb/config.params.sh index 3123d875fd..fc239642b5 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -14,6 +14,7 @@ HAVE_OPENGL=yes # Disable OpenGL support HAVE_GLES=no # Use GLESv2 instead of desktop GL HAVE_GLES3=no # Enable OpenGLES3 support HAVE_X11=auto # Disable everything X11. +HAVE_LIMA=no # Enable Lima video support HAVE_OMAP=no # Enable OMAP video support HAVE_XINERAMA=auto # Disable Xinerama support. HAVE_KMS=auto # Enable KMS context support From 9226c3c783717e4f6af92843f5fff8589d065243 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 25 Jan 2014 08:58:58 +0100 Subject: [PATCH 02/10] video_lima: adjust aspect ratio depending on frame dimensions With this the emulator framebuffer is no longer stretched to the entire screen. However it's still not entirely correct. For example the aspect ratio value provided by the frontend is not used yet. Also I noticed this behaviour: When using the game "Secret of Mana 2" (unofficial english translation) on the snes9x-next core, the game normally uses a framebuffer of dimension 256x224. But when rendering text windows, it increases resolution to 512x224. With the current behaviour this alters aspect ratio and let's the image "explode" when text boxes are displayed, only to "implode" again when they close. This should not happen! (Needs further investigation...) --- gfx/lima_gfx.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/gfx/lima_gfx.c b/gfx/lima_gfx.c index 41060f81be..f1540f46f7 100644 --- a/gfx/lima_gfx.c +++ b/gfx/lima_gfx.c @@ -75,6 +75,7 @@ typedef struct limare_data { int program_rgui_rgba32; float screen_aspect; + float frame_aspect; unsigned upload_format; unsigned upload_bpp; /* bytes per pixel */ @@ -156,13 +157,28 @@ float get_screen_aspect(limare_state_t *state) { void apply_aspect(limare_data_t *pdata, float ratio) { vec3f_t *vertices = pdata->vertices; - float value; + float x, y; - if (ratio == 0.0f) return; - value = 1.0f / ratio; + if (fabsf(pdata->screen_aspect - pdata->frame_aspect) < 0.0001f) { + x = 1.0f; + y = 1.0f; + } else { + if (pdata->screen_aspect > pdata->frame_aspect) { + x = pdata->frame_aspect / pdata->screen_aspect; + y = 1.0f; + } else { + x = 1.0f; + y = pdata->screen_aspect / pdata->frame_aspect; + } + } - vertices[0].x = vertices[2].x = -value; - vertices[1].x = vertices[3].x = value; + /* TODO: use ratio parameter */ + + vertices[0].x = vertices[2].x = -x; + vertices[1].x = vertices[3].x = x; + + vertices[0].y = vertices[1].y = -y; + vertices[2].y = vertices[3].y = y; } int destroy_textures(limare_data_t *pdata) { @@ -560,8 +576,12 @@ static bool lima_gfx_frame(void *data, const void *frame, } lima->cur_texture = tex; + vid->width = width; vid->height = height; + + lima->frame_aspect = (float)width / (float)height; + vid->aspect_changed = true; } if (upload_frame) { @@ -666,8 +686,6 @@ static void lima_gfx_viewport_info(void *data, struct rarch_viewport *vp){ static void lima_set_aspect_ratio(void *data, unsigned aspect_ratio_idx) { lima_video_t *vid = data; - printf("debug: lima_set_aspect_ratio = %u\n", aspect_ratio_idx); - switch (aspect_ratio_idx) { case ASPECT_RATIO_SQUARE: gfx_set_square_pixel_viewport(g_extern.system.av_info.geometry.base_width, g_extern.system.av_info.geometry.base_height); From 55fc57b846694e2178c7803bd789582b13456c92 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 25 Jan 2014 09:14:55 +0100 Subject: [PATCH 03/10] video_lima: fix some swizzling in the fragment shader Thanks to AreaScout for testing this! --- gfx/lima_gfx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gfx/lima_gfx.c b/gfx/lima_gfx.c index f1540f46f7..0bdb292363 100644 --- a/gfx/lima_gfx.c +++ b/gfx/lima_gfx.c @@ -355,7 +355,7 @@ static int create_programs(limare_data_t *pdata) { char tmpbufm[1024]; /* temp buffer for main function */ char tmpbuf[1024]; /* temp buffer for whole program */ - const char* swz = (pdata->upload_bpp == 4) ? ".rgb" : ".rgb"; /* TODO / XRGB8888 */ + const char* swz = (pdata->upload_bpp == 4) ? ".bgr" : ".rgb"; /* Create shader program for regular operation first. */ pdata->program = limare_program_new(pdata->state); @@ -385,7 +385,7 @@ static int create_programs(limare_data_t *pdata) { pdata->program_rgui_rgba32 = limare_program_new(pdata->state); if (pdata->program_rgui_rgba32 < 0) goto fail; - snprintf(tmpbufm, 1024, fshader_rgui_main_src, ".rgba"); + snprintf(tmpbufm, 1024, fshader_rgui_main_src, ".abgr"); strncpy(tmpbuf, fshader_rgui_header_src, 1024); strcat(tmpbuf, tmpbufm); From 0493f8680cb23f771844a277470e22a85cb93937 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 25 Jan 2014 20:23:22 +0100 Subject: [PATCH 04/10] video_lima: update TODO Swizzling is fixed. Restart issue is fixed. Update lima-drm for now correct initialization and destroying of threading/mutex objects. --- README-lima.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README-lima.md b/README-lima.md index c5a135c166..bcea5ae88d 100644 --- a/README-lima.md +++ b/README-lima.md @@ -33,9 +33,7 @@ The two fullscreen parameters select the mode the DRM should select. If zero, th The driver still suffers from some issues. - - The aspect ratio is wrong. The emulator framebuffer is always stretched over the entire screen. + - The aspect ratio is wrong. The dimensions of the emulator framebuffer on the screen are not computed correctly at the moment. - Font rendering is non-operational. Implement it by copying code from OMAP and doing software blitting in the temp buffer. This shouldn't be so bad, since fonts aren't displayed a lot. - Sometimes opening RGUI the second time results in broken rendering (flickering artifacts, blending not done properly). I don't know where these artifacts come from and they also don't appear again when closing RGUI and opening it again. This needs further investigation (silent memory corruption?). - - Swizzling for XRGB32 input is probably broken. Test this with a core that uses this format and apply correct swizzling. - - Restarting (for example when choosing a different ROM in RGUI) the driver does not work. Maybe some deinitialization in the limare driver is missing? - - Limare should be able to handle a custom pitch, when uploading texture pixel data. This would save some memcpy for emulator cores which don't provide the framebuffer with full pitch. + - Limare should be able to handle a custom pitch, when uploading texture pixel data. This would save some memcpy for emulator cores which don't provide the framebuffer with full pitch (snes9x-next for example). From a338d99781253191728057108f72ae89a7a95b8d Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 25 Jan 2014 20:34:49 +0100 Subject: [PATCH 05/10] video_lima: misc fixes --- gfx/lima_gfx.c | 43 ++++++++++++++----------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/gfx/lima_gfx.c b/gfx/lima_gfx.c index 0bdb292363..1bb9dede3d 100644 --- a/gfx/lima_gfx.c +++ b/gfx/lima_gfx.c @@ -23,7 +23,7 @@ #include "gfx_common.h" #include "fonts/fonts.h" -/* Rename to LIMA_GFX_DEBUG to enable debugging code.*/ +/* Rename to LIMA_GFX_DEBUG to enable debugging code. */ #define NO_LIMA_GFX_DEBUG 1 /* Current limare only natively supports a limited amount of formats for texture * @@ -54,7 +54,7 @@ typedef struct limare_texture { } limare_texture_t; typedef struct vec2f { - float x,y; + float x, y; } vec2f_t; typedef struct vec3f { @@ -143,7 +143,7 @@ static const char *fshader_rgui_main_src = " gl_FragColor = pixel * uColor;\n" "}\n"; -float get_screen_aspect(limare_state_t *state) { +static float get_screen_aspect(limare_state_t *state) { unsigned w = 0, h = 0; limare_buffer_size(state, &w, &h); @@ -155,7 +155,7 @@ float get_screen_aspect(limare_state_t *state) { return 0.0f; } -void apply_aspect(limare_data_t *pdata, float ratio) { +static void apply_aspect(limare_data_t *pdata, float ratio) { vec3f_t *vertices = pdata->vertices; float x, y; @@ -181,7 +181,7 @@ void apply_aspect(limare_data_t *pdata, float ratio) { vertices[2].y = vertices[3].y = y; } -int destroy_textures(limare_data_t *pdata) { +static int destroy_textures(limare_data_t *pdata) { unsigned i; int ret; @@ -235,7 +235,7 @@ static limare_texture_t *add_texture(limare_data_t *pdata, texture = limare_texture_upload(pdata->state, pixels, width, height, format, 0); if (texture != -1) break; - + destroy_textures(pdata); retries--; } @@ -288,7 +288,7 @@ static const void *make_contiguous(limare_data_t *pdata, } #ifdef LIMA_GFX_DEBUG -void print_status(limare_data_t *pdata) { +static void print_status(limare_data_t *pdata) { unsigned i; RARCH_LOG("video_lima: upload format = 0x%x, upload bpp = %u\n", pdata->upload_format, pdata->upload_bpp); @@ -303,7 +303,7 @@ void print_status(limare_data_t *pdata) { } #endif -void destroy_data(limare_data_t *pdata) { +static void destroy_data(limare_data_t *pdata) { free(pdata->vertices); free(pdata->coords); } @@ -340,7 +340,7 @@ static int setup_data(limare_data_t *pdata) { for (i = 0; i < num_verts; ++i) { pdata->vertices[i] = vertices[i]; } - + for (i = 0; i < num_coords; ++i) { pdata->coords[i] = coords[i]; } @@ -501,23 +501,8 @@ static void *lima_gfx_init(const video_info_t *video, const input_driver_t **inp lima->textures = calloc(num_max_textures, sizeof(limare_texture_t*)); if (input && input_data) { -#ifdef HAVE_UDEV - lima_input = input_udev.init(); - if (lima_input) { - *input = lima_input ? &input_udev : NULL; - *input_data = lima_input; - } -#else - lima_input = input_linuxraw.init(); - if (lima_input) { - *input = lima_input ? &input_linuxraw : NULL; - *input_data = lima_input; - } -#endif - else { - *input = NULL; - *input_data = NULL; - } + *input = NULL; + *input_data = NULL; } vid->lima = lima; @@ -554,7 +539,7 @@ static bool lima_gfx_frame(void *data, const void *frame, /* Handle resolution changes from the emulation core. */ if (width != vid->width || height != vid->height) { - limare_texture_t* tex; + limare_texture_t *tex; if (width == 0 || height == 0) return true; @@ -664,7 +649,7 @@ static bool lima_gfx_alive(void *data) { return true; /* always alive */ } -static bool lima_gfx_focus(void *data){ +static bool lima_gfx_focus(void *data) { (void)data; return true; /* limare doesn't use windowing, so we always have focus */ } @@ -675,7 +660,7 @@ static void lima_gfx_set_rotation(void *data, unsigned rotation) { vid->rgui_rotation = rotation; } -static void lima_gfx_viewport_info(void *data, struct rarch_viewport *vp){ +static void lima_gfx_viewport_info(void *data, struct rarch_viewport *vp) { lima_video_t *vid = data; vp->x = vp->y = 0; From 4b05426bbe2541409c4b6164489686b5865b4bd0 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 25 Jan 2014 21:15:36 +0100 Subject: [PATCH 06/10] video_lima: add font rendering by using the temp buffer --- gfx/lima_gfx.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 168 insertions(+), 9 deletions(-) diff --git a/gfx/lima_gfx.c b/gfx/lima_gfx.c index 1bb9dede3d..e443bd95d6 100644 --- a/gfx/lima_gfx.c +++ b/gfx/lima_gfx.c @@ -90,8 +90,13 @@ typedef struct limare_data { limare_texture_t **textures; unsigned texture_slots; + limare_texture_t *cur_texture; limare_texture_t *cur_texture_rgui; + + unsigned font_width; + unsigned font_height; + limare_texture_t *font_texture; } limare_data_t; /* Header for simple vertex shader. */ @@ -143,6 +148,14 @@ static const char *fshader_rgui_main_src = " gl_FragColor = pixel * uColor;\n" "}\n"; +static inline void put_pixel_rgba4444(uint16_t *p, unsigned r, unsigned g, unsigned b, unsigned a) { + *p = (a >> 4) | ((b >> 4) << 4) | ((g >> 4) << 8) | ((r >> 4) << 12); +} + +static inline unsigned align4(unsigned i) { + return (i + 3) & ~0x3; +} + static float get_screen_aspect(limare_state_t *state) { unsigned w = 0, h = 0; @@ -278,6 +291,8 @@ static const void *make_contiguous(limare_data_t *pdata, RARCH_ERR("video_lima: failed to allocate buffer to make pixel data contiguous\n"); return NULL; } + + pdata->buffer_size = full_pitch * height; } for (i = 0; i < height; ++i) { @@ -399,6 +414,24 @@ fail: return -1; } +static void put_glyph_rgba4444(limare_data_t *pdata, const uint8_t *src, uint8_t *f_rgb, + unsigned g_width, unsigned g_height, unsigned g_pitch, + unsigned dst_x, unsigned dst_y) { + unsigned x, y; + uint16_t *dst; + unsigned r, g, b; + + dst = (uint16_t*)pdata->buffer + dst_y * pdata->font_width + dst_x; + + for (y = 0; y < g_height; ++y, src += g_pitch, dst += pdata->font_width) { + for (x = 0; x < g_width; ++x) { + const uint8_t blend = src[x]; + + if (blend != 0) put_pixel_rgba4444(&dst[x], f_rgb[0], f_rgb[1], f_rgb[2], blend); + } + } +} + typedef struct lima_video { limare_data_t *lima; @@ -439,6 +472,91 @@ static void lima_gfx_free(void *data) { free(vid); } +static void lima_init_font(lima_video_t *vid, const char *font_path, unsigned font_size) { + if (!g_settings.video.font_enable) return; + + if (font_renderer_create_default(&vid->font_driver, &vid->font)) { + int r = g_settings.video.msg_color_r * 255; + int g = g_settings.video.msg_color_g * 255; + int b = g_settings.video.msg_color_b * 255; + + vid->font_rgb[0] = r < 0 ? 0 : (r > 255 ? 255 : r); + vid->font_rgb[1] = g < 0 ? 0 : (g > 255 ? 255 : g); + vid->font_rgb[2] = b < 0 ? 0 : (b > 255 ? 255 : b); + } else { + RARCH_LOG("video_lima: font init failed\n"); + } +} + +static void lima_render_msg(lima_video_t *vid, const char *msg) { + struct font_output_list out; + struct font_output *head; + + unsigned req_size; + limare_data_t *lima = vid->lima; + + const int msg_base_x = g_settings.video.msg_pos_x * lima->font_width; + const int msg_base_y = (1.0 - g_settings.video.msg_pos_y) * lima->font_height; + + if (vid->font == NULL) return; + + /* Font texture uses RGBA4444 pixel data (2 bytes per pixel). */ + req_size = lima->font_width * lima->font_height * 2; + + if (lima->buffer_size < req_size) { + free(lima->buffer); + lima->buffer = NULL; + + lima->buffer = malloc(req_size); + if (lima->buffer == NULL) { + RARCH_ERR("video_lima: failed to allocate buffer to render fonts\n"); + return; + } + + lima->buffer_size = req_size; + } + + memset(lima->buffer, 0, req_size); + + vid->font_driver->render_msg(vid->font, msg, &out); + + for (head = out.head; head; head = head->next) { + int base_x = msg_base_x + head->off_x; + int base_y = msg_base_y - head->off_y - head->height; + + const int max_width = lima->font_width - base_x; + const int max_height = lima->font_height - base_y; + + int glyph_width = head->width; + int glyph_height = head->height; + + const uint8_t *src = head->output; + + if (base_x < 0) { + src -= base_x; + glyph_width += base_x; + base_x = 0; + } + + if (base_y < 0) { + src -= base_y * (int)head->pitch; + glyph_height += base_y; + base_y = 0; + } + + if (max_width <= 0 || max_height <= 0) continue; + + if (glyph_width > max_width) glyph_width = max_width; + if (glyph_height > max_height) glyph_height = max_height; + + put_glyph_rgba4444(lima, src, vid->font_rgb, + glyph_width, glyph_height, + head->pitch, base_x, base_y); + } + + vid->font_driver->free_output(vid->font, &out); +} + static void *lima_gfx_init(const video_info_t *video, const input_driver_t **input, void **input_data) { lima_video_t *vid = NULL; limare_data_t *lima = NULL; @@ -476,6 +594,9 @@ static void *lima_gfx_init(const video_info_t *video, const input_driver_t **inp lima->screen_aspect = get_screen_aspect(lima->state); + lima->font_height = 480; + lima->font_width = align4((unsigned)(lima->screen_aspect * (float)lima->font_height)); + lima->upload_format = (vid->bytes_per_pixel == 4) ? LIMA_TEXEL_FORMAT_RGBA_8888 : LIMA_TEXEL_FORMAT_BGR_565; lima->upload_bpp = vid->bytes_per_pixel; @@ -507,7 +628,7 @@ static void *lima_gfx_init(const video_info_t *video, const input_driver_t **inp vid->lima = lima; - /*lima_init_font(vid, g_settings.video.font_path, g_settings.video.font_size);*/ + lima_init_font(vid, g_settings.video.font_path, g_settings.video.font_size); return vid; @@ -575,15 +696,12 @@ static bool lima_gfx_frame(void *data, const void *frame, } } - /*if (msg) lima_render_msg(vid, vid->screen, msg, vid->screen->w, vid->screen->h); + if (g_settings.fps_show) { + char buffer[128], buffer_fps[128]; - char buffer[128], buffer_fps[128]; - bool fps_draw = g_settings.fps_show; - if (fps_draw) - { - gfx_get_fps(buffer, sizeof(buffer), fps_draw ? buffer_fps : NULL, sizeof(buffer_fps)); - msg_queue_push(g_extern.msg_queue, buffer_fps, 1, 1); - }*/ + gfx_get_fps(buffer, sizeof(buffer), g_settings.fps_show ? buffer_fps : NULL, sizeof(buffer_fps)); + msg_queue_push(g_extern.msg_queue, buffer_fps, 1, 1); + } if (vid->aspect_changed) { apply_aspect(lima, g_extern.system.aspect_ratio); @@ -605,6 +723,47 @@ static bool lima_gfx_frame(void *data, const void *frame, if (limare_draw_arrays(lima->state, GL_TRIANGLE_STRIP, 0, 4)) return false; } + /* Handle font rendering. */ + if (msg) { + bool upload_font = true; + + /* Both font_vertices and font_color are constant, but we can't make them * + * const, since limare_attribute_pointer expects (non-const) void pointers. */ + static vec3f_t font_vertices[4] = { + {-1.0f, -1.0f, 0.0f}, + { 1.0f, -1.0f, 0.0f}, + {-1.0f, 1.0f, 0.0f}, + { 1.0f, 1.0f, 0.0f} + }; + static float font_color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + + lima_render_msg(vid, msg); + + if (lima->font_texture == NULL) { + lima->font_texture = add_texture(lima, lima->font_width, lima->font_height, + lima->buffer, LIMA_TEXEL_FORMAT_RGBA_4444); + upload_font = false; + } + + if (upload_font) + limare_texture_mipmap_upload(lima->state, lima->font_texture->handle, 0, lima->buffer); + + /* We re-use the RGBA16 RGUI program here. */ + limare_program_current(lima->state, lima->program_rgui_rgba16); + + limare_attribute_pointer(lima->state, "in_vertex", LIMARE_ATTRIB_FLOAT, + 3, 0, 4, font_vertices); + limare_attribute_pointer(lima->state, "in_coord", LIMARE_ATTRIB_FLOAT, + 2, 0, 4, lima->coords + vid->rgui_rotation * 4); + + limare_texture_attach(lima->state, "in_texture", lima->font_texture->handle); + limare_uniform_attach(lima->state, "uColor", 4, font_color); + + limare_enable(lima->state, GL_BLEND); + if (limare_draw_arrays(lima->state, GL_TRIANGLE_STRIP, 0, 4)) return false; + limare_disable(lima->state, GL_BLEND); + } + if (vid->rgui_active && lima->cur_texture_rgui != NULL) { float color[4] = {1.0f, 1.0f, 1.0f, vid->rgui_alpha}; From 185aee467b4f58fccef9c7fdd9092bb5fe1e7b4c Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sun, 26 Jan 2014 02:44:15 +0100 Subject: [PATCH 07/10] video_lima: update TODO Font rendering is fixed. --- README-lima.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README-lima.md b/README-lima.md index bcea5ae88d..1c03728631 100644 --- a/README-lima.md +++ b/README-lima.md @@ -34,6 +34,5 @@ The two fullscreen parameters select the mode the DRM should select. If zero, th The driver still suffers from some issues. - The aspect ratio is wrong. The dimensions of the emulator framebuffer on the screen are not computed correctly at the moment. - - Font rendering is non-operational. Implement it by copying code from OMAP and doing software blitting in the temp buffer. This shouldn't be so bad, since fonts aren't displayed a lot. - Sometimes opening RGUI the second time results in broken rendering (flickering artifacts, blending not done properly). I don't know where these artifacts come from and they also don't appear again when closing RGUI and opening it again. This needs further investigation (silent memory corruption?). - Limare should be able to handle a custom pitch, when uploading texture pixel data. This would save some memcpy for emulator cores which don't provide the framebuffer with full pitch (snes9x-next for example). From afe0a02ed681e50b12b5e72f2129de08d4e1bd17 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sun, 26 Jan 2014 03:00:00 +0100 Subject: [PATCH 08/10] video_lima: update TODO I have failed to encounter the artifacts during my recent tests, so my guess is that they were fixed by some changes to the code (either in limare of in the RetroArch driver). --- README-lima.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README-lima.md b/README-lima.md index 1c03728631..1b7c75afe4 100644 --- a/README-lima.md +++ b/README-lima.md @@ -34,5 +34,4 @@ The two fullscreen parameters select the mode the DRM should select. If zero, th The driver still suffers from some issues. - The aspect ratio is wrong. The dimensions of the emulator framebuffer on the screen are not computed correctly at the moment. - - Sometimes opening RGUI the second time results in broken rendering (flickering artifacts, blending not done properly). I don't know where these artifacts come from and they also don't appear again when closing RGUI and opening it again. This needs further investigation (silent memory corruption?). - Limare should be able to handle a custom pitch, when uploading texture pixel data. This would save some memcpy for emulator cores which don't provide the framebuffer with full pitch (snes9x-next for example). From 8f41e80c6144f1522169f9804d59624405710270 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sun, 26 Jan 2014 04:12:24 +0100 Subject: [PATCH 09/10] video_lima: align temp buffer --- gfx/lima_gfx.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/gfx/lima_gfx.c b/gfx/lima_gfx.c index e443bd95d6..3cd9fab518 100644 --- a/gfx/lima_gfx.c +++ b/gfx/lima_gfx.c @@ -283,16 +283,18 @@ static const void *make_contiguous(limare_data_t *pdata, /* Enlarge our buffer, if it is currently too small. */ if (pdata->buffer_size < full_pitch * height) { + const aligned_size = align4(full_pitch * height); + free(pdata->buffer); pdata->buffer = NULL; - pdata->buffer = malloc(full_pitch * height); + pdata->buffer = aligned_alloc(4, aligned_size); if (pdata->buffer == NULL) { RARCH_ERR("video_lima: failed to allocate buffer to make pixel data contiguous\n"); return NULL; } - pdata->buffer_size = full_pitch * height; + pdata->buffer_size = aligned_size; } for (i = 0; i < height; ++i) { @@ -504,16 +506,18 @@ static void lima_render_msg(lima_video_t *vid, const char *msg) { req_size = lima->font_width * lima->font_height * 2; if (lima->buffer_size < req_size) { + const aligned_size = align4(req_size); + free(lima->buffer); lima->buffer = NULL; - lima->buffer = malloc(req_size); + lima->buffer = aligned_alloc(4, aligned_size); if (lima->buffer == NULL) { RARCH_ERR("video_lima: failed to allocate buffer to render fonts\n"); return; } - lima->buffer_size = req_size; + lima->buffer_size = aligned_size; } memset(lima->buffer, 0, req_size); From 846c561f0a557db38169574863a5434da2810803 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sun, 26 Jan 2014 04:17:35 +0100 Subject: [PATCH 10/10] video_lima: update documentation lima-drm is now also hosted on github. --- README-lima.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README-lima.md b/README-lima.md index 1b7c75afe4..ceb1d888df 100644 --- a/README-lima.md +++ b/README-lima.md @@ -16,7 +16,7 @@ Users with similar experiences on a Exynos4-based hardware (coupled with a Mali The original Lima driver suffers from a similar problem as the blob, since it can only render into a framebuffer provided by a fbdev device. In the repository mentioned below you can find a modified Lima version, which can utilize the Exynos DRM directly. -[lima-drm repository](https://gitorious.org/lima-drm/lima-drm) +[lima-drm repository](https://github.com/tobiasjakobi/lima-drm) The Lima video driver for RetroArch only works with this version. Proceed with the usual steps to install limare from the repository onto your system. After that compile RetroArch against the resulting limare library.