From 74fd184097cce2763af7788a23f05d200d5ca26c Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Fri, 8 Feb 2019 12:14:55 -0500 Subject: [PATCH] initial GL1 video driver, only rgui is working currently --- Makefile.common | 5 +- configuration.c | 1 + gfx/common/gl1_common.h | 90 ++ gfx/drivers/gl1.c | 1085 +++++++++++++++++++++++ gfx/drivers_font/gl1_raster_font.c | 581 ++++++++++++ gfx/font_driver.c | 32 + gfx/font_driver.h | 1 + gfx/video_defines.h | 1 + gfx/video_driver.c | 2 +- gfx/video_driver.h | 1 + griffin/griffin.c | 3 + menu/drivers_display/menu_display_gl1.c | 223 +++++ menu/menu_driver.c | 8 +- menu/menu_driver.h | 1 + 14 files changed, 2031 insertions(+), 3 deletions(-) create mode 100644 gfx/common/gl1_common.h create mode 100644 gfx/drivers/gl1.c create mode 100644 gfx/drivers_font/gl1_raster_font.c create mode 100644 menu/drivers_display/menu_display_gl1.c diff --git a/Makefile.common b/Makefile.common index bee4401f69..9352bfa7b7 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1056,12 +1056,15 @@ endif ifeq ($(HAVE_GL_CONTEXT), 1) DEFINES += -DHAVE_OPENGL -DHAVE_GLSL OBJ += gfx/drivers/gl.o \ + gfx/drivers/gl1.o \ $(LIBRETRO_COMM_DIR)/gfx/gl_capabilities.o \ gfx/drivers_font/gl_raster_font.o \ + gfx/drivers_font/gl1_raster_font.o \ $(LIBRETRO_COMM_DIR)/glsym/rglgen.o ifeq ($(HAVE_MENU_COMMON), 1) - OBJ += menu/drivers_display/menu_display_gl.o + OBJ += menu/drivers_display/menu_display_gl.o \ + menu/drivers_display/menu_display_gl1.o endif ifeq ($(HAVE_VIDEOCORE), 1) diff --git a/configuration.c b/configuration.c index 49feb25290..b21432f7a8 100644 --- a/configuration.c +++ b/configuration.c @@ -2485,6 +2485,7 @@ static bool check_menu_driver_compatibility(void) string_is_equal(video_driver, "d3d12") || string_is_equal(video_driver, "gdi") || string_is_equal(video_driver, "gl") || + /*string_is_equal(video_driver, "gl1") ||*/ string_is_equal(video_driver, "gx2") || string_is_equal(video_driver, "vulkan") || string_is_equal(video_driver, "metal") || diff --git a/gfx/common/gl1_common.h b/gfx/common/gl1_common.h new file mode 100644 index 0000000000..266dc00a02 --- /dev/null +++ b/gfx/common/gl1_common.h @@ -0,0 +1,90 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * copyright (c) 2011-2017 - Daniel De Matteis + * copyright (c) 2016-2019 - Brad Parker + * + * 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 . + */ + +#ifndef __GL1_COMMON_H +#define __GL1_COMMON_H + +#include +#include + +#if defined(__APPLE__) +#include +#include +#else +#if defined(_WIN32) && !defined(_XBOX) +#define WIN32_LEAN_AND_MEAN +#include +#endif +#include +#include +#endif + +#include "../video_driver.h" + +struct string_list; + +typedef struct gl1 +{ + unsigned video_width; + unsigned video_height; + unsigned screen_width; + unsigned screen_height; + int version_major; + int version_minor; + void *ctx_data; + const gfx_ctx_driver_t *ctx_driver; + GLuint tex; + struct string_list *extensions; + bool supports_bgra; + struct video_viewport vp; + bool keep_aspect; + unsigned vp_out_width; + unsigned vp_out_height; + bool should_resize; + struct video_coords coords; + GLuint texture[GFX_MAX_TEXTURES]; + unsigned tex_index; /* For use with PREV. */ + unsigned textures; + math_matrix_4x4 mvp, mvp_no_rot; + struct video_tex_info tex_info; + const float *vertex_ptr; + const float *white_color_ptr; + unsigned rotation; +} gl1_t; + +typedef struct gl1_texture +{ + int width; + int height; + int active_width; + int active_height; + + enum texture_filter_type type; + void* data; +} gl1_texture_t; + +static INLINE void gl1_bind_texture(GLuint id, GLint wrap_mode, GLint mag_filter, + GLint min_filter) +{ + glBindTexture(GL_TEXTURE_2D, id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter); +} + +#endif diff --git a/gfx/drivers/gl1.c b/gfx/drivers/gl1.c new file mode 100644 index 0000000000..9a1e97a11e --- /dev/null +++ b/gfx/drivers/gl1.c @@ -0,0 +1,1085 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2019 - Brad Parker + * + * 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 . + */ + +/* We are targeting a minimum of OpenGL 1.1 and the Microsoft "GDI Generic" software GL implementation. + * Any additional features added for later 1.x versions should only be enabled if they are detected at runtime. */ + +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#ifdef HAVE_MENU +#include "../../menu/menu_driver.h" +#endif + +#include "../font_driver.h" + +#include "../../driver.h" +#include "../../configuration.h" +#include "../../retroarch.h" +#include "../../verbosity.h" +#include "../../frontend/frontend_driver.h" +#include "../common/gl1_common.h" + +#if defined(_WIN32) && !defined(_XBOX) +#include "../common/win32_common.h" +#endif + +static unsigned char *gl1_menu_frame = NULL; +static unsigned gl1_menu_width = 0; +static unsigned gl1_menu_height = 0; +static unsigned gl1_menu_pitch = 0; +static unsigned gl1_video_width = 0; +static unsigned gl1_video_height = 0; +static unsigned gl1_video_pitch = 0; +static unsigned gl1_video_bits = 0; +static unsigned gl1_menu_bits = 0; +static bool gl1_rgb32 = false; +static bool gl1_menu_rgb32 = false; +static unsigned char *gl1_video_buf = NULL; + +static bool gl1_shared_context_use = false; + +static struct video_ortho gl1_default_ortho = {0, 1, 0, 1, -1, 1}; + +/* Used for the last pass when rendering to the back buffer. */ +static const GLfloat gl1_vertexes_flipped[] = { + 0, 1, + 1, 1, + 0, 0, + 1, 0 +}; + +static const GLfloat gl1_vertexes[] = { + 0, 0, + 1, 0, + 0, 1, + 1, 1 +}; + +static const GLfloat gl1_tex_coords[] = { + 0, 0, + 1, 0, + 0, 1, + 1, 1 +}; + +static const GLfloat gl1_white_color[] = { + 1, 1, 1, 1, + 1, 1, 1, 1, + 1, 1, 1, 1, + 1, 1, 1, 1, +}; + +#define gl1_context_bind_hw_render(gl1, enable) \ + if (gl1_shared_context_use) \ + gl1->ctx_driver->bind_hw_render(gl1->ctx_data, enable) + +static bool is_pot(unsigned x) +{ + return (x & (x - 1)) == 0; +} + +static unsigned get_pot(unsigned x) +{ + return (is_pot(x) ? x : next_pow2(x)); +} + +static void gl1_gfx_create(void) +{ +} + +static void *gl1_gfx_init(const video_info_t *video, + const input_driver_t **input, void **input_data) +{ + unsigned full_x, full_y; + gfx_ctx_input_t inp; + gfx_ctx_mode_t mode; + void *ctx_data = NULL; + const gfx_ctx_driver_t *ctx_driver = NULL; + unsigned win_width = 0, win_height = 0; + unsigned temp_width = 0, temp_height = 0; + settings_t *settings = config_get_ptr(); + gl1_t *gl1 = (gl1_t*)calloc(1, sizeof(*gl1)); + const char *vendor = NULL; + const char *renderer = NULL; + const char *version = NULL; + const char *extensions = NULL; + int interval = 0; + struct retro_hw_render_callback *hwr = NULL; + + if (!gl1) + return NULL; + + *input = NULL; + *input_data = NULL; + + gl1_video_width = video->width; + gl1_video_height = video->height; + gl1_rgb32 = video->rgb32; + + gl1_video_bits = video->rgb32 ? 32 : 16; + + if (video->rgb32) + gl1_video_pitch = video->width * 4; + else + gl1_video_pitch = video->width * 2; + + gl1_gfx_create(); + + ctx_driver = video_context_driver_init_first(gl1, + settings->arrays.video_context_driver, + GFX_CTX_OPENGL_API, 1, 1, false, &ctx_data); + + if (!ctx_driver) + goto error; + + if (ctx_data) + gl1->ctx_data = ctx_data; + + gl1->ctx_driver = ctx_driver; + + video_context_driver_set((const gfx_ctx_driver_t*)ctx_driver); + + RARCH_LOG("[GL1]: Found GL1 context: %s\n", ctx_driver->ident); + + video_context_driver_get_video_size(&mode); + + full_x = mode.width; + full_y = mode.height; + mode.width = 0; + mode.height = 0; + + /* Clear out potential error flags in case we use cached context. */ + glGetError(); + + if (string_is_equal(ctx_driver->ident, "null")) + goto error; + + if (!string_is_empty(version)) + sscanf(version, "%d.%d", &gl1->version_major, &gl1->version_minor); + + RARCH_LOG("[GL1]: Detecting screen resolution %ux%u.\n", full_x, full_y); + + win_width = video->width; + win_height = video->height; + + if (video->fullscreen && (win_width == 0) && (win_height == 0)) + { + win_width = full_x; + win_height = full_y; + } + + mode.width = win_width; + mode.height = win_height; + mode.fullscreen = video->fullscreen; + + interval = video->swap_interval; + + video_context_driver_swap_interval(&interval); + + if (!video_context_driver_set_video_mode(&mode)) + goto error; + + mode.width = 0; + mode.height = 0; + + video_context_driver_get_video_size(&mode); + + temp_width = mode.width; + temp_height = mode.height; + mode.width = 0; + mode.height = 0; + + /* Get real known video size, which might have been altered by context. */ + + if (temp_width != 0 && temp_height != 0) + video_driver_set_size(&temp_width, &temp_height); + + video_driver_get_size(&temp_width, &temp_height); + + RARCH_LOG("[GL1]: Using resolution %ux%u\n", temp_width, temp_height); + + inp.input = input; + inp.input_data = input_data; + + video_context_driver_input_driver(&inp); + + if (settings->bools.video_font_enable) + font_driver_init_osd(gl1, false, + video->is_threaded, + FONT_DRIVER_RENDER_OPENGL1_API); + + vendor = (const char*)glGetString(GL_VENDOR); + renderer = (const char*)glGetString(GL_RENDERER); + version = (const char*)glGetString(GL_VERSION); + extensions = (const char*)glGetString(GL_EXTENSIONS); + + gl1->extensions = string_split(extensions, " "); + + RARCH_LOG("[GL1]: Vendor: %s, Renderer: %s.\n", vendor, renderer); + RARCH_LOG("[GL1]: Version: %s.\n", version); + RARCH_LOG("[GL1]: Extensions: %s\n", extensions); + + gl1->supports_bgra = string_list_find_elem(gl1->extensions, "GL_EXT_bgra"); + + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_SCISSOR_TEST); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glGenTextures(1, &gl1->tex); + + hwr = video_driver_get_hw_context(); + + memcpy(gl1->tex_info.coord, gl1_tex_coords, sizeof(gl1->tex_info.coord)); + gl1->vertex_ptr = hwr->bottom_left_origin + ? gl1_vertexes : gl1_vertexes_flipped; + gl1->textures = 4; + gl1->white_color_ptr = gl1_white_color; + gl1->coords.vertex = gl1->vertex_ptr; + gl1->coords.tex_coord = gl1->tex_info.coord; + gl1->coords.color = gl1->white_color_ptr; + gl1->coords.lut_tex_coord = gl1_tex_coords; + gl1->coords.vertices = 4; + + RARCH_LOG("[GL1]: Init complete.\n"); + + return gl1; + +error: + video_context_driver_destroy(); + if (gl1) + { + if (gl1->extensions) + string_list_free(gl1->extensions); + free(gl1); + } + return NULL; +} + +static void gl1_set_projection(gl1_t *gl1, + struct video_ortho *ortho, bool allow_rotate) +{ + math_matrix_4x4 rot; + + /* Calculate projection. */ + matrix_4x4_ortho(gl1->mvp_no_rot, ortho->left, ortho->right, + ortho->bottom, ortho->top, ortho->znear, ortho->zfar); + + if (!allow_rotate) + { + gl1->mvp = gl1->mvp_no_rot; + return; + } + + matrix_4x4_rotate_z(rot, M_PI * gl1->rotation / 180.0f); + matrix_4x4_multiply(gl1->mvp, rot, gl1->mvp_no_rot); +} + +void gl1_gfx_set_viewport(gl1_t *gl1, + video_frame_info_t *video_info, + unsigned viewport_width, + unsigned viewport_height, + bool force_full, bool allow_rotate) +{ + gfx_ctx_aspect_t aspect_data; + int x = 0; + int y = 0; + float device_aspect = (float)viewport_width / viewport_height; + unsigned height = video_info->height; + + aspect_data.aspect = &device_aspect; + aspect_data.width = viewport_width; + aspect_data.height = viewport_height; + + video_context_driver_translate_aspect(&aspect_data); + + if (video_info->scale_integer && !force_full) + { + video_viewport_get_scaled_integer(&gl1->vp, + viewport_width, viewport_height, + video_driver_get_aspect_ratio(), gl1->keep_aspect); + viewport_width = gl1->vp.width; + viewport_height = gl1->vp.height; + } + else if (gl1->keep_aspect && !force_full) + { + float desired_aspect = video_driver_get_aspect_ratio(); + +#if defined(HAVE_MENU) + if (video_info->aspect_ratio_idx == ASPECT_RATIO_CUSTOM) + { + /* GL has bottom-left origin viewport. */ + x = video_info->custom_vp_x; + y = height - video_info->custom_vp_y - video_info->custom_vp_height; + viewport_width = video_info->custom_vp_width; + viewport_height = video_info->custom_vp_height; + } + else +#endif + { + float delta; + + if (fabsf(device_aspect - desired_aspect) < 0.0001f) + { + /* If the aspect ratios of screen and desired aspect + * ratio are sufficiently equal (floating point stuff), + * assume they are actually equal. + */ + } + else if (device_aspect > desired_aspect) + { + delta = (desired_aspect / device_aspect - 1.0f) / 2.0f + 0.5f; + x = (int)roundf(viewport_width * (0.5f - delta)); + viewport_width = (unsigned)roundf(2.0f * viewport_width * delta); + } + else + { + delta = (device_aspect / desired_aspect - 1.0f) / 2.0f + 0.5f; + y = (int)roundf(viewport_height * (0.5f - delta)); + viewport_height = (unsigned)roundf(2.0f * viewport_height * delta); + } + } + + gl1->vp.x = x; + gl1->vp.y = y; + gl1->vp.width = viewport_width; + gl1->vp.height = viewport_height; + } + else + { + gl1->vp.x = gl1->vp.y = 0; + gl1->vp.width = viewport_width; + gl1->vp.height = viewport_height; + } + +#if defined(RARCH_MOBILE) + /* In portrait mode, we want viewport to gravitate to top of screen. */ + if (device_aspect < 1.0f) + gl1->vp.y *= 2; +#endif + + glViewport(gl1->vp.x, gl1->vp.y, gl1->vp.width, gl1->vp.height); + gl1_set_projection(gl1, &gl1_default_ortho, allow_rotate); + + /* Set last backbuffer viewport. */ + if (!force_full) + { + gl1->vp_out_width = viewport_width; + gl1->vp_out_height = viewport_height; + } + +#if 0 + RARCH_LOG("Setting viewport @ %ux%u\n", viewport_width, viewport_height); +#endif +} + +static bool gl1_gfx_frame(void *data, const void *frame, + unsigned frame_width, unsigned frame_height, uint64_t frame_count, + unsigned pitch, const char *msg, video_frame_info_t *video_info) +{ + gfx_ctx_mode_t mode; + const void *frame_to_copy = NULL; + unsigned width = 0; + unsigned height = 0; + unsigned bits = gl1_video_bits; + bool draw = true; + gl1_t *gl1 = (gl1_t*)data; + unsigned tex_width = 0; + unsigned pot_width = 0; + unsigned pot_height = 0; + + gl1_context_bind_hw_render(gl1, false); + + /* FIXME: Force these settings off as they interfere with the rendering */ + video_info->xmb_shadows_enable = false; + video_info->menu_shader_pipeline = 0; + + if (!frame || !frame_width || !frame_height) + return true; + + if (gl1->should_resize) + { + gfx_ctx_mode_t mode; + + gl1->should_resize = false; + + mode.width = width; + mode.height = height; + + video_info->cb_set_resize(video_info->context_data, + mode.width, mode.height); + + gl1_gfx_set_viewport(gl1, video_info, video_info->width, video_info->height, false, true); + } + +#ifdef HAVE_MENU + menu_driver_frame(video_info); +#endif + + if ( gl1_video_width != frame_width || + gl1_video_height != frame_height || + gl1_video_pitch != pitch) + { + if (frame_width > 4 && frame_height > 4) + { + gl1_video_width = frame_width; + gl1_video_height = frame_height; + gl1_video_pitch = pitch; + + tex_width = pitch / (bits / 8); + pot_width = get_pot(tex_width); + pot_height = get_pot(frame_height); + + if (gl1_video_buf) + free(gl1_video_buf); + + gl1_video_buf = (unsigned char*)malloc(pot_width * pot_height * 4); + } + } + + if (gl1_menu_frame && video_info->menu_is_alive) + { + unsigned x, y; + frame_to_copy = NULL; + width = gl1_menu_width; + height = gl1_menu_height; + pitch = gl1_menu_pitch; + bits = gl1_menu_bits; + + tex_width = pitch / (bits / 8); + pot_width = get_pot(tex_width); + pot_height = get_pot(height); + + if (!gl1_video_buf) + gl1_video_buf = (unsigned char*)malloc(pot_width * pot_height * 4); + + if (bits == 16 && gl1_video_buf) + { + /* change bit depth from 4444 to 8888 */ + for (y = 0; y < height; y++) + { + for (x = 0; x < tex_width; x++) + { + unsigned pixel = ((unsigned short*)gl1_menu_frame)[tex_width * y + x]; + unsigned *new_pixel = (unsigned*)gl1_video_buf + (pot_width * y + x); + unsigned r = (255.0f / 15.0f) * ((pixel & 0xF000) >> 12); + unsigned g = (255.0f / 15.0f) * ((pixel & 0xF00) >> 8); + unsigned b = (255.0f / 15.0f) * ((pixel & 0xF0) >> 4); + unsigned a = (255.0f / 15.0f) * ((pixel & 0xF) >> 0); + /* copy pixels into top-left portion of larger (power-of-two) buffer */ + *new_pixel = (a << 24) | (r << 16) | (g << 8) | b; + } + } + + frame_to_copy = gl1_video_buf; + } + } + else + { + width = gl1_video_width; + height = gl1_video_height; + pitch = gl1_video_pitch; + + tex_width = pitch / (bits / 8); + pot_width = get_pot(tex_width); + pot_height = get_pot(height); + + if ( frame_width == 4 && + frame_height == 4 && + (frame_width < width && frame_height < height) + ) + draw = false; + + if (video_info->menu_is_alive) + draw = false; + + if (draw && gl1_video_buf) + { + unsigned x, y; + + if (bits == 32) + { + for (y = 0; y < height; y++) + { + /* copy lines into top-left portion of larger (power-of-two) buffer */ + memcpy(gl1_video_buf + ((pot_width * (bits / 8)) * y), frame + (pitch * y), pitch); + } + } + else if (bits == 16) + { + /* change bit depth from 565 to 8888 */ + for (y = 0; y < height; y++) + { + for (x = 0; x < tex_width; x++) + { + unsigned pixel = ((unsigned short*)frame)[tex_width * y + x]; + unsigned *new_pixel = (unsigned*)gl1_video_buf + (pot_width * y + x); + unsigned r = (255.0f / 31.0f) * ((pixel & 0xF800) >> 11); + unsigned g = (255.0f / 63.0f) * ((pixel & 0x7E0) >> 5); + unsigned b = (255.0f / 31.0f) * ((pixel & 0x1F) >> 0); + /* copy pixels into top-left portion of larger (power-of-two) buffer */ + *new_pixel = 0xFF000000 | (r << 16) | (g << 8) | b; + } + } + } + + frame_to_copy = gl1_video_buf; + } + } + + if (gl1->video_width != width || gl1->video_height != height) + { + gl1->video_width = width; + gl1->video_height = height; + } + + video_context_driver_get_video_size(&mode); + + gl1->screen_width = mode.width; + gl1->screen_height = mode.height; + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + if (draw && frame_to_copy) + { + /* FIXME: For now, everything is uploaded as BGRA8888, I could not get 444 or 555 to work, and there is no 565 support in GL 1.1 either. */ + GLint internalFormat = GL_RGBA8; + GLenum format = (gl1->supports_bgra ? GL_BGRA_EXT : GL_RGBA); + GLenum type = GL_UNSIGNED_BYTE; + + /*glDisable(GL_BLEND);*/ + glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_SCISSOR_TEST); + glEnable(GL_TEXTURE_2D); + + /* multi-texture not part of GL 1.1 */ + /*glActiveTexture(GL_TEXTURE0);*/ + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, pot_width); + glBindTexture(GL_TEXTURE_2D, gl1->tex); + + /* TODO: We could implement red/blue swap if client GL does not support BGRA... but even MS GDI Generic supports it */ + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, pot_width, pot_height, 0, format, type, NULL); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_width, height, format, type, frame_to_copy); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + /*glLoadMatrixf(gl1->mvp.data);*/ + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + /*glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glColorPointer(4, GL_FLOAT, 0, gl1->coords.color); + glVertexPointer(2, GL_FLOAT, 0, gl1->coords.vertex); + glTexCoordPointer(2, GL_FLOAT, 0, gl1->coords.tex_coord); + + glDrawArrays(GL_TRIANGLES, 0, gl1->coords.vertices); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY);*/ + + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + glBegin(GL_QUADS); + + { + float tex_BL[2] = {0.0f, 0.0f}; + float tex_BR[2] = {1.0f, 0.0f}; + float tex_TL[2] = {0.0f, 1.0f}; + float tex_TR[2] = {1.0f, 1.0f}; + float *tex_mirror_BL = tex_TL; + float *tex_mirror_BR = tex_TR; + float *tex_mirror_TL = tex_BL; + float *tex_mirror_TR = tex_BR; + float norm_width = (1.0f / (float)pot_width) * (float)tex_width; + float norm_height = (1.0f / (float)pot_height) * (float)height; + + /* remove extra POT padding */ + tex_mirror_BR[0] = norm_width; + tex_mirror_TR[0] = norm_width; + + /* normally this would be 1.0 - height, but we're drawing upside-down */ + tex_mirror_BL[1] = norm_height; + tex_mirror_BR[1] = norm_height; + + glTexCoord2f(tex_mirror_BL[0], tex_mirror_BL[1]); + glVertex2f(-1.0f, -1.0f); + + glTexCoord2f(tex_mirror_TL[0], tex_mirror_TL[1]); + glVertex2f(-1.0f, 1.0f); + + glTexCoord2f(tex_mirror_TR[0], tex_mirror_TR[1]); + glVertex2f(1.0f, 1.0f); + + glTexCoord2f(tex_mirror_BR[0], tex_mirror_BR[1]); + glVertex2f(1.0f, -1.0f); + } + + glEnd(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + } + + if (msg) + font_driver_render_msg(video_info, NULL, msg, NULL); + + video_info->cb_update_window_title( + video_info->context_data, video_info); + + video_info->cb_swap_buffers(video_info->context_data, video_info); + + gl1_context_bind_hw_render(gl1, true); + + return true; +} + +static void gl1_gfx_set_nonblock_state(void *data, bool state) +{ + int interval = 0; + gl1_t *gl1 = (gl1_t*)data; + settings_t *settings = config_get_ptr(); + + if (!gl1) + return; + + RARCH_LOG("[GL1]: VSync => %s\n", state ? "off" : "on"); + + gl1_context_bind_hw_render(gl1, false); + + if (!state) + interval = settings->uints.video_swap_interval; + + video_context_driver_swap_interval(&interval); + gl1_context_bind_hw_render(gl1, true); +} + +static bool gl1_gfx_alive(void *data) +{ + unsigned temp_width = 0; + unsigned temp_height = 0; + bool quit = false; + bool resize = false; + bool ret = false; + bool is_shutdown = rarch_ctl(RARCH_CTL_IS_SHUTDOWN, NULL); + gl1_t *gl1 = (gl1_t*)data; + + /* Needed because some context drivers don't track their sizes */ + video_driver_get_size(&temp_width, &temp_height); + + gl1->ctx_driver->check_window(gl1->ctx_data, + &quit, &resize, &temp_width, &temp_height, is_shutdown); + + if (resize) + gl1->should_resize = true; + + ret = !quit; + + if (temp_width != 0 && temp_height != 0) + video_driver_set_size(&temp_width, &temp_height); + + return ret; +} + +static bool gl1_gfx_focus(void *data) +{ + (void)data; + return true; +} + +static bool gl1_gfx_suppress_screensaver(void *data, bool enable) +{ + (void)data; + (void)enable; + return false; +} + +static bool gl1_gfx_has_windowed(void *data) +{ + (void)data; + return true; +} + +static void gl1_gfx_free(void *data) +{ + gl1_t *gl1 = (gl1_t*)data; + + gl1_context_bind_hw_render(gl1, false); + + if (gl1_menu_frame) + { + free(gl1_menu_frame); + gl1_menu_frame = NULL; + } + + if (gl1_video_buf) + { + free(gl1_video_buf); + gl1_video_buf = NULL; + } + + if (!gl1) + return; + + if (gl1->tex) + { + glDeleteTextures(1, &gl1->tex); + gl1->tex = 0; + } + + if (gl1->extensions) + { + string_list_free(gl1->extensions); + gl1->extensions = NULL; + } + + font_driver_free_osd(); + video_context_driver_free(); + free(gl1); +} + +static bool gl1_gfx_set_shader(void *data, + enum rarch_shader_type type, const char *path) +{ + (void)data; + (void)type; + (void)path; + + return false; +} + +static void gl1_gfx_set_rotation(void *data, + unsigned rotation) +{ + gl1_t *gl1 = (gl1_t*)data; + + if (!gl1) + return; + + gl1->rotation = 90 * rotation; + gl1_set_projection(gl1, &gl1_default_ortho, true); +} + +static void gl1_gfx_viewport_info(void *data, + struct video_viewport *vp) +{ + (void)data; + (void)vp; +} + +static bool gl1_gfx_read_viewport(void *data, uint8_t *buffer, bool is_idle) +{ + (void)data; + (void)buffer; + + return true; +} + +static void gl1_set_texture_frame(void *data, + const void *frame, bool rgb32, unsigned width, unsigned height, + float alpha) +{ + unsigned pitch = width * 2; + gl1_t *gl1 = (gl1_t*)data; + + if (!gl1) + return; + + gl1_context_bind_hw_render(gl1, false); + + if (rgb32) + pitch = width * 4; + + if (gl1_menu_frame) + { + free(gl1_menu_frame); + gl1_menu_frame = NULL; + } + + if ( !gl1_menu_frame || + gl1_menu_width != width || + gl1_menu_height != height || + gl1_menu_pitch != pitch) + { + if (pitch && height) + { + if (gl1_menu_frame) + free(gl1_menu_frame); + + /* FIXME? We have to assume the pitch has no extra padding in it because that will mess up the POT calculation when we don't know how many bpp there are. */ + gl1_menu_frame = (unsigned char*)malloc(pitch * height); + } + } + + if (gl1_menu_frame && frame && pitch && height) + { + memcpy(gl1_menu_frame, frame, pitch * height); + gl1_menu_width = width; + gl1_menu_height = height; + gl1_menu_pitch = pitch; + gl1_menu_bits = rgb32 ? 32 : 16; + } + + gl1_context_bind_hw_render(gl1, true); +} + +static void gl1_set_osd_msg(void *data, + video_frame_info_t *video_info, + const char *msg, + const void *params, void *font) +{ + font_driver_render_msg(video_info, font, + msg, (const struct font_params *)params); +} + +static void gl1_get_video_output_size(void *data, + unsigned *width, unsigned *height) +{ + gfx_ctx_size_t size_data; + size_data.width = width; + size_data.height = height; + video_context_driver_get_video_output_size(&size_data); +} + +static void gl1_get_video_output_prev(void *data) +{ + video_context_driver_get_video_output_prev(); +} + +static void gl1_get_video_output_next(void *data) +{ + video_context_driver_get_video_output_next(); +} + +static void gl1_set_video_mode(void *data, unsigned width, unsigned height, + bool fullscreen) +{ + gfx_ctx_mode_t mode; + + mode.width = width; + mode.height = height; + mode.fullscreen = fullscreen; + + video_context_driver_set_video_mode(&mode); +} + +static uintptr_t gl1_load_texture(void *video_data, void *data, + bool threaded, enum texture_filter_type filter_type) +{ + void *tmpdata = NULL; + gl1_texture_t *texture = NULL; + struct texture_image *image = (struct texture_image*)data; + int size = image->width * + image->height * sizeof(uint32_t); + + if (!image || image->width > 2048 || image->height > 2048) + return 0; + + texture = (gl1_texture_t*)calloc(1, sizeof(*texture)); + + if (!texture) + return 0; + + texture->width = image->width; + texture->height = image->height; + texture->active_width = image->width; + texture->active_height = image->height; + texture->data = calloc(1, + texture->width * texture->height * sizeof(uint32_t)); + texture->type = filter_type; + + if (!texture->data) + { + free(texture); + return 0; + } + + memcpy(texture->data, image->pixels, + texture->width * texture->height * sizeof(uint32_t)); + + return (uintptr_t)texture; +} + +static void gl1_set_aspect_ratio(void *data, unsigned aspect_ratio_idx) +{ + gl1_t *gl1 = (gl1_t*)data; + + switch (aspect_ratio_idx) + { + case ASPECT_RATIO_SQUARE: + video_driver_set_viewport_square_pixel(); + break; + + case ASPECT_RATIO_CORE: + video_driver_set_viewport_core(); + break; + + case ASPECT_RATIO_CONFIG: + video_driver_set_viewport_config(); + break; + + default: + break; + } + + video_driver_set_aspect_ratio_value( + aspectratio_lut[aspect_ratio_idx].value); + + if (!gl1) + return; + + gl1->keep_aspect = true; + gl1->should_resize = true; +} + +static void gl1_unload_texture(void *data, uintptr_t handle) +{ + struct gl1_texture *texture = (struct gl1_texture*)handle; + + if (!texture) + return; + + if (texture->data) + free(texture->data); + + free(texture); +} + +static float gl1_get_refresh_rate(void *data) +{ + float refresh_rate = 0.0f; + if (video_context_driver_get_refresh_rate(&refresh_rate)) + return refresh_rate; + return 0.0f; +} + +static const video_poke_interface_t gl1_poke_interface = { + NULL, /* get_flags */ + NULL, /* set_coords */ + NULL, /* set_mvp */ + gl1_load_texture, + gl1_unload_texture, + gl1_set_video_mode, + gl1_get_refresh_rate, + NULL, + gl1_get_video_output_size, + gl1_get_video_output_prev, + gl1_get_video_output_next, + NULL, + NULL, + gl1_set_aspect_ratio, + NULL, + gl1_set_texture_frame, + NULL, + gl1_set_osd_msg, + NULL, + NULL, /* grab_mouse_toggle */ + NULL, /* get_current_shader */ + NULL, /* get_current_software_framebuffer */ + NULL /* get_hw_render_interface */ +}; + +static void gl1_gfx_get_poke_interface(void *data, + const video_poke_interface_t **iface) +{ + (void)data; + *iface = &gl1_poke_interface; +} + +static void gl1_gfx_set_viewport_wrapper(void *data, unsigned viewport_width, + unsigned viewport_height, bool force_full, bool allow_rotate) +{ + video_frame_info_t video_info; + gl1_t *gl1 = (gl1_t*)data; + + video_driver_build_info(&video_info); + + gl1_gfx_set_viewport(gl1, &video_info, + viewport_width, viewport_height, force_full, allow_rotate); +} + +bool gl1_has_menu_frame(void) +{ + return (gl1_menu_frame != NULL); +} + +static unsigned gl1_wrap_type_to_enum(enum gfx_wrap_type type) +{ + switch (type) + { + case RARCH_WRAP_REPEAT: + case RARCH_WRAP_MIRRORED_REPEAT: /* mirrored not actually supported */ + return GL_REPEAT; + default: + return GL_CLAMP; + break; + } + + return 0; +} + +video_driver_t video_gl1 = { + gl1_gfx_init, + gl1_gfx_frame, + gl1_gfx_set_nonblock_state, + gl1_gfx_alive, + gl1_gfx_focus, + gl1_gfx_suppress_screensaver, + gl1_gfx_has_windowed, + gl1_gfx_set_shader, + gl1_gfx_free, + "gl1", + gl1_gfx_set_viewport_wrapper, + gl1_gfx_set_rotation, + gl1_gfx_viewport_info, + gl1_gfx_read_viewport, + NULL, /* read_frame_raw */ + +#ifdef HAVE_OVERLAY + NULL, /* overlay_interface */ +#endif + gl1_gfx_get_poke_interface, + gl1_wrap_type_to_enum, +#if defined(HAVE_MENU) && defined(HAVE_MENU_WIDGETS) + NULL +#endif +}; diff --git a/gfx/drivers_font/gl1_raster_font.c b/gfx/drivers_font/gl1_raster_font.c new file mode 100644 index 0000000000..aed73f80fc --- /dev/null +++ b/gfx/drivers_font/gl1_raster_font.c @@ -0,0 +1,581 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2017 - Brad Parker + * + * 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 "../common/gl1_common.h" +#include "../font_driver.h" +#include "../video_driver.h" +#include "../../verbosity.h" + +/* TODO: Move viewport side effects to the caller: it's a source of bugs. */ + +#define gl1_raster_font_emit(c, vx, vy) do { \ + font_vertex[ 2 * (6 * i + c) + 0] = (x + (delta_x + off_x + vx * width) * scale) * inv_win_width; \ + font_vertex[ 2 * (6 * i + c) + 1] = (y + (delta_y - off_y - vy * height) * scale) * inv_win_height; \ + font_tex_coords[ 2 * (6 * i + c) + 0] = (tex_x + vx * width) * inv_tex_size_x; \ + font_tex_coords[ 2 * (6 * i + c) + 1] = (tex_y + vy * height) * inv_tex_size_y; \ + font_color[ 4 * (6 * i + c) + 0] = color[0]; \ + font_color[ 4 * (6 * i + c) + 1] = color[1]; \ + font_color[ 4 * (6 * i + c) + 2] = color[2]; \ + font_color[ 4 * (6 * i + c) + 3] = color[3]; \ + font_lut_tex_coord[ 2 * (6 * i + c) + 0] = gl->coords.lut_tex_coord[0]; \ + font_lut_tex_coord[ 2 * (6 * i + c) + 1] = gl->coords.lut_tex_coord[1]; \ +} while(0) + +#define MAX_MSG_LEN_CHUNK 64 + +typedef struct +{ + gl1_t *gl; + GLuint tex; + unsigned tex_width, tex_height; + + const font_renderer_driver_t *font_driver; + void *font_data; + struct font_atlas *atlas; + + video_font_raster_block_t *block; +} gl1_raster_t; + +static void gl1_raster_font_free_font(void *data, + bool is_threaded) +{ + gl1_raster_t *font = (gl1_raster_t*)data; + if (!font) + return; + + if (font->font_driver && font->font_data) + font->font_driver->free(font->font_data); + + if (is_threaded) + video_context_driver_make_current(true); + + glDeleteTextures(1, &font->tex); + + free(font); +} + +#if 0 +static bool gl1_raster_font_upload_atlas_components_4(gl1_raster_t *font) +{ + unsigned i, j; + GLint gl_internal = GL_RGBA; + GLenum gl_format = GL_RGBA; + size_t ncomponents = 4; + uint8_t *tmp = NULL; + + tmp = (uint8_t*)calloc(font->tex_height, font->tex_width * ncomponents); + + for (i = 0; i < font->atlas->height; ++i) + { + const uint8_t *src = &font->atlas->buffer[i * font->atlas->width]; + uint8_t *dst = &tmp[i * font->tex_width * ncomponents]; + + for (j = 0; j < font->atlas->width; ++j) + { + *dst++ = 0xff; + *dst++ = 0xff; + *dst++ = 0xff; + *dst++ = *src++; + } + break; + } + + glTexImage2D(GL_TEXTURE_2D, 0, gl_internal, font->tex_width, font->tex_height, + 0, gl_format, GL_UNSIGNED_BYTE, tmp); + + free(tmp); + + return true; +} +#endif + +static bool gl1_raster_font_upload_atlas(gl1_raster_t *font) +{ + unsigned i, j; + GLint gl_internal = GL_LUMINANCE_ALPHA; + GLenum gl_format = GL_LUMINANCE_ALPHA; + size_t ncomponents = 2; + uint8_t *tmp = NULL; + + tmp = (uint8_t*)calloc(font->tex_height, font->tex_width * ncomponents); + + switch (ncomponents) + { + case 1: + for (i = 0; i < font->atlas->height; ++i) + { + const uint8_t *src = &font->atlas->buffer[i * font->atlas->width]; + uint8_t *dst = &tmp[i * font->tex_width * ncomponents]; + + memcpy(dst, src, font->atlas->width); + } + break; + case 2: + for (i = 0; i < font->atlas->height; ++i) + { + const uint8_t *src = &font->atlas->buffer[i * font->atlas->width]; + uint8_t *dst = &tmp[i * font->tex_width * ncomponents]; + + for (j = 0; j < font->atlas->width; ++j) + { + *dst++ = 0xff; + *dst++ = *src++; + } + } + break; + } + + glTexImage2D(GL_TEXTURE_2D, 0, gl_internal, font->tex_width, font->tex_height, + 0, gl_format, GL_UNSIGNED_BYTE, tmp); + + free(tmp); + + return true; +} + +static void *gl1_raster_font_init_font(void *data, + const char *font_path, float font_size, + bool is_threaded) +{ + gl1_raster_t *font = (gl1_raster_t*)calloc(1, sizeof(*font)); + + if (!font) + return NULL; + + font->gl = (gl1_t*)data; + + if (!font_renderer_create_default( + &font->font_driver, + &font->font_data, font_path, font_size)) + { + RARCH_WARN("Couldn't initialize font renderer.\n"); + free(font); + return NULL; + } + + RARCH_LOG("[Font]: Using font driver GL1\n"); + + if (is_threaded) + video_context_driver_make_current(false); + + glGenTextures(1, &font->tex); + + gl1_bind_texture(font->tex, GL_CLAMP, GL_LINEAR, GL_LINEAR); + + font->atlas = font->font_driver->get_atlas(font->font_data); + font->tex_width = next_pow2(font->atlas->width); + font->tex_height = next_pow2(font->atlas->height); + + if (!gl1_raster_font_upload_atlas(font)) + goto error; + + font->atlas->dirty = false; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, font->gl->texture[font->gl->tex_index]); + + return font; + +error: + gl1_raster_font_free_font(font, is_threaded); + font = NULL; + + return NULL; +} + +static int gl1_get_message_width(void *data, const char *msg, + unsigned msg_len, float scale) +{ + gl1_raster_t *font = (gl1_raster_t*)data; + const char* msg_end = msg + msg_len; + int delta_x = 0; + + if ( !font + || !font->font_driver + || !font->font_driver->get_glyph + || !font->font_data ) + return 0; + + while (msg < msg_end) + { + unsigned code = utf8_walk(&msg); + const struct font_glyph *glyph = font->font_driver->get_glyph( + font->font_data, code); + + if (!glyph) /* Do something smarter here ... */ + glyph = font->font_driver->get_glyph(font->font_data, '?'); + if (!glyph) + continue; + + delta_x += glyph->advance_x; + } + + return delta_x * scale; +} + +static void gl1_raster_font_draw_vertices(gl1_raster_t *font, + const video_coords_t *coords, + video_frame_info_t *video_info) +{ + video_shader_ctx_coords_t coords_data; + + if (font->atlas->dirty) + { + gl1_raster_font_upload_atlas(font); + font->atlas->dirty = false; + } + + coords_data.handle_data = NULL; + coords_data.data = coords; + + video_driver_set_coords(&coords_data); + + /*video_info->cb_set_mvp(font->gl, + video_info->shader_data, &font->gl->mvp_no_rot);*/ + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(font->gl->mvp.data); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glColorPointer(4, GL_FLOAT, 0, coords->color); + glVertexPointer(2, GL_FLOAT, 0, coords->vertex); + glTexCoordPointer(2, GL_FLOAT, 0, coords->tex_coord); + + glDrawArrays(GL_TRIANGLES, 0, coords->vertices); + + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); +} + +static void gl1_raster_font_render_line( + gl1_raster_t *font, const char *msg, unsigned msg_len, + GLfloat scale, const GLfloat color[4], GLfloat pos_x, + GLfloat pos_y, unsigned text_align, + video_frame_info_t *video_info) +{ + unsigned i; + struct video_coords coords; + GLfloat font_tex_coords[2 * 6 * MAX_MSG_LEN_CHUNK]; + GLfloat font_vertex[2 * 6 * MAX_MSG_LEN_CHUNK]; + GLfloat font_color[4 * 6 * MAX_MSG_LEN_CHUNK]; + GLfloat font_lut_tex_coord[2 * 6 * MAX_MSG_LEN_CHUNK]; + gl1_t *gl = font->gl; + const char* msg_end = msg + msg_len; + int x = roundf(pos_x * gl->vp.width); + int y = roundf(pos_y * gl->vp.height); + int delta_x = 0; + int delta_y = 0; + float inv_tex_size_x = 1.0f / font->tex_width; + float inv_tex_size_y = 1.0f / font->tex_height; + float inv_win_width = 1.0f / font->gl->vp.width; + float inv_win_height = 1.0f / font->gl->vp.height; + + switch (text_align) + { + case TEXT_ALIGN_RIGHT: + x -= gl1_get_message_width(font, msg, msg_len, scale); + break; + case TEXT_ALIGN_CENTER: + x -= gl1_get_message_width(font, msg, msg_len, scale) / 2.0; + break; + } + + while (msg < msg_end) + { + i = 0; + while ((i < MAX_MSG_LEN_CHUNK) && (msg < msg_end)) + { + int off_x, off_y, tex_x, tex_y, width, height; + unsigned code = utf8_walk(&msg); + const struct font_glyph *glyph = font->font_driver->get_glyph( + font->font_data, code); + + if (!glyph) /* Do something smarter here ... */ + glyph = font->font_driver->get_glyph(font->font_data, '?'); + + if (!glyph) + continue; + + off_x = glyph->draw_offset_x; + off_y = glyph->draw_offset_y; + tex_x = glyph->atlas_offset_x; + tex_y = glyph->atlas_offset_y; + width = glyph->width; + height = glyph->height; + + gl1_raster_font_emit(0, 0, 1); /* Bottom-left */ + gl1_raster_font_emit(1, 1, 1); /* Bottom-right */ + gl1_raster_font_emit(2, 0, 0); /* Top-left */ + + gl1_raster_font_emit(3, 1, 0); /* Top-right */ + gl1_raster_font_emit(4, 0, 0); /* Top-left */ + gl1_raster_font_emit(5, 1, 1); /* Bottom-right */ + + i++; + + delta_x += glyph->advance_x; + delta_y -= glyph->advance_y; + } + + coords.tex_coord = font_tex_coords; + coords.vertex = font_vertex; + coords.color = font_color; + coords.vertices = i * 6; + coords.lut_tex_coord = font_lut_tex_coord; + + if (font->block) + video_coord_array_append(&font->block->carr, &coords, coords.vertices); + else + gl1_raster_font_draw_vertices(font, &coords, video_info); + } +} + +static void gl1_raster_font_render_message( + gl1_raster_t *font, const char *msg, GLfloat scale, + const GLfloat color[4], GLfloat pos_x, GLfloat pos_y, + unsigned text_align, + video_frame_info_t *video_info) +{ + float line_height; + int lines = 0; + + /* If the font height is not supported just draw as usual */ + if (!font->font_driver->get_line_height) + { + gl1_raster_font_render_line(font, + msg, (unsigned)strlen(msg), scale, color, pos_x, + pos_y, text_align, + video_info); + return; + } + + line_height = (float) font->font_driver->get_line_height(font->font_data) * + scale / font->gl->vp.height; + + for (;;) + { + const char *delim = strchr(msg, '\n'); + unsigned msg_len = delim + ? (unsigned)(delim - msg) : (unsigned)strlen(msg); + + /* Draw the line */ + gl1_raster_font_render_line(font, + msg, msg_len, scale, color, pos_x, + pos_y - (float)lines*line_height, text_align, + video_info); + + if (!delim) + break; + + msg += msg_len + 1; + lines++; + } +} + +static void gl1_raster_font_setup_viewport(unsigned width, unsigned height, + gl1_raster_t *font, bool full_screen) +{ + video_shader_ctx_info_t shader_info; + + video_driver_set_viewport(width, height, full_screen, false); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + /*glBlendEquation(GL_FUNC_ADD);*/ + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, font->tex); + + /*shader_info.data = NULL; + shader_info.idx = VIDEO_SHADER_STOCK_BLEND; + shader_info.set_active = true; + + video_shader_driver_use(&shader_info);*/ +} + +static void gl1_raster_font_render_msg( + video_frame_info_t *video_info, + void *data, const char *msg, + const struct font_params *params) +{ + GLfloat color[4]; + int drop_x, drop_y; + GLfloat x, y, scale, drop_mod, drop_alpha; + enum text_alignment text_align = TEXT_ALIGN_LEFT; + bool full_screen = false ; + gl1_raster_t *font = (gl1_raster_t*)data; + unsigned width = video_info->width; + unsigned height = video_info->height; + + if (!font || string_is_empty(msg)) + return; + + if (params) + { + x = params->x; + y = params->y; + scale = params->scale; + full_screen = params->full_screen; + text_align = params->text_align; + drop_x = params->drop_x; + drop_y = params->drop_y; + drop_mod = params->drop_mod; + drop_alpha = params->drop_alpha; + + color[0] = FONT_COLOR_GET_RED(params->color) / 255.0f; + color[1] = FONT_COLOR_GET_GREEN(params->color) / 255.0f; + color[2] = FONT_COLOR_GET_BLUE(params->color) / 255.0f; + color[3] = FONT_COLOR_GET_ALPHA(params->color) / 255.0f; + + /* If alpha is 0.0f, turn it into default 1.0f */ + if (color[3] <= 0.0f) + color[3] = 1.0f; + } + else + { + x = video_info->font_msg_pos_x; + y = video_info->font_msg_pos_y; + scale = 1.0f; + full_screen = true; + text_align = TEXT_ALIGN_LEFT; + + color[0] = video_info->font_msg_color_r; + color[1] = video_info->font_msg_color_g; + color[2] = video_info->font_msg_color_b; + color[3] = 1.0f; + + drop_x = -2; + drop_y = -2; + drop_mod = 0.3f; + drop_alpha = 1.0f; + } + + if (font->block) + font->block->fullscreen = full_screen; + else + gl1_raster_font_setup_viewport(width, height, font, full_screen); + + if (!string_is_empty(msg) && font->gl + && font->font_data && font->font_driver) + { + if (drop_x || drop_y) + { + GLfloat color_dark[4]; + + color_dark[0] = color[0] * drop_mod; + color_dark[1] = color[1] * drop_mod; + color_dark[2] = color[2] * drop_mod; + color_dark[3] = color[3] * drop_alpha; + + if (font->gl) + gl1_raster_font_render_message(font, msg, scale, color_dark, + x + scale * drop_x / font->gl->vp.width, y + + scale * drop_y / font->gl->vp.height, text_align, + video_info); + } + + if (font->gl) + gl1_raster_font_render_message(font, msg, scale, color, + x, y, text_align, video_info); + } + + if (!font->block && font->gl) + { + /* restore viewport */ + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, font->gl->texture[font->gl->tex_index]); + + glDisable(GL_BLEND); + video_driver_set_viewport(width, height, false, true); + } +} + +static const struct font_glyph *gl1_raster_font_get_glyph( + void *data, uint32_t code) +{ + gl1_raster_t *font = (gl1_raster_t*)data; + + if (!font || !font->font_driver) + return NULL; + if (!font->font_driver->ident) + return NULL; + return font->font_driver->get_glyph((void*)font->font_driver, code); +} + +static void gl1_raster_font_flush_block(unsigned width, unsigned height, + void *data, video_frame_info_t *video_info) +{ + gl1_raster_t *font = (gl1_raster_t*)data; + video_font_raster_block_t *block = font ? font->block : NULL; + + if (!font || !block || !block->carr.coords.vertices) + return; + + gl1_raster_font_setup_viewport(width, height, font, block->fullscreen); + gl1_raster_font_draw_vertices(font, (video_coords_t*)&block->carr.coords, + video_info); + + if (font->gl) + { + /* restore viewport */ + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, font->gl->texture[font->gl->tex_index]); + + glDisable(GL_BLEND); + video_driver_set_viewport(width, height, block->fullscreen, true); + } +} + +static void gl1_raster_font_bind_block(void *data, void *userdata) +{ + gl1_raster_t *font = (gl1_raster_t*)data; + video_font_raster_block_t *block = (video_font_raster_block_t*)userdata; + + if (font) + font->block = block; +} + +font_renderer_t gl1_raster_font = { + gl1_raster_font_init_font, + gl1_raster_font_free_font, + gl1_raster_font_render_msg, + "GL1 raster", + gl1_raster_font_get_glyph, + gl1_raster_font_bind_block, + gl1_raster_font_flush_block, + gl1_get_message_width +}; diff --git a/gfx/font_driver.c b/gfx/font_driver.c index de4cf2f5b1..7b53005901 100644 --- a/gfx/font_driver.c +++ b/gfx/font_driver.c @@ -183,6 +183,35 @@ static bool gl_font_init_first( return false; } + +static const font_renderer_t *gl1_font_backends[] = { + &gl1_raster_font, + NULL, +}; + +static bool gl1_font_init_first( + const void **font_driver, void **font_handle, + void *video_data, const char *font_path, + float font_size, bool is_threaded) +{ + unsigned i; + + for (i = 0; gl1_font_backends[i]; i++) + { + void *data = gl1_font_backends[i]->init( + video_data, font_path, font_size, + is_threaded); + + if (!data) + continue; + + *font_driver = gl1_font_backends[i]; + *font_handle = data; + return true; + } + + return false; +} #endif #ifdef HAVE_CACA @@ -630,6 +659,9 @@ static bool font_init_first( case FONT_DRIVER_RENDER_OPENGL_API: return gl_font_init_first(font_driver, font_handle, video_data, font_path, font_size, is_threaded); + case FONT_DRIVER_RENDER_OPENGL1_API: + return gl1_font_init_first(font_driver, font_handle, + video_data, font_path, font_size, is_threaded); #endif #ifdef HAVE_VULKAN case FONT_DRIVER_RENDER_VULKAN_API: diff --git a/gfx/font_driver.h b/gfx/font_driver.h index 499c0a16e1..7e808311a1 100644 --- a/gfx/font_driver.h +++ b/gfx/font_driver.h @@ -157,6 +157,7 @@ void font_driver_init_osd( void font_driver_free_osd(void); extern font_renderer_t gl_raster_font; +extern font_renderer_t gl1_raster_font; extern font_renderer_t libdbg_font; extern font_renderer_t d3d_xbox360_font; extern font_renderer_t d3d_xdk1_font; diff --git a/gfx/video_defines.h b/gfx/video_defines.h index bf5a821972..b06287f5cc 100644 --- a/gfx/video_defines.h +++ b/gfx/video_defines.h @@ -83,6 +83,7 @@ enum font_driver_render_api { FONT_DRIVER_RENDER_DONT_CARE, FONT_DRIVER_RENDER_OPENGL_API, + FONT_DRIVER_RENDER_OPENGL1_API, FONT_DRIVER_RENDER_D3D8_API, FONT_DRIVER_RENDER_D3D9_API, FONT_DRIVER_RENDER_D3D10_API, diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 9d7e308a0b..a6f5f29fae 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -260,6 +260,7 @@ struct aspect_ratio_elem aspectratio_lut[ASPECT_RATIO_END] = { static const video_driver_t *video_drivers[] = { #ifdef HAVE_OPENGL &video_gl2, + &video_gl1, #endif #ifdef HAVE_VULKAN &video_vulkan, @@ -425,7 +426,6 @@ static const gfx_ctx_driver_t *gfx_ctx_drivers[] = { NULL }; - bool video_driver_started_fullscreen(void) { return video_started_fullscreen; diff --git a/gfx/video_driver.h b/gfx/video_driver.h index 6e745f9c8a..fcdc17c5e3 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -1209,6 +1209,7 @@ bool video_driver_get_all_flags(gfx_ctx_flags_t *flags, enum display_flags flag); extern video_driver_t video_gl2; +extern video_driver_t video_gl1; extern video_driver_t video_vulkan; extern video_driver_t video_metal; extern video_driver_t video_psp1; diff --git a/griffin/griffin.c b/griffin/griffin.c index 7d54aead27..5d34ee0117 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -411,6 +411,7 @@ VIDEO DRIVER #ifdef HAVE_OPENGL #include "../gfx/drivers/gl.c" +#include "../gfx/drivers/gl1.c" #include "../libretro-common/gfx/gl_capabilities.c" #ifndef HAVE_PSGL @@ -488,6 +489,7 @@ FONTS #if defined(HAVE_OPENGL) #include "../gfx/drivers_font/gl_raster_font.c" +#include "../gfx/drivers_font/gl1_raster_font.c" #endif #if defined(_XBOX1) @@ -1248,6 +1250,7 @@ MENU #ifdef HAVE_OPENGL #include "../menu/drivers_display/menu_display_gl.c" +#include "../menu/drivers_display/menu_display_gl1.c" #endif #ifdef HAVE_VULKAN diff --git a/menu/drivers_display/menu_display_gl1.c b/menu/drivers_display/menu_display_gl1.c new file mode 100644 index 0000000000..744bba10f6 --- /dev/null +++ b/menu/drivers_display/menu_display_gl1.c @@ -0,0 +1,223 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2017 - 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 . + */ + +#include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#include "../../retroarch.h" +#include "../../gfx/font_driver.h" +#include "../../gfx/video_driver.h" +#include "../../gfx/common/gl1_common.h" + +#include "../menu_driver.h" + +static const GLfloat gl1_menu_vertexes[] = { + 0, 0, + 1, 0, + 0, 1, + 1, 1 +}; + +static const GLfloat gl1_menu_tex_coords[] = { + 0, 1, + 1, 1, + 0, 0, + 1, 0 +}; + +static const float *menu_display_gl1_get_default_vertices(void) +{ + return &gl1_menu_vertexes[0]; +} + +static const float *menu_display_gl1_get_default_tex_coords(void) +{ + return &gl1_menu_tex_coords[0]; +} + +static void *menu_display_gl1_get_default_mvp(video_frame_info_t *video_info) +{ + gl1_t *gl1 = video_info ? (gl1_t*)video_info->userdata : NULL; + + if (!gl1) + return NULL; + + return &gl1->mvp_no_rot; +} + +static GLenum menu_display_prim_to_gl1_enum( + enum menu_display_prim_type type) +{ + switch (type) + { + case MENU_DISPLAY_PRIM_TRIANGLESTRIP: + return GL_TRIANGLE_STRIP; + case MENU_DISPLAY_PRIM_TRIANGLES: + return GL_TRIANGLES; + case MENU_DISPLAY_PRIM_NONE: + default: + break; + } + + return 0; +} + +static void menu_display_gl1_blend_begin(video_frame_info_t *video_info) +{ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +static void menu_display_gl1_blend_end(video_frame_info_t *video_info) +{ + glDisable(GL_BLEND); +} + +static void menu_display_gl1_viewport(menu_display_ctx_draw_t *draw, + video_frame_info_t *video_info) +{ + if (draw) + glViewport(draw->x, draw->y, draw->width, draw->height); +} + +static void menu_display_gl1_draw(menu_display_ctx_draw_t *draw, + video_frame_info_t *video_info) +{ + video_shader_ctx_mvp_t mvp; + video_shader_ctx_coords_t coords; + gl1_t *gl1 = video_info ? + (gl1_t*)video_info->userdata : NULL; + + if (!gl1 || !draw) + return; + + if (!draw->coords->vertex) + draw->coords->vertex = menu_display_gl1_get_default_vertices(); + if (!draw->coords->tex_coord) + draw->coords->tex_coord = menu_display_gl1_get_default_tex_coords(); + if (!draw->coords->lut_tex_coord) + draw->coords->lut_tex_coord = menu_display_gl1_get_default_tex_coords(); + + menu_display_gl1_viewport(draw, video_info); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, (GLuint)draw->texture); + + coords.handle_data = gl1; + coords.data = draw->coords; + + video_driver_set_coords(&coords); + + mvp.data = gl1; + mvp.matrix = draw->matrix_data ? (math_matrix_4x4*)draw->matrix_data + : (math_matrix_4x4*)menu_display_gl1_get_default_mvp(video_info); + + video_driver_set_mvp(&mvp); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(((math_matrix_4x4*)mvp.matrix)->data); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glColorPointer(4, GL_FLOAT, 0, draw->coords->color); + glVertexPointer(2, GL_FLOAT, 0, draw->coords->vertex); + glTexCoordPointer(2, GL_FLOAT, 0, draw->coords->tex_coord); + + glDrawArrays(menu_display_prim_to_gl1_enum( + draw->prim_type), 0, draw->coords->vertices); + + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + gl1->coords.color = gl1->white_color_ptr; +} + +static void menu_display_gl1_restore_clear_color(void) +{ + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); +} + +static void menu_display_gl1_clear_color( + menu_display_ctx_clearcolor_t *clearcolor, + video_frame_info_t *video_info) +{ + if (!clearcolor) + return; + + glClearColor(clearcolor->r, + clearcolor->g, clearcolor->b, clearcolor->a); + glClear(GL_COLOR_BUFFER_BIT); +} + +static bool menu_display_gl1_font_init_first( + void **font_handle, void *video_data, + const char *font_path, float menu_font_size, + bool is_threaded) +{ + font_data_t **handle = (font_data_t**)font_handle; + if (!(*handle = font_driver_init_first(video_data, + font_path, menu_font_size, true, + is_threaded, + FONT_DRIVER_RENDER_OPENGL1_API))) + return false; + return true; +} + +static void menu_display_gl1_scissor_begin(video_frame_info_t *video_info, int x, int y, + unsigned width, unsigned height) +{ + glScissor(x, video_info->height - y - height, width, height); + glEnable(GL_SCISSOR_TEST); +} + +static void menu_display_gl1_scissor_end(video_frame_info_t *video_info) +{ + glDisable(GL_SCISSOR_TEST); +} + +menu_display_ctx_driver_t menu_display_ctx_gl1 = { + menu_display_gl1_draw, + NULL, + menu_display_gl1_viewport, + menu_display_gl1_blend_begin, + menu_display_gl1_blend_end, + menu_display_gl1_restore_clear_color, + menu_display_gl1_clear_color, + menu_display_gl1_get_default_mvp, + menu_display_gl1_get_default_vertices, + menu_display_gl1_get_default_tex_coords, + menu_display_gl1_font_init_first, + MENU_VIDEO_DRIVER_OPENGL1, + "gl1", + false, + menu_display_gl1_scissor_begin, + menu_display_gl1_scissor_end +}; diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 311addcd7f..1375981f57 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -126,6 +126,7 @@ static menu_display_ctx_driver_t *menu_display_ctx_drivers[] = { #endif #ifdef HAVE_OPENGL &menu_display_ctx_gl, + &menu_display_ctx_gl1, #endif #ifdef HAVE_VULKAN &menu_display_ctx_vulkan, @@ -274,6 +275,10 @@ static bool menu_display_check_compatibility( if (string_is_equal(video_driver, "gl")) return true; break; + case MENU_VIDEO_DRIVER_OPENGL1: + if (string_is_equal(video_driver, "gl1")) + return true; + break; case MENU_VIDEO_DRIVER_VULKAN: if (string_is_equal(video_driver, "vulkan")) return true; @@ -781,7 +786,8 @@ void menu_display_draw_bg(menu_display_ctx_draw_t *draw, if (!draw->texture) draw->texture = menu_display_white_texture; - draw->matrix_data = (math_matrix_4x4*)menu_disp->get_default_mvp(video_info); + if (menu_disp && menu_disp->get_default_mvp) + draw->matrix_data = (math_matrix_4x4*)menu_disp->get_default_mvp(video_info); } void menu_display_draw_gradient(menu_display_ctx_draw_t *draw, diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 58cb2afac8..727c078370 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -692,6 +692,7 @@ void menu_subsystem_populate(const struct retro_subsystem_info* subsystem, menu_ extern uintptr_t menu_display_white_texture; extern menu_display_ctx_driver_t menu_display_ctx_gl; +extern menu_display_ctx_driver_t menu_display_ctx_gl1; extern menu_display_ctx_driver_t menu_display_ctx_vulkan; extern menu_display_ctx_driver_t menu_display_ctx_metal; extern menu_display_ctx_driver_t menu_display_ctx_d3d8;