diff --git a/driver.h b/driver.h index 5ffbe23332..cff09a9cc1 100644 --- a/driver.h +++ b/driver.h @@ -289,9 +289,16 @@ typedef struct driver uintptr_t video_window; enum rarch_display_type display_type; + // Used for 15-bit -> 16-bit conversions that take place before being passed to video driver. struct scaler_ctx scaler; void *scaler_out; + // Graphics driver requires RGBA byte order data (ABGR on little-endian) for 32-bit. + // This takes effect for overlay and shader cores that wants to load data into graphics driver. + // Kinda hackish to place it here, it is only used for GLES. + // TODO: Refactor this better. + bool gfx_use_rgba; + #ifdef HAVE_OVERLAY input_overlay_t *overlay; uint64_t overlay_state; diff --git a/gfx/gl.c b/gfx/gl.c index c8a4a6a207..027e5209e8 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -912,6 +912,28 @@ static inline void gl_convert_frame_rgb16_32(gl_t *gl, void *output, const void } #endif +#ifdef HAVE_OPENGLES2 +static inline void gl_convert_frame_argb8888_abgr8888(gl_t *gl, void *output, const void *input, + int width, int height, int in_pitch) +{ + if (width != gl->scaler.in_width || height != gl->scaler.in_height) + { + gl->scaler.in_width = width; + gl->scaler.in_height = height; + gl->scaler.out_width = width; + gl->scaler.out_height = height; + gl->scaler.in_fmt = SCALER_FMT_ARGB8888; + gl->scaler.out_fmt = SCALER_FMT_ABGR8888; + gl->scaler.scaler_type = SCALER_TYPE_POINT; + scaler_ctx_gen_filter(&gl->scaler); + } + + gl->scaler.in_stride = in_pitch; + gl->scaler.out_stride = width * sizeof(uint32_t); + scaler_ctx_scale(&gl->scaler, output, input); +} +#endif + static void gl_init_textures_data(gl_t *gl) { for (unsigned i = 0; i < TEXTURES; i++) @@ -996,27 +1018,40 @@ static inline void gl_copy_frame(gl_t *gl, const void *frame, unsigned width, un else #endif { - // No GL_UNPACK_ROW_LENGTH ;( - unsigned pitch_width = pitch / gl->base_size; - if (width == pitch_width) // Happy path :D + glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(width * gl->base_size)); + + // Fallback for GLES devices without GL_BGRA_EXT. + if (gl->base_size == 4 && driver.gfx_use_rgba) { + gl_convert_frame_argb8888_abgr8888(gl, gl->conv_buffer, frame, width, height, pitch); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, gl->texture_type, - gl->texture_fmt, frame); + gl->texture_fmt, gl->conv_buffer); } - else // Slower path. + else { - const unsigned line_bytes = width * gl->base_size; + // No GL_UNPACK_ROW_LENGTH ;( + unsigned pitch_width = pitch / gl->base_size; + if (width == pitch_width) // Happy path :D + { + glTexSubImage2D(GL_TEXTURE_2D, + 0, 0, 0, width, height, gl->texture_type, + gl->texture_fmt, frame); + } + else // Slower path. + { + const unsigned line_bytes = width * gl->base_size; - uint8_t *dst = (uint8_t*)gl->conv_buffer; // This buffer is preallocated for this purpose. - const uint8_t *src = (const uint8_t*)frame; + uint8_t *dst = (uint8_t*)gl->conv_buffer; // This buffer is preallocated for this purpose. + const uint8_t *src = (const uint8_t*)frame; - for (unsigned h = 0; h < height; h++, src += pitch, dst += line_bytes) - memcpy(dst, src, line_bytes); + for (unsigned h = 0; h < height; h++, src += pitch, dst += line_bytes) + memcpy(dst, src, line_bytes); - glTexSubImage2D(GL_TEXTURE_2D, - 0, 0, 0, width, height, gl->texture_type, - gl->texture_fmt, gl->conv_buffer); + glTexSubImage2D(GL_TEXTURE_2D, + 0, 0, 0, width, height, gl->texture_type, + gl->texture_fmt, gl->conv_buffer); + } } } #else @@ -1257,6 +1292,8 @@ static void gl_free(void *data) glDeleteBuffers(1, &gl->pbo); #endif + scaler_ctx_gen_reset(&gl->scaler); + #if !defined(HAVE_OPENGLES) && defined(HAVE_FFMPEG) if (gl->pbo_readback_enable) { @@ -1302,11 +1339,15 @@ static bool resolve_extensions(gl_t *gl) gl->border_type = GL_CLAMP_TO_BORDER; #endif + driver.gfx_use_rgba = false; #ifdef HAVE_OPENGLES2 - if (!gl_query_extension("BGRA8888")) + if (gl_query_extension("BGRA8888")) + RARCH_LOG("[GL]: BGRA8888 extension found for GLES.\n"); + else { - RARCH_ERR("[GL]: GLES implementation does not have BGRA8888 extension.\n"); - return false; + driver.gfx_use_rgba = true; + RARCH_WARN("[GL]: GLES implementation does not have BGRA8888 extension.\n" + "32-bit path will require conversion.\n"); } #endif @@ -1320,16 +1361,27 @@ static bool resolve_extensions(gl_t *gl) return true; } +static inline void gl_set_texture_fmts(gl_t *gl, bool rgb32) +{ + gl->internal_fmt = rgb32 ? RARCH_GL_INTERNAL_FORMAT32 : RARCH_GL_INTERNAL_FORMAT16; + gl->texture_type = rgb32 ? RARCH_GL_TEXTURE_TYPE32 : RARCH_GL_TEXTURE_TYPE16; + gl->texture_fmt = rgb32 ? RARCH_GL_FORMAT32 : RARCH_GL_FORMAT16; + gl->base_size = rgb32 ? sizeof(uint32_t) : sizeof(uint16_t); + + if (driver.gfx_use_rgba && rgb32) + { + gl->internal_fmt = GL_RGBA; + gl->texture_type = GL_RGBA; + } +} + static inline void gl_reinit_textures(gl_t *gl, const video_info_t *video) { unsigned old_base_size = gl->base_size; unsigned old_width = gl->tex_w; unsigned old_height = gl->tex_h; - gl->internal_fmt = video->rgb32 ? RARCH_GL_INTERNAL_FORMAT32 : RARCH_GL_INTERNAL_FORMAT16; - gl->texture_type = video->rgb32 ? RARCH_GL_TEXTURE_TYPE32 : RARCH_GL_TEXTURE_TYPE16; - gl->texture_fmt = video->rgb32 ? RARCH_GL_FORMAT32 : RARCH_GL_FORMAT16; - gl->base_size = video->rgb32 ? sizeof(uint32_t) : sizeof(uint16_t); + gl_set_texture_fmts(gl, video->rgb32); gl->tex_w = gl->tex_h = RARCH_SCALE_BASE * video->input_scale; gl->empty_buf = realloc(gl->empty_buf, sizeof(uint32_t) * gl->tex_w * gl->tex_h); @@ -1526,10 +1578,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo else gl->tex_filter = video->smooth ? GL_LINEAR : GL_NEAREST; - gl->internal_fmt = video->rgb32 ? RARCH_GL_INTERNAL_FORMAT32 : RARCH_GL_INTERNAL_FORMAT16; - gl->texture_type = video->rgb32 ? RARCH_GL_TEXTURE_TYPE32 : RARCH_GL_TEXTURE_TYPE16; - gl->texture_fmt = video->rgb32 ? RARCH_GL_FORMAT32 : RARCH_GL_FORMAT16; - gl->base_size = video->rgb32 ? sizeof(uint32_t) : sizeof(uint16_t); + gl_set_texture_fmts(gl, video->rgb32); #ifndef HAVE_OPENGLES glEnable(GL_TEXTURE_2D); @@ -1816,8 +1865,8 @@ static bool gl_overlay_load(void *data, const uint32_t *image, unsigned width, u glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(width * sizeof(uint32_t))); - glTexImage2D(GL_TEXTURE_2D, 0, RARCH_GL_INTERNAL_FORMAT32, - width, height, 0, RARCH_GL_TEXTURE_TYPE32, + glTexImage2D(GL_TEXTURE_2D, 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_INTERNAL_FORMAT32, + width, height, 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_TEXTURE_TYPE32, RARCH_GL_FORMAT32, image); gl_overlay_tex_geom(gl, 0, 0, 1, 1); // Default. Stretch to whole screen. diff --git a/gfx/gl_common.h b/gfx/gl_common.h index d23dee2bcb..0e1636e3ff 100644 --- a/gfx/gl_common.h +++ b/gfx/gl_common.h @@ -260,6 +260,7 @@ typedef struct gl struct gl_coords coords; GLuint pbo; + GLenum internal_fmt; GLenum texture_type; // RGB565 or ARGB GLenum texture_fmt; diff --git a/gfx/image.c b/gfx/image.c index fcfd584028..e4832a813b 100644 --- a/gfx/image.c +++ b/gfx/image.c @@ -28,7 +28,8 @@ #ifdef HAVE_SDL_IMAGE #include "SDL_image.h" -bool texture_image_load(const char *path, struct texture_image *out_img) +bool texture_image_load_argb_shift(const char *path, struct texture_image *out_img, + unsigned a_shift, unsigned r_shift, unsigned g_shift, unsigned b_shift) { SDL_Surface *img = IMG_Load(path); if (!img) @@ -61,7 +62,7 @@ bool texture_image_load(const char *path, struct texture_image *out_img) uint32_t g = (src[x] & fmt->Gmask) >> fmt->Gshift; uint32_t b = (src[x] & fmt->Bmask) >> fmt->Bshift; uint32_t a = (src[x] & fmt->Amask) >> fmt->Ashift; - dst[x] = (a << 24) | (r << 16) | (g << 8) | b; + dst[x] = (a << a_shift) | (r << r_shift) | (g << g_shift) | (b << b_shift); } } } @@ -82,7 +83,7 @@ bool texture_image_load(const char *path, struct texture_image *out_img) uint32_t r = (color & fmt->Rmask) >> fmt->Rshift; uint32_t g = (color & fmt->Gmask) >> fmt->Gshift; uint32_t b = (color & fmt->Bmask) >> fmt->Bshift; - dst[x] = (0xff << 24) | (r << 16) | (g << 8) | b; + dst[x] = (0xff << a_shift) | (r << r_shift) | (g << g_shift) | (b << b_shift); } } } @@ -100,7 +101,8 @@ bool texture_image_load(const char *path, struct texture_image *out_img) #else -bool texture_image_load(const char *path, struct texture_image *out_img) +bool texture_image_load_argb_shift(const char *path, struct texture_image *out_img, + unsigned a_shift, unsigned r_shift, unsigned g_shift, unsigned b_shift) { // TODO: Check more gracefully. if (!strstr(path, ".tga")) @@ -159,7 +161,7 @@ bool texture_image_load(const char *path, struct texture_image *out_img) uint32_t r = tmp[i * 4 + 2]; uint32_t a = tmp[i * 4 + 3]; - out_img->pixels[i] = (a << 24) | (r << 16) | (g << 8) | b; + out_img->pixels[i] = (0xff << a_shift) | (r << r_shift) | (g << g_shift) | (b << b_shift); } } else if (bits == 24) @@ -171,7 +173,7 @@ bool texture_image_load(const char *path, struct texture_image *out_img) uint32_t r = tmp[i * 3 + 2]; uint32_t a = 0xff; - out_img->pixels[i] = (a << 24) | (r << 16) | (g << 8) | b; + out_img->pixels[i] = (a << a_shift) | (r << r_shift) | (g << g_shift) | (b << b_shift); } } else @@ -188,3 +190,13 @@ bool texture_image_load(const char *path, struct texture_image *out_img) } #endif + +bool texture_image_load(const char *path, struct texture_image *out_img) +{ + // This interface "leak" is very ugly. FIXME: Fix this properly ... + if (driver.gfx_use_rgba) + return texture_image_load_argb_shift(path, out_img, 24, 0, 8, 16); + else + return texture_image_load_argb_shift(path, out_img, 24, 16, 8, 0); +} + diff --git a/gfx/scaler/pixconv.c b/gfx/scaler/pixconv.c index 54d9b18206..855221e37a 100644 --- a/gfx/scaler/pixconv.c +++ b/gfx/scaler/pixconv.c @@ -645,6 +645,23 @@ void conv_argb8888_bgr24(void *output_, const void *input_, } #endif +void conv_argb8888_abgr8888(void *output_, const void *input_, + int width, int height, + int out_stride, int in_stride) +{ + const uint32_t *input = (const uint32_t*)input_; + uint32_t *output = (uint32_t*)output_; + + for (int h = 0; h < height; h++, output += out_stride >> 2, input += in_stride >> 2) + { + for (int w = 0; w < width; w++) + { + uint32_t col = input[w]; + output[w] = ((col << 16) & 0xff0000) | ((col >> 16) & 0xff) | (col & 0xff00ff00); + } + } +} + void conv_copy(void *output_, const void *input_, int width, int height, int out_stride, int in_stride) diff --git a/gfx/scaler/pixconv.h b/gfx/scaler/pixconv.h index fe7ee1e811..4587189572 100644 --- a/gfx/scaler/pixconv.h +++ b/gfx/scaler/pixconv.h @@ -48,6 +48,10 @@ void conv_argb8888_bgr24(void *output, const void *input, int width, int height, int out_stride, int in_stride); +void conv_argb8888_abgr8888(void *output, const void *input, + int width, int height, + int out_stride, int in_stride); + void conv_0rgb1555_bgr24(void *output, const void *input, int width, int height, int out_stride, int in_stride); diff --git a/gfx/scaler/scaler.c b/gfx/scaler/scaler.c index e7b2bbf376..3fa9cb3b77 100644 --- a/gfx/scaler/scaler.c +++ b/gfx/scaler/scaler.c @@ -86,6 +86,8 @@ static bool set_direct_pix_conv(struct scaler_ctx *ctx) ctx->direct_pixconv = conv_0rgb1555_bgr24; else if (ctx->in_fmt == SCALER_FMT_RGB565 && ctx->out_fmt == SCALER_FMT_BGR24) ctx->direct_pixconv = conv_rgb565_bgr24; + else if (ctx->in_fmt == SCALER_FMT_ARGB8888 && ctx->out_fmt == SCALER_FMT_ABGR8888) + ctx->direct_pixconv = conv_argb8888_abgr8888; else return false; diff --git a/gfx/scaler/scaler.h b/gfx/scaler/scaler.h index 371cbf1c07..86f420c131 100644 --- a/gfx/scaler/scaler.h +++ b/gfx/scaler/scaler.h @@ -25,6 +25,7 @@ enum scaler_pix_fmt { SCALER_FMT_ARGB8888 = 0, + SCALER_FMT_ABGR8888, SCALER_FMT_0RGB1555, SCALER_FMT_RGB565, SCALER_FMT_BGR24 diff --git a/gfx/shader_cg.c b/gfx/shader_cg.c index 2ebbadc0f9..a80c1adec2 100644 --- a/gfx/shader_cg.c +++ b/gfx/shader_cg.c @@ -505,13 +505,19 @@ static bool load_menu_shader(void) #define print_buf(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__) +#ifdef HAVE_OPENGLES2 +#define BORDER_FUNC GL_CLAMP_TO_EDGE +#else +#define BORDER_FUNC GL_CLAMP_TO_BORDER +#endif + static void load_texture_data(GLuint *obj, const struct texture_image *img, bool smooth) { glGenTextures(1, obj); glBindTexture(GL_TEXTURE_2D, *obj); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, BORDER_FUNC); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, BORDER_FUNC); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, smooth ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, smooth ? GL_LINEAR : GL_NEAREST); @@ -519,8 +525,8 @@ static void load_texture_data(GLuint *obj, const struct texture_image *img, bool glPixelStorei(GL_UNPACK_ALIGNMENT, 4); #endif glTexImage2D(GL_TEXTURE_2D, - 0, RARCH_GL_INTERNAL_FORMAT32, img->width, img->height, - 0, RARCH_GL_TEXTURE_TYPE32, RARCH_GL_FORMAT32, img->pixels); + 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_INTERNAL_FORMAT32, img->width, img->height, + 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_TEXTURE_TYPE32, RARCH_GL_FORMAT32, img->pixels); free(img->pixels); } @@ -562,6 +568,7 @@ static bool load_textures(const char *cgp_path, config_file_t *conf) fill_pathname_resolve_relative(image_path, cgp_path, path, sizeof(image_path)); RARCH_LOG("Loading image from: \"%s\".\n", image_path); + struct texture_image img; if (!texture_image_load(image_path, &img)) { diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index c5f9b7f839..0f19f18c08 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -481,6 +481,7 @@ static bool get_texture_image(const char *shader_path, xmlNodePtr ptr) fill_pathname_resolve_relative(tex_path, shader_path, (const char*)filename, sizeof(tex_path)); RARCH_LOG("Loading texture image from: \"%s\" ...\n", tex_path); + if (!texture_image_load(tex_path, &img)) { RARCH_ERR("Failed to load texture image from: \"%s\"\n", tex_path); @@ -501,8 +502,9 @@ static bool get_texture_image(const char *shader_path, xmlNodePtr ptr) glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glTexImage2D(GL_TEXTURE_2D, - 0, RARCH_GL_INTERNAL_FORMAT32, - img.width, img.height, 0, RARCH_GL_TEXTURE_TYPE32, RARCH_GL_FORMAT32, img.pixels); + 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_INTERNAL_FORMAT32, + img.width, img.height, 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_TEXTURE_TYPE32, + RARCH_GL_FORMAT32, img.pixels); pglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0);