2018-11-16 00:42:29 +01:00
|
|
|
/*****************************************************************************\
|
|
|
|
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
|
|
|
|
This file is licensed under the Snes9x License.
|
|
|
|
For further information, consult the LICENSE file in the root directory.
|
|
|
|
\*****************************************************************************/
|
|
|
|
|
2018-11-07 01:46:44 +01:00
|
|
|
#include "gtk_2_3_compat.h"
|
2010-09-25 17:46:12 +02:00
|
|
|
#include <dlfcn.h>
|
2010-09-26 11:19:15 +02:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
2010-09-25 17:46:12 +02:00
|
|
|
#include <sys/time.h>
|
|
|
|
|
|
|
|
#include "gtk_display.h"
|
|
|
|
#include "gtk_display_driver_opengl.h"
|
2018-05-24 19:18:59 +02:00
|
|
|
#include "gtk_shader_parameters.h"
|
2018-05-11 22:56:58 +02:00
|
|
|
#include "shaders/shader_helpers.h"
|
2018-05-08 23:56:18 +02:00
|
|
|
|
2018-10-28 17:22:00 +01:00
|
|
|
static const GLchar *stock_vertex_shader_110 =
|
|
|
|
"#version 110\n"
|
|
|
|
|
|
|
|
"attribute vec2 in_position;\n"
|
|
|
|
"attribute vec2 in_texcoord;\n"
|
|
|
|
"varying vec2 texcoord;\n"
|
|
|
|
|
|
|
|
"void main()\n"
|
|
|
|
"{\n"
|
|
|
|
" gl_Position = vec4 (in_position, 0.0, 1.0);\n"
|
|
|
|
" texcoord = in_texcoord;\n"
|
|
|
|
"}\n";
|
|
|
|
|
|
|
|
static const GLchar *stock_vertex_shader_140 =
|
|
|
|
"#version 140\n"
|
2018-10-28 01:13:51 +02:00
|
|
|
|
|
|
|
"in vec2 in_position;\n"
|
|
|
|
"in vec2 in_texcoord;\n"
|
|
|
|
"out vec2 texcoord;\n"
|
|
|
|
|
|
|
|
"void main()\n"
|
|
|
|
"{\n"
|
|
|
|
" gl_Position = vec4 (in_position, 0.0, 1.0);\n"
|
|
|
|
" texcoord = in_texcoord;\n"
|
|
|
|
"}\n";
|
|
|
|
|
2018-10-28 17:22:00 +01:00
|
|
|
static const GLchar *stock_fragment_shader_110 =
|
|
|
|
"#version 110\n"
|
|
|
|
|
|
|
|
"uniform sampler2D texmap;\n"
|
|
|
|
"varying vec2 texcoord;\n"
|
|
|
|
|
|
|
|
"void main()\n"
|
|
|
|
"{\n"
|
|
|
|
" gl_FragColor = texture2D(texmap, texcoord);\n"
|
|
|
|
"}\n";
|
2018-10-28 01:13:51 +02:00
|
|
|
|
2018-10-28 17:22:00 +01:00
|
|
|
static const GLchar *stock_fragment_shader_140 =
|
|
|
|
"#version 140\n"
|
2018-10-28 01:13:51 +02:00
|
|
|
|
|
|
|
"uniform sampler2D texmap;\n"
|
|
|
|
"out vec4 fragcolor;\n"
|
|
|
|
"in vec2 texcoord;\n"
|
|
|
|
|
|
|
|
"void main()\n"
|
|
|
|
"{\n"
|
2018-11-29 23:09:18 +01:00
|
|
|
" fragcolor = texture(texmap, texcoord);\n"
|
2018-10-28 01:13:51 +02:00
|
|
|
"}\n";
|
|
|
|
|
|
|
|
|
|
|
|
static GLfloat coords[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
|
|
|
0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, };
|
|
|
|
|
2018-05-21 21:15:31 +02:00
|
|
|
static void S9xViewportCallback (int src_width, int src_height,
|
|
|
|
int viewport_x, int viewport_y,
|
|
|
|
int viewport_width, int viewport_height,
|
|
|
|
int *out_x, int *out_y,
|
|
|
|
int *out_width, int *out_height)
|
|
|
|
{
|
|
|
|
|
|
|
|
S9xApplyAspect (src_width, src_height, viewport_width, viewport_height);
|
|
|
|
*out_x = src_width + viewport_x;
|
|
|
|
*out_y = src_height + viewport_y;
|
|
|
|
*out_width = viewport_width;
|
|
|
|
*out_height = viewport_height;
|
|
|
|
}
|
|
|
|
|
2010-09-25 17:46:12 +02:00
|
|
|
S9xOpenGLDisplayDriver::S9xOpenGLDisplayDriver (Snes9xWindow *window,
|
|
|
|
Snes9xConfig *config)
|
|
|
|
{
|
|
|
|
this->window = window;
|
|
|
|
this->config = config;
|
|
|
|
this->drawing_area = GTK_WIDGET (window->drawing_area);
|
2018-12-04 01:21:09 +01:00
|
|
|
fence = NULL;
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
void S9xOpenGLDisplayDriver::update (int width, int height, int yoffset)
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
|
|
|
uint8 *final_buffer = NULL;
|
|
|
|
int final_pitch;
|
2018-10-28 01:13:51 +02:00
|
|
|
void *pbo_map = NULL;
|
2010-09-26 11:19:15 +02:00
|
|
|
int x, y, w, h;
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2010-09-26 11:19:15 +02:00
|
|
|
GtkAllocation allocation;
|
|
|
|
gtk_widget_get_allocation (drawing_area, &allocation);
|
|
|
|
|
2018-05-05 21:14:06 +02:00
|
|
|
if (output_window_width != allocation.width ||
|
|
|
|
output_window_height != allocation.height)
|
|
|
|
{
|
2018-10-27 01:22:51 +02:00
|
|
|
resize ();
|
2018-05-05 21:14:06 +02:00
|
|
|
}
|
|
|
|
|
2016-10-03 02:41:42 +02:00
|
|
|
#if GTK_CHECK_VERSION(3,10,0)
|
|
|
|
int gdk_scale_factor = gdk_window_get_scale_factor (gdk_window);
|
|
|
|
|
2018-10-20 23:47:43 +02:00
|
|
|
allocation.width *= gdk_scale_factor;
|
|
|
|
allocation.height *= gdk_scale_factor;
|
2016-10-03 02:41:42 +02:00
|
|
|
#endif
|
|
|
|
|
2018-11-09 22:46:13 +01:00
|
|
|
if (!legacy)
|
|
|
|
glActiveTexture (GL_TEXTURE0);
|
2018-10-27 23:16:41 +02:00
|
|
|
glBindTexture (GL_TEXTURE_2D, texmap);
|
2018-05-14 00:31:25 +02:00
|
|
|
GLint filter = Settings.BilinearFilter ? GL_LINEAR : GL_NEAREST;
|
2018-05-12 22:07:07 +02:00
|
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
|
|
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
|
2018-10-27 23:16:41 +02:00
|
|
|
GLint clamp = (using_glsl_shaders || !npot) ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE;
|
2018-05-12 22:07:07 +02:00
|
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp);
|
|
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
glClear (GL_COLOR_BUFFER_BIT);
|
|
|
|
|
|
|
|
if (config->scale_method > 0)
|
|
|
|
{
|
|
|
|
uint8 *src_buffer = (uint8 *) padded_buffer[0];
|
|
|
|
int src_pitch = image_width * image_bpp;
|
|
|
|
uint8 *dst_buffer;
|
|
|
|
int dst_pitch;
|
|
|
|
|
2018-05-05 00:12:22 +02:00
|
|
|
src_buffer += (src_pitch * yoffset);
|
|
|
|
|
2010-09-25 17:46:12 +02:00
|
|
|
dst_buffer = (uint8 *) padded_buffer[1];
|
|
|
|
dst_pitch = scaled_max_width * image_bpp;
|
|
|
|
final_buffer = (uint8 *) padded_buffer[1];
|
|
|
|
final_pitch = scaled_max_width * image_bpp;
|
|
|
|
|
|
|
|
S9xFilter (src_buffer,
|
|
|
|
src_pitch,
|
|
|
|
dst_buffer,
|
|
|
|
dst_pitch,
|
|
|
|
width,
|
|
|
|
height);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
final_buffer = (uint8 *) padded_buffer[0];
|
|
|
|
final_pitch = image_width * image_bpp;
|
2018-05-05 00:12:22 +02:00
|
|
|
final_buffer += (final_pitch * yoffset);
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
|
2018-10-28 01:13:51 +02:00
|
|
|
x = width;
|
|
|
|
y = height;
|
|
|
|
w = allocation.width;
|
|
|
|
h = allocation.height;
|
2010-09-26 11:19:15 +02:00
|
|
|
S9xApplyAspect (x, y, w, h);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2010-11-18 13:42:55 +01:00
|
|
|
glViewport (x, allocation.height - y - h, w, h);
|
2010-09-26 11:19:15 +02:00
|
|
|
window->set_mouseable_area (x, y, w, h);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2010-09-26 11:19:15 +02:00
|
|
|
update_texture_size (width, height);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2010-09-26 11:19:15 +02:00
|
|
|
if (using_pbos)
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
2018-10-27 22:33:55 +02:00
|
|
|
if (config->pbo_format == 16)
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
|
|
|
|
2010-09-26 11:19:15 +02:00
|
|
|
glBindBuffer (GL_PIXEL_UNPACK_BUFFER, pbo);
|
|
|
|
glBufferData (GL_PIXEL_UNPACK_BUFFER,
|
|
|
|
width * height * 2,
|
|
|
|
NULL,
|
|
|
|
GL_STREAM_DRAW);
|
2019-01-17 17:41:27 +01:00
|
|
|
|
|
|
|
if (version >= 30)
|
2019-01-18 00:08:38 +01:00
|
|
|
pbo_map = glMapBufferRange (
|
|
|
|
GL_PIXEL_UNPACK_BUFFER, 0, width * height * 2,
|
|
|
|
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT |
|
|
|
|
GL_MAP_UNSYNCHRONIZED_BIT);
|
|
|
|
else
|
|
|
|
pbo_map = glMapBuffer (GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2010-09-26 11:19:15 +02:00
|
|
|
for (int y = 0; y < height; y++)
|
|
|
|
{
|
2018-10-28 01:13:51 +02:00
|
|
|
memcpy ((uint8 *) pbo_map + (width * y * 2),
|
2010-09-26 11:19:15 +02:00
|
|
|
final_buffer + (y * final_pitch),
|
|
|
|
width * image_bpp);
|
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2010-09-26 11:19:15 +02:00
|
|
|
glUnmapBuffer (GL_PIXEL_UNPACK_BUFFER);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2010-09-26 11:19:15 +02:00
|
|
|
glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
|
2018-05-12 22:07:07 +02:00
|
|
|
glTexSubImage2D (GL_TEXTURE_2D,
|
2010-09-26 11:19:15 +02:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
width,
|
|
|
|
height,
|
2018-05-04 21:28:50 +02:00
|
|
|
GL_RGB,
|
|
|
|
GL_UNSIGNED_SHORT_5_6_5,
|
2010-09-26 11:19:15 +02:00
|
|
|
BUFFER_OFFSET (0));
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2010-09-26 11:19:15 +02:00
|
|
|
glBindBuffer (GL_PIXEL_UNPACK_BUFFER, 0);
|
|
|
|
}
|
2018-10-27 22:33:55 +02:00
|
|
|
else /* 32-bit color */
|
2010-09-26 11:19:15 +02:00
|
|
|
{
|
|
|
|
glBindBuffer (GL_PIXEL_UNPACK_BUFFER, pbo);
|
|
|
|
glBufferData (GL_PIXEL_UNPACK_BUFFER,
|
|
|
|
width * height * 4,
|
|
|
|
NULL,
|
|
|
|
GL_STREAM_DRAW);
|
2019-01-17 17:41:27 +01:00
|
|
|
|
|
|
|
if (version >= 30)
|
2019-01-18 00:08:38 +01:00
|
|
|
pbo_map = glMapBufferRange (
|
|
|
|
GL_PIXEL_UNPACK_BUFFER, 0, width * height * 4,
|
|
|
|
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT |
|
|
|
|
GL_MAP_UNSYNCHRONIZED_BIT);
|
|
|
|
else
|
|
|
|
pbo_map = glMapBuffer (GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
2010-09-26 11:19:15 +02:00
|
|
|
|
|
|
|
/* Pixel swizzling in software */
|
2018-05-04 21:28:50 +02:00
|
|
|
S9xSetEndianess (ENDIAN_NORMAL);
|
2010-09-26 11:19:15 +02:00
|
|
|
S9xConvert (final_buffer,
|
2018-10-28 01:13:51 +02:00
|
|
|
pbo_map,
|
2010-09-26 11:19:15 +02:00
|
|
|
final_pitch,
|
|
|
|
width * 4,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
32);
|
|
|
|
|
|
|
|
glUnmapBuffer (GL_PIXEL_UNPACK_BUFFER);
|
|
|
|
|
|
|
|
glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
|
2018-05-12 22:07:07 +02:00
|
|
|
glTexSubImage2D (GL_TEXTURE_2D,
|
2010-09-25 17:46:12 +02:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
GL_BGRA,
|
2018-10-27 22:33:55 +02:00
|
|
|
GL_UNSIGNED_BYTE,
|
2010-09-26 11:19:15 +02:00
|
|
|
BUFFER_OFFSET (0));
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2010-09-26 11:19:15 +02:00
|
|
|
glBindBuffer (GL_PIXEL_UNPACK_BUFFER, 0);
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
2010-09-26 11:19:15 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glPixelStorei (GL_UNPACK_ROW_LENGTH, final_pitch / image_bpp);
|
2018-05-12 22:07:07 +02:00
|
|
|
glTexSubImage2D (GL_TEXTURE_2D,
|
2010-09-26 11:19:15 +02:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
width,
|
|
|
|
height,
|
2018-05-04 21:28:50 +02:00
|
|
|
GL_RGB,
|
|
|
|
GL_UNSIGNED_SHORT_5_6_5,
|
2010-09-26 11:19:15 +02:00
|
|
|
final_buffer);
|
|
|
|
}
|
|
|
|
|
2018-10-27 23:16:41 +02:00
|
|
|
if (using_glsl_shaders)
|
2018-05-11 01:47:55 +02:00
|
|
|
{
|
2018-05-21 21:15:31 +02:00
|
|
|
glsl_shader->render (texmap, width, height, x, allocation.height - y - h, w, h, S9xViewportCallback);
|
2018-10-27 01:22:51 +02:00
|
|
|
swap_buffers ();
|
2018-05-13 01:07:23 +02:00
|
|
|
return;
|
2018-05-11 01:47:55 +02:00
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2018-11-17 19:19:16 +01:00
|
|
|
glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
|
2010-09-26 11:19:15 +02:00
|
|
|
|
2018-10-27 01:22:51 +02:00
|
|
|
swap_buffers ();
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
void *S9xOpenGLDisplayDriver::get_parameters ()
|
2018-05-12 22:07:07 +02:00
|
|
|
{
|
|
|
|
if (using_glsl_shaders && glsl_shader)
|
|
|
|
{
|
|
|
|
return (void *) &glsl_shader->param;
|
|
|
|
}
|
2018-10-27 23:06:16 +02:00
|
|
|
|
2018-05-12 22:07:07 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
void S9xOpenGLDisplayDriver::save (const char *filename)
|
2018-05-12 23:22:27 +02:00
|
|
|
{
|
|
|
|
if (using_glsl_shaders && glsl_shader)
|
|
|
|
{
|
2018-05-13 18:02:52 +02:00
|
|
|
glsl_shader->save(filename);
|
2018-05-12 23:22:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
void S9xOpenGLDisplayDriver::clear_buffers ()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
|
|
|
memset (buffer[0], 0, image_padded_size);
|
|
|
|
memset (buffer[1], 0, scaled_padded_size);
|
|
|
|
|
2018-05-05 00:12:22 +02:00
|
|
|
glPixelStorei (GL_UNPACK_ROW_LENGTH, scaled_max_width);
|
2018-05-12 22:07:07 +02:00
|
|
|
glTexSubImage2D (GL_TEXTURE_2D,
|
2010-09-25 17:46:12 +02:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
2019-01-26 03:01:52 +01:00
|
|
|
MIN(scaled_max_width, texture_width),
|
|
|
|
MIN(scaled_max_height, texture_height),
|
2018-05-04 21:28:50 +02:00
|
|
|
GL_RGB,
|
|
|
|
GL_UNSIGNED_SHORT_5_6_5,
|
2018-05-05 00:12:22 +02:00
|
|
|
buffer[1]);
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
void S9xOpenGLDisplayDriver::update_texture_size (int width, int height)
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
|
|
|
if (width != texture_width || height != texture_height)
|
|
|
|
{
|
2018-10-27 22:56:22 +02:00
|
|
|
if (npot)
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
2018-05-12 22:07:07 +02:00
|
|
|
glBindTexture (GL_TEXTURE_2D, texmap);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2018-10-27 22:33:55 +02:00
|
|
|
if (using_pbos && config->pbo_format == 32)
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
2018-05-12 22:07:07 +02:00
|
|
|
glTexImage2D (GL_TEXTURE_2D,
|
2010-09-25 17:46:12 +02:00
|
|
|
0,
|
2018-11-30 22:35:19 +01:00
|
|
|
GL_RGBA,
|
2010-09-25 17:46:12 +02:00
|
|
|
width,
|
|
|
|
height,
|
|
|
|
0,
|
2018-10-27 22:33:55 +02:00
|
|
|
GL_BGRA,
|
|
|
|
GL_UNSIGNED_BYTE,
|
2010-09-25 17:46:12 +02:00
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-05-12 22:07:07 +02:00
|
|
|
glTexImage2D (GL_TEXTURE_2D,
|
2010-09-25 17:46:12 +02:00
|
|
|
0,
|
2018-05-04 21:28:50 +02:00
|
|
|
GL_RGB565,
|
2010-09-25 17:46:12 +02:00
|
|
|
width,
|
|
|
|
height,
|
|
|
|
0,
|
2018-05-04 21:28:50 +02:00
|
|
|
GL_RGB,
|
|
|
|
GL_UNSIGNED_SHORT_5_6_5,
|
2010-09-25 17:46:12 +02:00
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
2018-11-02 21:25:32 +01:00
|
|
|
coords[9] = 1.0f;
|
|
|
|
coords[10] = 1.0f;
|
|
|
|
coords[11] = 1.0f;
|
|
|
|
coords[14] = 1.0f;
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
2018-11-02 21:25:32 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
coords[9] = height / 1024.0f;
|
|
|
|
coords[10] = width / 1024.0f;
|
|
|
|
coords[11] = height / 1024.0f;
|
|
|
|
coords[14] = width / 1024.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
texture_width = width;
|
|
|
|
texture_height = height;
|
|
|
|
|
2018-11-09 22:47:38 +01:00
|
|
|
if (!legacy)
|
|
|
|
{
|
|
|
|
glBindBuffer (GL_ARRAY_BUFFER, coord_buffer);
|
|
|
|
glBufferData (GL_ARRAY_BUFFER, sizeof (GLfloat) * 16, coords, GL_STATIC_DRAW);
|
|
|
|
glBindBuffer (GL_ARRAY_BUFFER, 0);
|
|
|
|
}
|
2018-11-17 19:19:16 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
glVertexPointer (2, GL_FLOAT, 0, coords);
|
|
|
|
glTexCoordPointer (2, GL_FLOAT, 0, &coords[8]);
|
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-25 20:28:24 +01:00
|
|
|
bool S9xOpenGLDisplayDriver::load_shaders(const char *shader_file)
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
2019-01-25 20:28:24 +01:00
|
|
|
setlocale(LC_ALL, "C");
|
|
|
|
std::string filename(shader_file);
|
2018-05-11 01:47:55 +02:00
|
|
|
|
2019-01-25 20:28:24 +01:00
|
|
|
auto endswith = [&](std::string ext) -> bool {
|
|
|
|
return filename.rfind(ext) == filename.length() - ext.length();
|
|
|
|
};
|
2018-12-04 18:18:22 +01:00
|
|
|
|
2019-01-25 20:28:24 +01:00
|
|
|
if (endswith(".glslp") || endswith(".glsl")
|
|
|
|
#ifdef USE_SLANG
|
|
|
|
|| endswith(".slangp") || endswith(".slang")
|
|
|
|
#endif
|
|
|
|
)
|
2018-05-11 01:47:55 +02:00
|
|
|
{
|
2018-10-27 22:56:22 +02:00
|
|
|
glsl_shader = new GLSLShader;
|
2019-01-25 20:28:24 +01:00
|
|
|
if (glsl_shader->load_shader((char *)shader_file))
|
2018-05-11 01:47:55 +02:00
|
|
|
{
|
2018-10-27 22:56:22 +02:00
|
|
|
using_glsl_shaders = true;
|
|
|
|
npot = true;
|
2018-05-24 19:18:59 +02:00
|
|
|
|
2019-01-25 20:28:24 +01:00
|
|
|
if (glsl_shader->param.size() > 0)
|
|
|
|
window->enable_widget("shader_parameters_item", true);
|
2018-05-24 19:18:59 +02:00
|
|
|
|
2019-01-25 20:28:24 +01:00
|
|
|
setlocale(LC_ALL, "");
|
2018-12-28 23:32:32 +01:00
|
|
|
return true;
|
2018-05-11 01:47:55 +02:00
|
|
|
}
|
|
|
|
|
2018-10-27 22:56:22 +02:00
|
|
|
delete glsl_shader;
|
2010-10-22 04:32:47 +02:00
|
|
|
}
|
|
|
|
|
2019-01-25 20:28:24 +01:00
|
|
|
setlocale(LC_ALL, "");
|
2018-12-28 23:32:32 +01:00
|
|
|
return false;
|
2010-09-26 11:19:15 +02:00
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2018-12-28 23:32:32 +01:00
|
|
|
bool S9xOpenGLDisplayDriver::opengl_defaults()
|
2010-09-26 11:19:15 +02:00
|
|
|
{
|
2018-10-27 22:56:22 +02:00
|
|
|
npot = false;
|
|
|
|
using_pbos = false;
|
2018-05-12 22:07:07 +02:00
|
|
|
|
2010-09-25 17:46:12 +02:00
|
|
|
if (config->use_pbos)
|
|
|
|
{
|
2018-11-09 22:54:07 +01:00
|
|
|
if (version >= 15)
|
|
|
|
using_pbos = true;
|
|
|
|
else
|
|
|
|
config->use_pbos = false;
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
|
2018-10-27 22:56:22 +02:00
|
|
|
using_glsl_shaders = false;
|
2018-05-11 01:47:55 +02:00
|
|
|
glsl_shader = NULL;
|
|
|
|
|
2010-09-26 11:19:15 +02:00
|
|
|
if (config->use_shaders)
|
|
|
|
{
|
2018-12-29 01:36:23 +01:00
|
|
|
if (legacy || !load_shaders (config->shader_filename.c_str ()))
|
2010-09-26 11:19:15 +02:00
|
|
|
{
|
2018-10-27 22:56:22 +02:00
|
|
|
config->use_shaders = false;
|
2010-09-26 11:19:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-09 00:56:13 +02:00
|
|
|
texture_width = 1024;
|
|
|
|
texture_height = 1024;
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2018-05-12 22:07:07 +02:00
|
|
|
if (config->npot_textures)
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
2018-10-27 22:56:22 +02:00
|
|
|
npot = true;
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
|
2018-11-09 22:46:13 +01:00
|
|
|
if (legacy)
|
2018-10-28 17:22:00 +01:00
|
|
|
{
|
2018-11-09 22:46:13 +01:00
|
|
|
glEnableClientState (GL_VERTEX_ARRAY);
|
|
|
|
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glEnable (GL_TEXTURE_2D);
|
|
|
|
glMatrixMode (GL_PROJECTION);
|
|
|
|
glLoadIdentity ();
|
|
|
|
glMatrixMode (GL_MODELVIEW);
|
|
|
|
glLoadIdentity ();
|
2018-11-17 19:19:16 +01:00
|
|
|
|
|
|
|
glVertexPointer (2, GL_FLOAT, 0, coords);
|
|
|
|
glTexCoordPointer (2, GL_FLOAT, 0, &coords[8]);
|
2018-10-28 17:22:00 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-11-09 22:46:13 +01:00
|
|
|
stock_program = glCreateProgram ();
|
2018-10-28 17:22:00 +01:00
|
|
|
|
2018-11-09 22:46:13 +01:00
|
|
|
GLuint vertex_shader = glCreateShader (GL_VERTEX_SHADER);
|
|
|
|
GLuint fragment_shader = glCreateShader (GL_FRAGMENT_SHADER);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2018-11-09 22:46:13 +01:00
|
|
|
if (version < 30)
|
|
|
|
{
|
|
|
|
glShaderSource (vertex_shader, 1, &stock_vertex_shader_110, NULL);
|
|
|
|
glShaderSource (fragment_shader, 1, &stock_fragment_shader_110, NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glShaderSource (vertex_shader, 1, &stock_vertex_shader_140, NULL);
|
|
|
|
glShaderSource (fragment_shader, 1, &stock_fragment_shader_140, NULL);
|
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2018-11-09 22:46:13 +01:00
|
|
|
glCompileShader (vertex_shader);
|
|
|
|
glAttachShader (stock_program, vertex_shader);
|
|
|
|
glCompileShader (fragment_shader);
|
|
|
|
glAttachShader (stock_program, fragment_shader);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2018-11-09 22:46:13 +01:00
|
|
|
glBindAttribLocation (stock_program, 0, "in_position");
|
|
|
|
glBindAttribLocation (stock_program, 1, "in_texcoord");
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2018-11-09 22:46:13 +01:00
|
|
|
glLinkProgram (stock_program);
|
|
|
|
glUseProgram (stock_program);
|
2018-10-28 01:13:51 +02:00
|
|
|
|
2018-11-09 22:46:13 +01:00
|
|
|
glDeleteShader (vertex_shader);
|
|
|
|
glDeleteShader (fragment_shader);
|
|
|
|
|
|
|
|
GLint texture_uniform = glGetUniformLocation (stock_program, "texmap");
|
|
|
|
glUniform1i (texture_uniform, 0);
|
2018-10-28 01:13:51 +02:00
|
|
|
|
2018-11-09 22:46:13 +01:00
|
|
|
if (core)
|
|
|
|
{
|
|
|
|
GLuint vao;
|
|
|
|
glGenVertexArrays (1, &vao);
|
|
|
|
glBindVertexArray (vao);
|
|
|
|
}
|
|
|
|
|
|
|
|
glGenBuffers (1, &coord_buffer);
|
|
|
|
glBindBuffer (GL_ARRAY_BUFFER, coord_buffer);
|
|
|
|
glBufferData (GL_ARRAY_BUFFER, sizeof (GLfloat) * 16, coords, GL_STATIC_DRAW);
|
2018-11-17 19:19:16 +01:00
|
|
|
|
|
|
|
glEnableVertexAttribArray (0);
|
|
|
|
glVertexAttribPointer (0, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET (0));
|
|
|
|
glEnableVertexAttribArray (1);
|
|
|
|
glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET (32));
|
|
|
|
|
2018-11-09 22:46:13 +01:00
|
|
|
glBindBuffer (GL_ARRAY_BUFFER, 0);
|
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
if (config->use_pbos)
|
|
|
|
{
|
|
|
|
glGenBuffers (1, &pbo);
|
|
|
|
glGenTextures (1, &texmap);
|
|
|
|
|
2018-05-12 22:07:07 +02:00
|
|
|
glBindTexture (GL_TEXTURE_2D, texmap);
|
|
|
|
glTexImage2D (GL_TEXTURE_2D,
|
2010-09-25 17:46:12 +02:00
|
|
|
0,
|
2018-11-30 22:35:19 +01:00
|
|
|
config->pbo_format == 16 ? GL_RGB565 : GL_RGBA,
|
2010-09-25 17:46:12 +02:00
|
|
|
texture_width,
|
|
|
|
texture_height,
|
|
|
|
0,
|
2018-10-27 22:33:55 +02:00
|
|
|
config->pbo_format == 16 ? GL_RGB : GL_BGRA,
|
|
|
|
config->pbo_format == 16 ? GL_UNSIGNED_SHORT_5_6_5 : GL_UNSIGNED_BYTE,
|
2010-09-25 17:46:12 +02:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
glBindBuffer (GL_PIXEL_UNPACK_BUFFER, pbo);
|
|
|
|
glBufferData (GL_PIXEL_UNPACK_BUFFER,
|
|
|
|
texture_width * texture_height * 3,
|
|
|
|
NULL,
|
|
|
|
GL_STREAM_DRAW);
|
|
|
|
|
|
|
|
glBindBuffer (GL_PIXEL_UNPACK_BUFFER, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glGenTextures (1, &texmap);
|
|
|
|
|
2018-05-12 22:07:07 +02:00
|
|
|
glBindTexture (GL_TEXTURE_2D, texmap);
|
|
|
|
glTexImage2D (GL_TEXTURE_2D,
|
2010-09-25 17:46:12 +02:00
|
|
|
0,
|
2018-05-04 21:28:50 +02:00
|
|
|
GL_RGB565,
|
2010-09-25 17:46:12 +02:00
|
|
|
texture_width,
|
|
|
|
texture_height,
|
|
|
|
0,
|
2018-05-04 21:28:50 +02:00
|
|
|
GL_RGB,
|
|
|
|
GL_UNSIGNED_SHORT_5_6_5,
|
2010-09-25 17:46:12 +02:00
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
glClearColor (0.0, 0.0, 0.0, 0.0);
|
|
|
|
|
2018-12-28 23:32:32 +01:00
|
|
|
return true;
|
2010-09-26 11:19:15 +02:00
|
|
|
}
|
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
void S9xOpenGLDisplayDriver::refresh (int width, int height)
|
2010-09-26 11:19:15 +02:00
|
|
|
{
|
2018-10-29 22:41:41 +01:00
|
|
|
resize ();
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
void S9xOpenGLDisplayDriver::resize ()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
2018-10-27 01:22:51 +02:00
|
|
|
context->resize ();
|
|
|
|
context->swap_interval (config->sync_to_vblank);
|
|
|
|
output_window_width = context->width;
|
|
|
|
output_window_height = context->height;
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
|
2018-12-28 23:32:32 +01:00
|
|
|
bool S9xOpenGLDisplayDriver::create_context()
|
2010-09-26 11:19:15 +02:00
|
|
|
{
|
2018-10-27 01:22:51 +02:00
|
|
|
gdk_window = gtk_widget_get_window (drawing_area);
|
2010-09-26 11:19:15 +02:00
|
|
|
|
2018-10-27 01:22:51 +02:00
|
|
|
#ifdef GDK_WINDOWING_WAYLAND
|
|
|
|
if (GDK_IS_WAYLAND_WINDOW (gdk_window))
|
2018-10-20 23:47:43 +02:00
|
|
|
{
|
2018-10-27 01:22:51 +02:00
|
|
|
context = &wl;
|
2018-10-20 23:47:43 +02:00
|
|
|
}
|
2018-10-27 01:22:51 +02:00
|
|
|
#endif
|
|
|
|
#ifdef GDK_WINDOWING_X11
|
|
|
|
if (GDK_IS_X11_WINDOW (gdk_window))
|
2018-10-20 23:47:43 +02:00
|
|
|
{
|
2018-10-27 01:22:51 +02:00
|
|
|
context = &glx;
|
2010-09-26 11:19:15 +02:00
|
|
|
}
|
2018-10-27 01:22:51 +02:00
|
|
|
#endif
|
2010-09-26 11:19:15 +02:00
|
|
|
|
2018-10-27 01:22:51 +02:00
|
|
|
if (!context->attach (drawing_area))
|
2018-12-28 23:32:32 +01:00
|
|
|
return false;
|
2018-10-15 21:04:28 +02:00
|
|
|
|
2018-10-27 01:22:51 +02:00
|
|
|
if (!context->create_context ())
|
2018-12-28 23:32:32 +01:00
|
|
|
return false;
|
2018-10-27 01:22:51 +02:00
|
|
|
|
|
|
|
output_window_width = context->width;
|
|
|
|
output_window_height = context->height;
|
|
|
|
|
|
|
|
context->make_current ();
|
2010-09-26 11:19:15 +02:00
|
|
|
|
2018-11-09 22:46:13 +01:00
|
|
|
legacy = false;
|
2018-10-28 17:22:00 +01:00
|
|
|
version = epoxy_gl_version ();
|
2018-10-28 01:13:51 +02:00
|
|
|
if (version < 20)
|
2018-10-27 22:56:22 +02:00
|
|
|
{
|
2018-11-09 22:46:13 +01:00
|
|
|
printf ("OpenGL version is only %d.%d. Recommended version is 2.0.\n",
|
2018-10-28 01:13:51 +02:00
|
|
|
version / 10,
|
|
|
|
version % 10);
|
2018-11-09 22:46:13 +01:00
|
|
|
legacy = true;
|
2018-10-27 22:56:22 +02:00
|
|
|
}
|
|
|
|
|
2018-10-28 01:13:51 +02:00
|
|
|
int profile_mask = 0;
|
|
|
|
glGetIntegerv (GL_CONTEXT_PROFILE_MASK, &profile_mask);
|
|
|
|
if (profile_mask & GL_CONTEXT_CORE_PROFILE_BIT)
|
|
|
|
core = true;
|
|
|
|
else
|
|
|
|
core = false;
|
|
|
|
|
2018-12-04 00:28:22 +01:00
|
|
|
if (version >= 31 || epoxy_has_gl_extension ("GL_ARB_sync"))
|
|
|
|
fences = true;
|
|
|
|
|
2018-12-28 23:32:32 +01:00
|
|
|
return true;
|
2010-09-26 11:19:15 +02:00
|
|
|
}
|
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
int S9xOpenGLDisplayDriver::init ()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
2018-10-27 22:56:22 +02:00
|
|
|
initialized = false;
|
2010-09-26 11:19:15 +02:00
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
if (!create_context ())
|
2010-09-26 11:19:15 +02:00
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2010-09-26 11:19:15 +02:00
|
|
|
if (!opengl_defaults ())
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
buffer[0] = malloc (image_padded_size);
|
|
|
|
buffer[1] = malloc (scaled_padded_size);
|
|
|
|
|
|
|
|
clear_buffers ();
|
|
|
|
|
2018-05-05 00:12:22 +02:00
|
|
|
padded_buffer[0] = (void *) (((uint8 *) buffer[0]) + image_padded_offset);
|
|
|
|
padded_buffer[1] = (void *) (((uint8 *) buffer[1]) + scaled_padded_offset);
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
GFX.Screen = (uint16 *) padded_buffer[0];
|
|
|
|
GFX.Pitch = image_width * image_bpp;
|
|
|
|
|
2018-10-27 01:22:51 +02:00
|
|
|
context->swap_interval (config->sync_to_vblank);
|
2010-09-26 11:19:15 +02:00
|
|
|
|
2018-10-27 22:56:22 +02:00
|
|
|
initialized = true;
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
uint16 *S9xOpenGLDisplayDriver::get_next_buffer ()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
|
|
|
return (uint16 *) padded_buffer[0];
|
|
|
|
}
|
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
void S9xOpenGLDisplayDriver::push_buffer (uint16 *src)
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
|
|
|
memmove (padded_buffer[0], src, image_size);
|
|
|
|
}
|
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
uint16 *S9xOpenGLDisplayDriver::get_current_buffer ()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
|
|
|
return (uint16 *) padded_buffer[0];
|
|
|
|
}
|
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
void S9xOpenGLDisplayDriver::swap_buffers ()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
2018-10-27 01:22:51 +02:00
|
|
|
context->swap_buffers ();
|
2010-09-26 11:19:15 +02:00
|
|
|
|
2018-12-28 19:06:00 +01:00
|
|
|
if (config->sync_every_frame && !config->use_fences)
|
2010-09-26 11:19:15 +02:00
|
|
|
{
|
2018-12-28 19:06:00 +01:00
|
|
|
usleep (0);
|
|
|
|
glFinish ();
|
|
|
|
}
|
|
|
|
else if (config->use_fences && fences)
|
|
|
|
{
|
|
|
|
if (fence)
|
|
|
|
glDeleteSync (fence);
|
|
|
|
fence = glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
2010-09-26 11:19:15 +02:00
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
void S9xOpenGLDisplayDriver::deinit ()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
2010-09-26 11:19:15 +02:00
|
|
|
if (!initialized)
|
|
|
|
return;
|
|
|
|
|
2018-10-27 23:16:41 +02:00
|
|
|
if (using_glsl_shaders)
|
2018-05-11 01:47:55 +02:00
|
|
|
{
|
2018-12-28 23:32:32 +01:00
|
|
|
window->enable_widget ("shader_parameters_item", false);
|
2018-05-24 19:18:59 +02:00
|
|
|
gtk_shader_parameters_dialog_close ();
|
2018-05-11 01:47:55 +02:00
|
|
|
glsl_shader->destroy();
|
|
|
|
delete glsl_shader;
|
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
GFX.Screen = NULL;
|
|
|
|
|
|
|
|
padded_buffer[0] = NULL;
|
|
|
|
padded_buffer[1] = NULL;
|
|
|
|
|
|
|
|
free (buffer[0]);
|
|
|
|
free (buffer[1]);
|
|
|
|
|
|
|
|
if (using_pbos)
|
|
|
|
{
|
|
|
|
glBindBuffer (GL_PIXEL_UNPACK_BUFFER, 0);
|
|
|
|
glDeleteBuffers (1, &pbo);
|
|
|
|
}
|
|
|
|
|
|
|
|
glDeleteTextures (1, &texmap);
|
|
|
|
}
|
|
|
|
|
2018-10-27 23:06:16 +02:00
|
|
|
int S9xOpenGLDisplayDriver::query_availability ()
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
2018-10-20 23:47:43 +02:00
|
|
|
GdkDisplay *gdk_display = gdk_display_get_default ();
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2018-10-27 01:22:51 +02:00
|
|
|
#ifdef GDK_WINDOWING_WAYLAND
|
|
|
|
if (GDK_IS_WAYLAND_DISPLAY (gdk_display))
|
|
|
|
{
|
2018-10-22 00:05:37 +02:00
|
|
|
return 1;
|
2018-10-27 01:22:51 +02:00
|
|
|
}
|
|
|
|
#endif
|
2018-10-22 00:05:37 +02:00
|
|
|
|
2018-10-15 21:04:28 +02:00
|
|
|
#ifdef GDK_WINDOWING_X11
|
2018-10-20 23:47:43 +02:00
|
|
|
if (GDK_IS_X11_DISPLAY (gdk_display))
|
|
|
|
{
|
2018-11-07 01:04:10 +01:00
|
|
|
Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display);
|
2018-07-12 17:15:07 +02:00
|
|
|
|
2018-11-06 18:39:17 +01:00
|
|
|
if (glXQueryExtension (dpy, NULL, NULL) == True)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
2018-10-20 23:47:43 +02:00
|
|
|
}
|
2018-10-15 21:04:28 +02:00
|
|
|
#endif
|
2010-09-25 17:46:12 +02:00
|
|
|
|
2018-10-15 21:04:28 +02:00
|
|
|
if (gui_config->hw_accel == HWA_OPENGL)
|
|
|
|
gui_config->hw_accel = HWA_NONE;
|
|
|
|
|
|
|
|
return 0;
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
2018-12-04 01:21:09 +01:00
|
|
|
|
|
|
|
bool S9xOpenGLDisplayDriver::is_ready ()
|
|
|
|
{
|
|
|
|
if (!fence)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (glClientWaitSync (fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0) == GL_TIMEOUT_EXPIRED)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
glDeleteSync (fence);
|
|
|
|
fence = NULL;
|
2018-12-28 19:06:00 +01:00
|
|
|
|
2018-12-04 01:21:09 +01:00
|
|
|
return true;
|
|
|
|
}
|