Merge pull request #5320 from Cacodemon345/qt-openglrenderer-pcem

Port OpenGL 3.x renderer from PCem
This commit is contained in:
Miran Grča
2025-03-10 16:29:51 +01:00
committed by GitHub
31 changed files with 3307 additions and 1162 deletions

View File

@@ -172,7 +172,6 @@ int force_43 = 0; /* (C) video *
int video_filter_method = 1; /* (C) video */ int video_filter_method = 1; /* (C) video */
int video_vsync = 0; /* (C) video */ int video_vsync = 0; /* (C) video */
int video_framerate = -1; /* (C) video */ int video_framerate = -1; /* (C) video */
char video_shader[512] = { '\0' }; /* (C) video */
bool serial_passthrough_enabled[SERIAL_MAX] = { 0, 0, 0, 0, 0, 0, 0 }; /* (C) activation and kind of bool serial_passthrough_enabled[SERIAL_MAX] = { 0, 0, 0, 0, 0, 0, 0 }; /* (C) activation and kind of
pass-through for serial ports */ pass-through for serial ports */
int bugger_enabled = 0; /* (C) enable ISAbugger */ int bugger_enabled = 0; /* (C) enable ISAbugger */
@@ -252,6 +251,8 @@ int unscaled_size_y = SCREEN_RES_Y; /* current unscaled size Y */
int efscrnsz_y = SCREEN_RES_Y; int efscrnsz_y = SCREEN_RES_Y;
#endif #endif
__thread int is_cpu_thread = 0;
static wchar_t mouse_msg[3][200]; static wchar_t mouse_msg[3][200];
static volatile atomic_int do_pause_ack = 0; static volatile atomic_int do_pause_ack = 0;

View File

@@ -77,6 +77,10 @@
#include <86box/snd_opl.h> #include <86box/snd_opl.h>
#include <86box/version.h> #include <86box/version.h>
/* Deliberate to not make the 86box.h header kitchen-sink. */
#include <86box/qt-glsl.h>
extern char gl3_shader_file[MAX_USER_SHADERS][512];
static int cx; static int cx;
static int cy; static int cy;
static int cw; static int cw;
@@ -196,7 +200,6 @@ load_general(void)
video_framerate = ini_section_get_int(cat, "video_gl_framerate", -1); video_framerate = ini_section_get_int(cat, "video_gl_framerate", -1);
video_vsync = ini_section_get_int(cat, "video_gl_vsync", 0); video_vsync = ini_section_get_int(cat, "video_gl_vsync", 0);
strncpy(video_shader, ini_section_get_string(cat, "video_gl_shader", ""), sizeof(video_shader) - 1);
window_remember = ini_section_get_int(cat, "window_remember", 0); window_remember = ini_section_get_int(cat, "window_remember", 0);
if (window_remember) { if (window_remember) {
@@ -1714,6 +1717,46 @@ load_other_peripherals(void)
ini_section_delete_var(cat, temp); ini_section_delete_var(cat, temp);
} }
/* Load OpenGL 3.0 renderer options. */
static void
load_gl3_shaders(void)
{
ini_section_t cat = ini_find_section(config, "GL3 Shaders");
char *p;
char temp[512];
int i = 0, shaders = 0;
memset(temp, 0, sizeof(temp));
memset(gl3_shader_file, 0, sizeof(gl3_shader_file));
shaders = ini_section_get_int(cat, "shaders", 0);
if (shaders > MAX_USER_SHADERS)
shaders = MAX_USER_SHADERS;
if (shaders == 0) {
ini_section_t general = ini_find_section(config, "General");
if (general) {
p = ini_section_get_string(general, "video_gl_shader", NULL);
if (p) {
strncpy(gl3_shader_file[0], p, 512);
ini_delete_var(config, general, "video_gl_shader");
return;
}
}
}
for (i = 0; i < shaders; i++) {
temp[0] = 0;
snprintf(temp, 512, "shader%d", i);
p = ini_section_get_string(cat, temp, "");
if (p[0]) {
strncpy(gl3_shader_file[i], p, 512);
} else {
gl3_shader_file[i][0] = 0;
break;
}
}
}
/* Load the specified or a default configuration file. */ /* Load the specified or a default configuration file. */
void void
config_load(void) config_load(void)
@@ -1810,6 +1853,7 @@ config_load(void)
load_floppy_and_cdrom_drives(); /* Floppy and CD-ROM drives */ load_floppy_and_cdrom_drives(); /* Floppy and CD-ROM drives */
load_other_removable_devices(); /* Other removable devices */ load_other_removable_devices(); /* Other removable devices */
load_other_peripherals(); /* Other peripherals */ load_other_peripherals(); /* Other peripherals */
load_gl3_shaders(); /* GL3 Shaders */
/* Migrate renamed device configurations. */ /* Migrate renamed device configurations. */
c = ini_find_section(config, "MDA"); c = ini_find_section(config, "MDA");
@@ -2002,10 +2046,6 @@ save_general(void)
ini_section_set_int(cat, "video_gl_vsync", video_vsync); ini_section_set_int(cat, "video_gl_vsync", video_vsync);
else else
ini_section_delete_var(cat, "video_gl_vsync"); ini_section_delete_var(cat, "video_gl_vsync");
if (strlen(video_shader) > 0)
ini_section_set_string(cat, "video_gl_shader", video_shader);
else
ini_section_delete_var(cat, "video_gl_shader");
if (do_auto_pause) if (do_auto_pause)
ini_section_set_int(cat, "do_auto_pause", do_auto_pause); ini_section_set_int(cat, "do_auto_pause", do_auto_pause);
@@ -2632,6 +2672,38 @@ save_other_peripherals(void)
ini_delete_section_if_empty(config, cat); ini_delete_section_if_empty(config, cat);
} }
/* Save "GL3 Shaders" section. */
static void
save_gl3_shaders(void)
{
ini_section_t cat = ini_find_or_create_section(config, "GL3 Shaders");
char temp[512];
int shaders = 0, i = 0;
for (i = 0; i < MAX_USER_SHADERS; i++) {
if (gl3_shader_file[i][0] == 0) {
temp[0] = 0;
snprintf(temp, 512, "shader%d", i);
ini_section_delete_var(cat, temp);
break;
}
shaders++;
}
ini_section_set_int(cat, "shaders", shaders);
if (shaders == 0) {
ini_section_delete_var(cat, "shaders");
} else {
for (i = 0; i < shaders; i++) {
temp[0] = 0;
snprintf(temp, 512, "shader%d", i);
ini_section_set_string(cat, temp, gl3_shader_file[i]);
}
}
ini_delete_section_if_empty(config, cat);
}
/* Save "Hard Disks" section. */ /* Save "Hard Disks" section. */
static void static void
save_hard_disks(void) save_hard_disks(void)
@@ -2987,6 +3059,7 @@ config_save(void)
save_floppy_and_cdrom_drives(); /* Floppy and CD-ROM drives */ save_floppy_and_cdrom_drives(); /* Floppy and CD-ROM drives */
save_other_removable_devices(); /* Other removable devices */ save_other_removable_devices(); /* Other removable devices */
save_other_peripherals(); /* Other peripherals */ save_other_peripherals(); /* Other peripherals */
save_gl3_shaders(); /* GL3 Shaders */
ini_write(config, cfg_path); ini_write(config, cfg_path);
} }

View File

@@ -126,7 +126,6 @@ extern int video_filter_method; /* (C) video */
extern int video_vsync; /* (C) video */ extern int video_vsync; /* (C) video */
extern int video_framerate; /* (C) video */ extern int video_framerate; /* (C) video */
extern int gfxcard[GFXCARD_MAX]; /* (C) graphics/video card */ extern int gfxcard[GFXCARD_MAX]; /* (C) graphics/video card */
extern char video_shader[512]; /* (C) video */
extern int bugger_enabled; /* (C) enable ISAbugger */ extern int bugger_enabled; /* (C) enable ISAbugger */
extern int novell_keycard_enabled; /* (C) enable Novell NetWare 2.x key card emulation. */ extern int novell_keycard_enabled; /* (C) enable Novell NetWare 2.x key card emulation. */
extern int postcard_enabled; /* (C) enable POST card */ extern int postcard_enabled; /* (C) enable POST card */
@@ -188,6 +187,8 @@ extern FILE *stdlog; /* file to log output to */
#endif #endif
extern int config_changed; /* config has changed */ extern int config_changed; /* config has changed */
extern __thread int is_cpu_thread; /* Is this the CPU thread? */
/* Function prototypes. */ /* Function prototypes. */
#ifdef HAVE_STDARG_H #ifdef HAVE_STDARG_H
extern void pclog_ex(const char *fmt, va_list ap); extern void pclog_ex(const char *fmt, va_list ap);

View File

@@ -31,6 +31,7 @@ typedef void *ini_section_t;
extern ini_t ini_new(void); extern ini_t ini_new(void);
extern ini_t ini_read(const char *fn); extern ini_t ini_read(const char *fn);
extern void ini_strip_quotes(ini_t ini);
extern void ini_write(ini_t ini, const char *fn); extern void ini_write(ini_t ini, const char *fn);
extern void ini_dump(ini_t ini); extern void ini_dump(ini_t ini);
extern void ini_close(ini_t ini); extern void ini_close(ini_t ini);
@@ -58,6 +59,7 @@ extern void ini_section_set_hex20(ini_section_t section, const char *name, i
extern void ini_section_set_mac(ini_section_t section, const char *name, int val); extern void ini_section_set_mac(ini_section_t section, const char *name, int val);
extern void ini_section_set_string(ini_section_t section, const char *name, const char *val); extern void ini_section_set_string(ini_section_t section, const char *name, const char *val);
extern void ini_section_set_wstring(ini_section_t section, const char *name, wchar_t *val); extern void ini_section_set_wstring(ini_section_t section, const char *name, wchar_t *val);
extern int ini_has_entry(ini_section_t self, const char *name);
#define ini_delete_var(ini, head, name) ini_section_delete_var(ini_find_section(ini, head), name) #define ini_delete_var(ini, head, name) ini_section_delete_var(ini_find_section(ini, head), name)

158
src/include/86box/qt-glsl.h Normal file
View File

@@ -0,0 +1,158 @@
#ifndef SRC_WX_GLSL_H_
#define SRC_WX_GLSL_H_
#define MAX_PREV 7
#define MAX_SHADERS 20
#define MAX_TEXTURES 20
#define MAX_PARAMETERS 100
#define MAX_USER_SHADERS 20
//#define SDL2_SHADER_DEBUG
struct shader_scale {
int mode[2];
float value[2];
};
struct shader_state {
float input_size[2];
float input_texture_size[2];
float output_texture_size[2];
float output_size[2];
float tex_coords[8];
};
struct shader_vbo {
int vertex_coord;
int tex_coord;
int color;
};
struct shader_texture {
int id;
int width;
int height;
int type;
int internal_format;
int format;
int min_filter;
int mag_filter;
int wrap_mode;
void *data;
int mipmap;
};
struct shader_lut_texture {
char name[50];
struct shader_texture texture;
};
struct shader_fbo {
int id;
struct shader_texture texture;
int srgb;
int mipmap_input;
};
struct shader_prev {
struct shader_fbo fbo;
struct shader_vbo vbo;
};
struct shader_input {
int texture;
int input_size;
int texture_size;
int tex_coord;
};
struct shader_uniforms {
int mvp_matrix;
int vertex_coord;
int tex_coord;
int color;
int texture;
int input_size;
int texture_size;
int output_size;
int frame_count;
int frame_direction;
struct shader_input orig;
struct shader_input pass[MAX_SHADERS];
struct shader_input prev_pass[MAX_SHADERS];
struct shader_input prev[MAX_PREV];
int parameters[MAX_PARAMETERS];
int lut_textures[MAX_TEXTURES];
};
struct shader_program {
int vertex_shader;
int fragment_shader;
int id;
};
struct shader_parameter {
char id[64];
char description[64];
float default_value;
float value;
float min;
float max;
float step;
};
struct shader_pass {
int active;
char alias[64];
int vertex_array;
int frame_count_mod;
struct shader_program program;
struct shader_uniforms uniforms;
struct shader_fbo fbo;
struct shader_vbo vbo;
struct shader_state state;
struct shader_scale scale;
};
struct glsl_shader {
int active;
char name[64];
int num_passes;
struct shader_pass passes[MAX_SHADERS];
int num_lut_textures;
struct shader_lut_texture lut_textures[MAX_TEXTURES];
int num_parameters;
struct shader_parameter parameters[MAX_PARAMETERS];
struct shader_pass prev_scene;
struct shader_prev prev[MAX_PREV + 1];
int last_prev_update;
int has_prev;
float shader_refresh_rate;
int input_filter_linear;
};
typedef struct glsl_t {
int num_shaders;
struct glsl_shader shaders[MAX_USER_SHADERS];
struct shader_pass scene;
struct shader_pass final_pass;
struct shader_pass fs_color;
#ifdef SDL2_SHADER_DEBUG
struct shader_pass debug;
#endif
int srgb;
} glsl_t;
#endif

View File

@@ -0,0 +1,59 @@
#ifndef SRC_WX_GLSLP_PARSER_H_
#define SRC_WX_GLSLP_PARSER_H_
#include "qt-glsl.h"
struct parameter {
char id[64];
char description[64];
float default_value;
float value;
float min;
float max;
float step;
};
struct texture {
char path[256];
char name[50];
int linear;
int mipmap;
char wrap_mode[50];
};
struct shader {
char shader_fn[1024];
char *shader_program;
char alias[64];
int filter_linear;
int float_framebuffer;
int srgb_framebuffer;
int mipmap_input;
int frame_count_mod;
char wrap_mode[50];
char scale_type_x[9], scale_type_y[9];
float scale_x, scale_y;
};
typedef struct glslp_t {
char name[64];
int num_shaders;
struct shader shaders[MAX_SHADERS];
int num_textures;
struct texture textures[MAX_TEXTURES];
int num_parameters;
struct parameter parameters[MAX_PARAMETERS];
int input_filter_linear;
} glslp_t;
void get_glslp_name(const char *f, char *s, int size);
glslp_t *glslp_parse(const char *f);
void glslp_free(glslp_t *p);
void glslp_read_shader_config(glslp_t *shader);
void glslp_write_shader_config(glslp_t *shader);
#endif /* SRC_WX_GLSLP_PARSER_H_ */

133
src/ini.c
View File

@@ -157,6 +157,22 @@ find_entry(section_t *section, const char *name)
return (NULL); return (NULL);
} }
int
ini_has_entry(ini_section_t self, const char *name)
{
section_t *section = (section_t *) self;
const entry_t *entry;
if (section == NULL)
return 0;
entry = find_entry(section, name);
if (entry == NULL)
return 0;
return 1;
}
static int static int
entries_num(section_t *section) entries_num(section_t *section)
{ {
@@ -237,9 +253,8 @@ ini_delete_section_if_empty(ini_t ini, ini_section_t section)
static section_t * static section_t *
create_section(list_t *head, const char *name) create_section(list_t *head, const char *name)
{ {
section_t *ns = malloc(sizeof(section_t)); section_t *ns = calloc(1, sizeof(section_t));
memset(ns, 0x00, sizeof(section_t));
memcpy(ns->name, name, strlen(name) + 1); memcpy(ns->name, name, strlen(name) + 1);
list_add(&ns->list, head); list_add(&ns->list, head);
@@ -262,9 +277,8 @@ ini_find_or_create_section(ini_t ini, const char *name)
static entry_t * static entry_t *
create_entry(section_t *section, const char *name) create_entry(section_t *section, const char *name)
{ {
entry_t *ne = malloc(sizeof(entry_t)); entry_t *ne = calloc(1, sizeof(entry_t));
memset(ne, 0x00, sizeof(entry_t));
memcpy(ne->name, name, strlen(name) + 1); memcpy(ne->name, name, strlen(name) + 1);
list_add(&ne->list, &section->entry_head); list_add(&ne->list, &section->entry_head);
@@ -373,11 +387,8 @@ ini_read(const char *fn)
if (fp == NULL) if (fp == NULL)
return NULL; return NULL;
head = malloc(sizeof(list_t)); head = calloc(1, sizeof(list_t));
memset(head, 0x00, sizeof(list_t)); sec = calloc(1, sizeof(section_t));
sec = malloc(sizeof(section_t));
memset(sec, 0x00, sizeof(section_t));
list_add(&sec->list, head); list_add(&sec->list, head);
if (bom) if (bom)
@@ -458,7 +469,7 @@ ini_read(const char *fn)
d = c; d = c;
/* Allocate a new variable entry.. */ /* Allocate a new variable entry.. */
ne = malloc(sizeof(entry_t)); ne = calloc(1, sizeof(entry_t));
memset(ne, 0x00, sizeof(entry_t)); memset(ne, 0x00, sizeof(entry_t));
memcpy(ne->name, ename, 128); memcpy(ne->name, ename, 128);
wcsncpy(ne->wdata, &buff[d], sizeof_w(ne->wdata) - 1); wcsncpy(ne->wdata, &buff[d], sizeof_w(ne->wdata) - 1);
@@ -534,6 +545,103 @@ ini_write(ini_t ini, const char *fn)
(void) fclose(fp); (void) fclose(fp);
} }
/* Wide-character version of "trim" */
wchar_t *
trim_w(wchar_t *str)
{
size_t len = 0;
wchar_t *frontp = str;
wchar_t *endp = NULL;
if (str == NULL) {
return NULL;
}
if (str[0] == L'\0') {
return str;
}
len = wcslen(str);
endp = str + len;
/* Move the front and back pointers to address the first non-whitespace
* characters from each end.
*/
while (iswspace((wint_t) *frontp)) {
++frontp;
}
if (endp != frontp) {
while (iswspace((wint_t) *(--endp)) && endp != frontp) { }
}
if (frontp != str && endp == frontp)
*str = L'\0';
else if ((str + len - 1) != endp)
*(endp + 1) = L'\0';
/* Shift the string so that it starts at str so that if it's dynamically
* allocated, we can still free it on the returned pointer. Note the reuse
* of endp to mean the front of the string buffer now.
*/
endp = str;
if (frontp != str) {
while (*frontp) {
*endp++ = *frontp++;
}
*endp = L'\0';
}
return str;
}
extern char* trim(char* str);
void
ini_strip_quotes(ini_t ini)
{
list_t *list = (list_t *) ini;
section_t *sec;
sec = (section_t *) list->next;
while (sec != NULL) {
entry_t *ent;
ent = (entry_t *) sec->entry_head.next;
while (ent != NULL) {
if (ent->name[0] != '\0') {
int trailing_hash = strcspn(ent->data, "#");
int trailing_quote;
ent->wdata[trailing_hash] = 0;
ent->data[trailing_hash] = 0;
if (ent->wdata[0] == L'\"') {
memmove(ent->wdata, &ent->wdata[1], sizeof(ent->wdata) - sizeof(wchar_t));
}
if (ent->wdata[wcslen(ent->wdata) - 1] == L'\"') {
ent->wdata[wcslen(ent->wdata) - 1] = 0;
}
if (ent->data[0] == '\"') {
memmove(ent->data, &ent->data[1], sizeof(ent->data) - sizeof(char));
}
if (ent->data[strlen(ent->data) - 1] == '\"') {
ent->data[strlen(ent->data) - 1] = 0;
}
trailing_quote = strcspn(ent->data, "\"");
ent->wdata[trailing_quote] = 0;
ent->data[trailing_quote] = 0;
trim_w(ent->wdata);
trim(ent->data);
}
ent = (entry_t *) ent->list.next;
}
sec = (section_t *) sec->list.next;
}
}
ini_t ini_t
ini_new(void) ini_new(void)
{ {
@@ -593,6 +701,11 @@ ini_section_get_int(ini_section_t self, const char *name, int def)
if (entry == NULL) if (entry == NULL)
return def; return def;
if (stricmp(entry->data, "true") == 0)
return 1;
if (stricmp(entry->data, "false") == 0)
return 0;
sscanf(entry->data, "%i", &value); sscanf(entry->data, "%i", &value);
return value; return value;

View File

@@ -89,11 +89,7 @@ add_library(ui STATIC
qt_hardwarerenderer.hpp qt_hardwarerenderer.hpp
qt_openglrenderer.cpp qt_openglrenderer.cpp
qt_openglrenderer.hpp qt_openglrenderer.hpp
qt_opengloptions.cpp qt_glsl_parser.cpp
qt_opengloptions.hpp
qt_opengloptionsdialog.cpp
qt_opengloptionsdialog.hpp
qt_opengloptionsdialog.ui
qt_settings.cpp qt_settings.cpp
qt_settings.hpp qt_settings.hpp
@@ -190,6 +186,14 @@ add_library(ui STATIC
../qt_resources.qrc ../qt_resources.qrc
./qdarkstyle/dark/darkstyle.qrc ./qdarkstyle/dark/darkstyle.qrc
qt_openglshadermanagerdialog.hpp
qt_openglshadermanagerdialog.cpp
qt_openglshadermanagerdialog.ui
qt_openglshaderconfig.hpp
qt_openglshaderconfig.cpp
qt_openglshaderconfig.ui
) )
if(RTMIDI) if(RTMIDI)

View File

@@ -54,6 +54,8 @@ plat_vidapi(const char *api)
return 4; return 4;
} else if (!strcasecmp(api, "vnc")) { } else if (!strcasecmp(api, "vnc")) {
return 5; return 5;
} else if (!strcasecmp(api, "qt_opengl3_pcem")) {
return 6;
} }
return 0; return 0;
@@ -83,6 +85,9 @@ plat_vidapi_name(int api)
case 5: case 5:
name = "vnc"; name = "vnc";
break; break;
case 6:
name = "qt_opengl3_pcem";
break;
default: default:
fatal("Unknown renderer: %i\n", api); fatal("Unknown renderer: %i\n", api);
break; break;

384
src/qt/qt_glsl_parser.cpp Normal file
View File

@@ -0,0 +1,384 @@
#include "qt_mainwindow.hpp"
#include <QMessageBox>
#include <QWindow>
#include <QCoreApplication>
extern MainWindow* main_window;
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdint.h>
extern "C"
{
#include <86box/86box.h>
#include <86box/ini.h>
#include <86box/config.h>
#include <86box/qt-glslp-parser.h>
#include <86box/path.h>
extern void startblit();
extern void endblit();
}
#define safe_strncpy(a, b, n) \
do { \
strncpy((a), (b), (n)-1); \
(a)[(n)-1] = 0; \
} while (0)
static inline void *wx_config_load(const char *path) { ini_t ini = ini_read(path); if (ini) ini_strip_quotes(ini); return (void*)ini; }
static inline int wx_config_get_string(void *config, const char *name, char *dst, int size, const char *defVal) {
int res = ini_has_entry(ini_find_or_create_section((ini_t)config, ""), name);
char* str = ini_get_string((ini_t)config, "", name, (char*)defVal);
if (size == 0)
return res;
if (str != NULL)
strncpy(dst, str, size - 1);
else
dst[0] = 0;
return res;
}
static inline int wx_config_get_int(void *config, const char *name, int *dst, int defVal) {
int res = ini_has_entry(ini_find_or_create_section((ini_t)config, ""), name);
*dst = ini_get_int((ini_t)config, "", name, defVal);
return res;
}
static inline int wx_config_get_float(void *config, const char *name, float *dst, float defVal) {
int res = ini_has_entry(ini_find_or_create_section((ini_t)config, ""), name);
*dst = (float)ini_get_double((ini_t)config, "", name, defVal);
return res;
}
static inline int wx_config_get_bool(void *config, const char *name, int *dst, int defVal) {
int res = ini_has_entry(ini_find_or_create_section((ini_t)config, ""), name);
*dst = !!ini_get_int((ini_t)config, "", name, defVal);
return res;
}
static inline int wx_config_has_entry(void *config, const char *name) { return ini_has_entry(ini_find_or_create_section((ini_t)config, ""), name); }
static inline void wx_config_free(void *config) { ini_close(config); };
static int endswith(const char *str, const char *ext) {
int i;
const char *p;
int elen = strlen(ext);
int slen = strlen(str);
if (slen >= elen) {
p = &str[slen - elen];
for (i = 0; i < elen; ++i) {
if (tolower(p[i]) != tolower(ext[i]))
return 0;
}
return 1;
}
return 0;
}
static char *load_file(const char *fn) {
FILE *f = fopen(fn, "rb");
if (!f)
return 0;
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);
char *data = (char*)malloc(fsize + 1);
fread(data, fsize, 1, f);
fclose(f);
data[fsize] = 0;
return data;
}
static void strip_lines(const char *program, const char *starts_with) {
/* strip parameters */
char *ptr = strstr(program, starts_with);
while (ptr) {
while (*ptr != '\n' && *ptr != '\0')
*ptr++ = ' ';
ptr = strstr(program, starts_with);
}
}
static void strip_parameters(const char *program) {
/* strip parameters */
strip_lines(program, "#pragma parameter");
}
static void strip_defines(const char *program) {
/* strip texture define */
strip_lines(program, "#define texture");
}
static int has_parameter(glslp_t *glsl, char *id) {
int i;
for (i = 0; i < glsl->num_parameters; ++i)
if (!strcmp(glsl->parameters[i].id, id))
return 1;
return 0;
}
static int get_parameters(glslp_t *glsl) {
int i;
struct parameter p;
for (i = 0; i < glsl->num_shaders; ++i) {
struct shader *shader = &glsl->shaders[i];
FILE *f = fopen(shader->shader_fn, "rb");
if (!f)
return 0;
char line[1024];
while (fgets(line, sizeof(line) - 1, f) && glsl->num_parameters < MAX_PARAMETERS) {
int num = sscanf(line, "#pragma parameter %63s \"%63[^\"]\" %f %f %f %f", p.id, p.description,
&p.default_value, &p.min, &p.max, &p.step);
if (num < 5)
continue;
p.id[63] = 0;
p.description[63] = 0;
if (num == 5)
p.step = 0.1f * (p.max - p.min);
p.value = p.default_value;
if (!has_parameter(glsl, p.id)) {
memcpy(&glsl->parameters[glsl->num_parameters++], &p, sizeof(struct parameter));
pclog("Read parameter: %s (%s) %f, %f -> %f (%f)\n", p.id, p.description, p.default_value, p.min,
p.max, p.step);
}
}
fclose(f);
}
return 1;
}
static struct parameter *get_parameter(glslp_t *glslp, const char *id) {
int i;
for (i = 0; i < glslp->num_parameters; ++i) {
if (!strcmp(glslp->parameters[i].id, id)) {
return &glslp->parameters[i];
}
}
return 0;
}
static glslp_t *glsl_parse(const char *f) {
glslp_t *glslp = (glslp_t*)malloc(sizeof(glslp_t));
memset(glslp, 0, sizeof(glslp_t));
glslp->num_shaders = 1;
struct shader *shader = &glslp->shaders[0];
strcpy(shader->shader_fn, f);
shader->shader_program = load_file(f);
if (!shader->shader_program) {
QMessageBox::critical((QWidget *) qApp->findChild<QWindow *>(), QObject::tr("GLSL error"), QObject::tr("Could not load shader %1").arg(shader->shader_fn));
//wx_simple_messagebox("GLSL error", "Could not load shader %s\n", shader->shader_fn);
glslp_free(glslp);
return 0;
}
strip_parameters(shader->shader_program);
strip_defines(shader->shader_program);
shader->scale_x = shader->scale_y = 1.0f;
strcpy(shader->scale_type_x, "source");
strcpy(shader->scale_type_y, "source");
get_parameters(glslp);
return glslp;
}
extern "C" {
void get_glslp_name(const char *f, char *s, int size) { safe_strncpy(s, path_get_filename((char *)f), size); }
glslp_t *glslp_parse(const char *f) {
int i, j, len, sublen;
char s[513], t[513], z[540];
memset(s, 0, sizeof(s));
if (endswith(f, ".glsl"))
return glsl_parse(f);
void *cfg = wx_config_load(f);
if (!cfg) {
fprintf(stderr, "GLSLP Error: Could not load GLSLP-file %s\n", f);
return 0;
}
glslp_t *glslp = (glslp_t*)malloc(sizeof(glslp_t));
memset(glslp, 0, sizeof(glslp_t));
get_glslp_name(f, glslp->name, sizeof(glslp->name));
wx_config_get_int(cfg, "shaders", &glslp->num_shaders, 0);
wx_config_get_bool(cfg, "filter_linear0", &glslp->input_filter_linear, -1);
for (i = 0; i < glslp->num_shaders; ++i) {
struct shader *shader = &glslp->shaders[i];
snprintf(s, sizeof(s) - 1, "shader%d", i);
if (!wx_config_get_string(cfg, s, t, sizeof(t), 0)) {
/* shader doesn't exist, lets break here */
glslp->num_shaders = i;
break;
}
strcpy(s, f);
*path_get_filename(s) = 0;
snprintf(shader->shader_fn, sizeof(shader->shader_fn) - 1, "%s%s", s, t);
shader->shader_program = load_file(shader->shader_fn);
if (!shader->shader_program) {
fprintf(stderr, "GLSLP Error: Could not load shader %s\n", shader->shader_fn);
glslp_free(glslp);
return 0;
}
strip_parameters(shader->shader_program);
strip_defines(shader->shader_program);
snprintf(s, sizeof(s) - 1, "alias%d", i);
wx_config_get_string(cfg, s, shader->alias, sizeof(shader->alias), 0);
snprintf(s, sizeof(s) - 1, "filter_linear%d", i + 1);
wx_config_get_bool(cfg, s, &shader->filter_linear, 0);
snprintf(s, sizeof(s) - 1, "wrap_mode%d", i);
wx_config_get_string(cfg, s, shader->wrap_mode, sizeof(shader->wrap_mode), 0);
snprintf(s, sizeof(s) - 1, "float_framebuffer%d", i);
wx_config_get_bool(cfg, s, &shader->float_framebuffer, 0);
snprintf(s, sizeof(s) - 1, "srgb_framebuffer%d", i);
wx_config_get_bool(cfg, s, &shader->srgb_framebuffer, 0);
snprintf(s, sizeof(s) - 1, "mipmap_input%d", i);
wx_config_get_bool(cfg, s, &shader->mipmap_input, 0);
strcpy(shader->scale_type_x, "source");
snprintf(s, sizeof(s) - 1, "scale_type_x%d", i);
wx_config_get_string(cfg, s, shader->scale_type_x, sizeof(shader->scale_type_x), 0);
strcpy(shader->scale_type_y, "source");
snprintf(s, sizeof(s) - 1, "scale_type_y%d", i);
wx_config_get_string(cfg, s, shader->scale_type_y, sizeof(shader->scale_type_y), 0);
snprintf(s, sizeof(s) - 1, "scale_type%d", i);
if (wx_config_has_entry(cfg, s)) {
wx_config_get_string(cfg, s, shader->scale_type_x, sizeof(shader->scale_type_x), 0);
wx_config_get_string(cfg, s, shader->scale_type_y, sizeof(shader->scale_type_y), 0);
}
snprintf(s, sizeof(s) - 1, "scale_x%d", i);
wx_config_get_float(cfg, s, &shader->scale_x, 1.0f);
snprintf(s, sizeof(s) - 1, "scale_y%d", i);
wx_config_get_float(cfg, s, &shader->scale_y, 1.0f);
snprintf(s, sizeof(s) - 1, "scale%d", i);
if (wx_config_has_entry(cfg, s)) {
wx_config_get_float(cfg, s, &shader->scale_x, 1.0f);
wx_config_get_float(cfg, s, &shader->scale_y, 1.0f);
}
snprintf(s, sizeof(s) - 1, "frame_count_mod%d", i);
wx_config_get_int(cfg, s, &shader->frame_count_mod, 0);
}
/* textures */
glslp->num_textures = 0;
wx_config_get_string(cfg, "textures", t, sizeof(t), 0);
len = strlen(t);
j = 0;
sublen = 0;
for (i = 0; i < len; ++i) {
if (t[i] == ';' || i == len - 1) {
sublen = (i - j) + ((i == len - 1) ? 1 : 0) + 1;
safe_strncpy(s, t + j, sublen);
s[511 < sublen ? 511 : sublen] = 0;
if (s[strlen(s) - 1] == ';') s[strlen(s) - 1] = 0;
struct texture *tex = &glslp->textures[glslp->num_textures++];
strcpy(tex->name, s);
wx_config_get_string(cfg, s, tex->path, sizeof(tex->path), 0);
snprintf(z, sizeof(z) - 1, "%s_linear", s);
wx_config_get_bool(cfg, z, &tex->linear, 0);
snprintf(z, sizeof(z) - 1, "%s_mipmap", s);
wx_config_get_bool(cfg, z, &tex->mipmap, 0);
snprintf(z, sizeof(z) - 1, "%s_wrap_mode", s);
wx_config_get_string(cfg, z, tex->wrap_mode, sizeof(tex->wrap_mode), 0);
j = i + 1;
}
}
/* parameters */
get_parameters(glslp);
wx_config_get_string(cfg, "parameters", t, sizeof(t), 0);
len = strlen(t);
j = 0;
sublen = 0;
for (i = 0; i < len; ++i) {
if (t[i] == ';' || i == len - 1) {
sublen = (i - j) + ((i == len - 1) ? 1 : 0) + 1;
safe_strncpy(s, t + j, sublen);
s[511 < sublen ? 511 : sublen] = 0;
struct parameter *p = get_parameter(glslp, s);
if (p)
wx_config_get_float(cfg, s, &p->default_value, 0);
j = i + 1;
}
}
wx_config_free(cfg);
return glslp;
}
void glslp_free(glslp_t *p) {
int i;
for (i = 0; i < p->num_shaders; ++i)
if (p->shaders[i].shader_program)
free(p->shaders[i].shader_program);
free(p);
}
void glslp_read_shader_config(glslp_t *shader) {
char s[512];
int i;
char *name = shader->name;
sprintf(s, "GL3 Shaders - %s", name);
for (i = 0; i < shader->num_parameters; ++i) {
struct parameter *param = &shader->parameters[i];
param->value = config_get_double(s, param->id, param->default_value);
}
}
void glslp_write_shader_config(glslp_t *shader) {
char s[512];
int i;
char *name = shader->name;
startblit();
sprintf(s, "GL3 Shaders - %s", name);
for (i = 0; i < shader->num_parameters; ++i) {
struct parameter *param = &shader->parameters[i];
config_set_double(s, param->id, param->value);
}
endblit();
}
}

View File

@@ -90,6 +90,8 @@ extern "C" {
#include <86box/timer.h> #include <86box/timer.h>
#include <86box/nvr.h> #include <86box/nvr.h>
extern int qt_nvr_save(void); extern int qt_nvr_save(void);
bool cpu_thread_running = false;
} }
void qt_set_sequence_auto_mnemonic(bool b); void qt_set_sequence_auto_mnemonic(bool b);
@@ -389,6 +391,7 @@ main_thread_fn()
// title_update = 1; // title_update = 1;
uint64_t old_time = elapsed_timer.elapsed(); uint64_t old_time = elapsed_timer.elapsed();
int drawits = frames = 0; int drawits = frames = 0;
is_cpu_thread = 1;
while (!is_quit && cpu_thread_run) { while (!is_quit && cpu_thread_run) {
/* See if it is time to run a frame of code. */ /* See if it is time to run a frame of code. */
const uint64_t new_time = elapsed_timer.elapsed(); const uint64_t new_time = elapsed_timer.elapsed();
@@ -443,6 +446,7 @@ main_thread_fn()
} }
} }
cpu_thread_running = false;
is_quit = 1; is_quit = 1;
for (uint8_t i = 1; i < GFXCARD_MAX; i ++) { for (uint8_t i = 1; i < GFXCARD_MAX; i ++) {
if (gfxcard[i]) { if (gfxcard[i]) {
@@ -735,6 +739,7 @@ main(int argc, char *argv[])
#endif #endif
plat_pause(0); plat_pause(0);
cpu_thread_running = true;
main_thread = new std::thread(main_thread_fn); main_thread = new std::thread(main_thread_fn);
}); });

View File

@@ -62,6 +62,8 @@ extern int qt_nvr_save(void);
#ifdef MTR_ENABLED #ifdef MTR_ENABLED
# include <minitrace/minitrace.h> # include <minitrace/minitrace.h>
#endif #endif
extern bool cpu_thread_running;
}; };
#include <QGuiApplication> #include <QGuiApplication>
@@ -452,6 +454,9 @@ MainWindow::MainWindow(QWidget *parent)
endblit(); endblit();
} }
#endif #endif
case 6:
newVidApi = RendererStack::Renderer::OpenGL3PCem;
break;
} }
ui->stackedWidget->switchRenderer(newVidApi); ui->stackedWidget->switchRenderer(newVidApi);
if (!show_second_monitors) if (!show_second_monitors)
@@ -779,6 +784,14 @@ MainWindow::closeEvent(QCloseEvent *event)
ui->stackedWidget->mouse_exit_func(); ui->stackedWidget->mouse_exit_func();
ui->stackedWidget->switchRenderer(RendererStack::Renderer::Software); ui->stackedWidget->switchRenderer(RendererStack::Renderer::Software);
for (int i = 1; i < MONITORS_NUM; i++) {
if (renderers[i] && renderers[i]->isHidden()) {
renderers[i]->show();
QApplication::processEvents();
renderers[i]->switchRenderer(RendererStack::Renderer::Software);
QApplication::processEvents();
}
}
qt_nvr_save(); qt_nvr_save();
config_save(); config_save();
@@ -1253,7 +1266,7 @@ MainWindow::eventFilter(QObject *receiver, QEvent *event)
static auto curdopause = dopause; static auto curdopause = dopause;
if (event->type() == QEvent::WindowBlocked) { if (event->type() == QEvent::WindowBlocked) {
curdopause = dopause; curdopause = dopause;
plat_pause(1); plat_pause(isShowMessage ? 2 : 1);
emit setMouseCapture(false); emit setMouseCapture(false);
} else if (event->type() == QEvent::WindowUnblocked) { } else if (event->type() == QEvent::WindowUnblocked) {
plat_pause(curdopause); plat_pause(curdopause);
@@ -1277,6 +1290,10 @@ void
MainWindow::showMessage(int flags, const QString &header, const QString &message) MainWindow::showMessage(int flags, const QString &header, const QString &message)
{ {
if (QThread::currentThread() == this->thread()) { if (QThread::currentThread() == this->thread()) {
if (!cpu_thread_running) {
showMessageForNonQtThread(flags, header, message, nullptr);
}
else
showMessage_(flags, header, message); showMessage_(flags, header, message);
} else { } else {
std::atomic_bool done = false; std::atomic_bool done = false;
@@ -1293,6 +1310,7 @@ MainWindow::showMessage_(int flags, const QString &header, const QString &messag
if (done) { if (done) {
*done = false; *done = false;
} }
isShowMessage = true;
QMessageBox box(QMessageBox::Warning, header, message, QMessageBox::NoButton, this); QMessageBox box(QMessageBox::Warning, header, message, QMessageBox::NoButton, this);
if (flags & (MBX_FATAL)) { if (flags & (MBX_FATAL)) {
box.setIcon(QMessageBox::Critical); box.setIcon(QMessageBox::Critical);
@@ -1304,6 +1322,7 @@ MainWindow::showMessage_(int flags, const QString &header, const QString &messag
if (done) { if (done) {
*done = true; *done = true;
} }
isShowMessage = false;
if (cpu_thread_run == 0) if (cpu_thread_run == 0)
QApplication::exit(-1); QApplication::exit(-1);
} }
@@ -1980,15 +1999,40 @@ MainWindow::changeEvent(QEvent *event)
} }
} }
void
MainWindow::reloadAllRenderers()
{
reload_renderers = true;
}
void void
MainWindow::on_actionRenderer_options_triggered() MainWindow::on_actionRenderer_options_triggered()
{ {
if (const auto dlg = ui->stackedWidget->getOptions(this)) { if (const auto dlg = ui->stackedWidget->getOptions(this)) {
if (dlg->exec() == QDialog::Accepted) { if (dlg->exec() == QDialog::Accepted) {
if (ui->stackedWidget->reloadRendererOption()) {
ui->stackedWidget->switchRenderer(static_cast<RendererStack::Renderer>(vid_api));
if (show_second_monitors) {
for (int i = 1; i < MONITORS_NUM; i++) { for (int i = 1; i < MONITORS_NUM; i++) {
if (renderers[i] && renderers[i]->reloadRendererOption() && renderers[i]->hasOptions()) {
ui->stackedWidget->switchRenderer(static_cast<RendererStack::Renderer>(vid_api));
}
}
}
} else for (int i = 1; i < MONITORS_NUM; i++) {
if (renderers[i] && renderers[i]->hasOptions()) if (renderers[i] && renderers[i]->hasOptions())
renderers[i]->reloadOptions(); renderers[i]->reloadOptions();
} }
} else if (reload_renderers && ui->stackedWidget->reloadRendererOption()) {
reload_renderers = false;
ui->stackedWidget->switchRenderer(static_cast<RendererStack::Renderer>(vid_api));
if (show_second_monitors) {
for (int i = 1; i < MONITORS_NUM; i++) {
if (renderers[i] && renderers[i]->reloadRendererOption() && renderers[i]->hasOptions()) {
ui->stackedWidget->switchRenderer(static_cast<RendererStack::Renderer>(vid_api));
}
}
}
} }
} }
} }

View File

@@ -32,6 +32,7 @@ public:
QSize getRenderWidgetSize(); QSize getRenderWidgetSize();
void setSendKeyboardInput(bool enabled); void setSendKeyboardInput(bool enabled);
void checkFullscreenHotkey(); void checkFullscreenHotkey();
void reloadAllRenderers();
std::array<std::unique_ptr<RendererStack>, 8> renderers; std::array<std::unique_ptr<RendererStack>, 8> renderers;
signals: signals:
@@ -173,11 +174,17 @@ private:
bool fs_on_signal = false; bool fs_on_signal = false;
bool fs_off_signal = false; bool fs_off_signal = false;
/* Reload the renderers after closing renderer options dialog. */
bool reload_renderers = false;
friend class SpecifyDimensions; friend class SpecifyDimensions;
friend class ProgSettings; friend class ProgSettings;
friend class RendererCommon; friend class RendererCommon;
friend class RendererStack; // For UI variable access by non-primary renderer windows. friend class RendererStack; // For UI variable access by non-primary renderer windows.
friend class WindowsRawInputFilter; // Needed to reload renderers on style sheet changes. friend class WindowsRawInputFilter; // Needed to reload renderers on style sheet changes.
bool isShowMessage = false;
}; };
#endif // QT_MAINWINDOW_HPP #endif // QT_MAINWINDOW_HPP

View File

@@ -1,196 +0,0 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* OpenGL renderer options for Qt
*
*
*
* Authors: Teemu Korhonen
*
* Copyright 2022 Teemu Korhonen
*/
#include <QByteArray>
#include <QFile>
#include <QRegularExpression>
#include <QStringBuilder>
#include <stdexcept>
#include "qt_opengloptions.hpp"
extern "C" {
#include <86box/86box.h>
}
/* Default vertex shader. */
static const GLchar *vertex_shader = "\
in vec2 VertexCoord;\n\
in vec2 TexCoord;\n\
out vec2 tex;\n\
void main(){\n\
gl_Position = vec4(VertexCoord, 0.0, 1.0);\n\
tex = TexCoord;\n\
}\n";
/* Default fragment shader. */
static const GLchar *fragment_shader = "\
in vec2 tex;\n\
uniform sampler2D texsampler;\n\
out vec4 color;\n\
void main() {\n\
color = texture(texsampler, tex);\n\
}\n";
OpenGLOptions::OpenGLOptions(QObject *parent, bool loadConfig, const QString &glslVersion)
: QObject(parent)
, m_glslVersion(glslVersion)
{
m_filter = video_filter_method == 0
? FilterType::Nearest
: FilterType::Linear;
if (!loadConfig)
return;
/* Initialize with config. */
m_vsync = video_vsync != 0;
m_framerate = video_framerate;
m_renderBehavior = video_framerate == -1
? RenderBehaviorType::SyncWithVideo
: RenderBehaviorType::TargetFramerate;
QString shaderPath(video_shader);
if (shaderPath.isEmpty()) {
addDefaultShader();
} else {
try {
addShader(shaderPath);
} catch (const std::runtime_error &) {
/* Fallback to default shader */
addDefaultShader();
}
}
}
void
OpenGLOptions::save() const
{
video_vsync = m_vsync ? 1 : 0;
video_framerate = m_renderBehavior == RenderBehaviorType::SyncWithVideo ? -1 : m_framerate;
video_filter_method = m_filter == FilterType::Nearest ? 0 : 1;
/* TODO: multiple shaders */
auto path = m_shaders.first().path().toLocal8Bit();
if (!path.isEmpty())
qstrncpy(video_shader, path.constData(), sizeof(video_shader));
else
video_shader[0] = '\0';
}
OpenGLOptions::FilterType
OpenGLOptions::filter() const
{
/* Filter method is controlled externally */
return video_filter_method == 0
? FilterType::Nearest
: FilterType::Linear;
}
void
OpenGLOptions::setRenderBehavior(RenderBehaviorType value)
{
m_renderBehavior = value;
}
void
OpenGLOptions::setFrameRate(int value)
{
m_framerate = value;
}
void
OpenGLOptions::setVSync(bool value)
{
m_vsync = value;
}
void
OpenGLOptions::setFilter(FilterType value)
{
m_filter = value;
}
void
OpenGLOptions::addShader(const QString &path)
{
QFile shader_file(path);
if (!shader_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
throw std::runtime_error(
QString(tr("Error opening \"%1\": %2"))
.arg(path)
.arg(shader_file.errorString())
.toStdString());
}
auto shader_text = QString(shader_file.readAll());
shader_file.close();
/* Remove parameter lines */
shader_text.remove(QRegularExpression("^\\s*#pragma parameter.*?\\n", QRegularExpression::MultilineOption));
QRegularExpression version("^\\s*(#version\\s+\\w+)", QRegularExpression::MultilineOption);
auto match = version.match(shader_text);
QString version_line(m_glslVersion);
if (match.hasMatch()) {
/* Extract existing version and remove it. */
version_line = match.captured(1);
shader_text.remove(version);
}
auto shader = new QOpenGLShaderProgram(this);
auto throw_shader_error = [path, shader](const QString &what) {
throw std::runtime_error(
QString(what % ":\n\n %2")
.arg(path)
.arg(shader->log())
.toStdString());
};
static const char *extension = "\n#extension GL_ARB_shading_language_420pack : enable\n";
if (!shader->addShaderFromSourceCode(QOpenGLShader::Vertex, version_line % extension % "\n#define VERTEX\n#line 1\n" % shader_text))
throw_shader_error(tr("Error compiling vertex shader in file \"%1\""));
if (!shader->addShaderFromSourceCode(QOpenGLShader::Fragment, version_line % extension % "\n#define FRAGMENT\n#line 1\n" % shader_text))
throw_shader_error(tr("Error compiling fragment shader in file \"%1\""));
if (!shader->link())
throw_shader_error(tr("Error linking shader program in file \"%1\""));
m_shaders << OpenGLShaderPass(shader, path);
}
void
OpenGLOptions::addDefaultShader()
{
auto shader = new QOpenGLShaderProgram(this);
shader->addShaderFromSourceCode(QOpenGLShader::Vertex, m_glslVersion % "\n" % vertex_shader);
shader->addShaderFromSourceCode(QOpenGLShader::Fragment, m_glslVersion % "\n" % fragment_shader);
shader->link();
m_shaders << OpenGLShaderPass(shader, QString());
}

View File

@@ -1,102 +0,0 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Header for OpenGL renderer options
*
*
*
* Authors: Teemu Korhonen
*
* Copyright 2022 Teemu Korhonen
*/
#ifndef QT_OPENGLOPTIONS_HPP
#define QT_OPENGLOPTIONS_HPP
#include <QList>
#include <QObject>
#include <QOpenGLContext>
#include <QOpenGLShaderProgram>
class OpenGLShaderPass {
public:
OpenGLShaderPass(QOpenGLShaderProgram *shader, const QString &path)
: m_shader(shader)
, m_path(path)
, m_vertex_coord(shader->attributeLocation("VertexCoord"))
, m_tex_coord(shader->attributeLocation("TexCoord"))
, m_color(shader->attributeLocation("Color"))
, m_mvp_matrix(shader->uniformLocation("MVPMatrix"))
, m_input_size(shader->uniformLocation("InputSize"))
, m_output_size(shader->uniformLocation("OutputSize"))
, m_texture_size(shader->uniformLocation("TextureSize"))
, m_frame_count(shader->uniformLocation("FrameCount"))
{
}
bool bind() const { return m_shader->bind(); }
const QString &path() const { return m_path; }
const GLint &vertex_coord() const { return m_vertex_coord; }
const GLint &tex_coord() const { return m_tex_coord; }
const GLint &color() const { return m_color; }
const GLint &mvp_matrix() const { return m_mvp_matrix; }
const GLint &input_size() const { return m_input_size; }
const GLint &output_size() const { return m_output_size; }
const GLint &texture_size() const { return m_texture_size; }
const GLint &frame_count() const { return m_frame_count; }
private:
QOpenGLShaderProgram *m_shader;
QString m_path;
GLint m_vertex_coord;
GLint m_tex_coord;
GLint m_color;
GLint m_mvp_matrix;
GLint m_input_size;
GLint m_output_size;
GLint m_texture_size;
GLint m_frame_count;
};
class OpenGLOptions : public QObject {
Q_OBJECT
public:
enum RenderBehaviorType { SyncWithVideo,
TargetFramerate };
enum FilterType { Nearest,
Linear };
OpenGLOptions(QObject *parent, bool loadConfig, const QString &glslVersion);
RenderBehaviorType renderBehavior() const { return m_renderBehavior; }
int framerate() const { return m_framerate; }
bool vSync() const { return m_vsync; }
FilterType filter() const;
const QList<OpenGLShaderPass> &shaders() const { return m_shaders; }
void setRenderBehavior(RenderBehaviorType value);
void setFrameRate(int value);
void setVSync(bool value);
void setFilter(FilterType value);
void addShader(const QString &path);
void addDefaultShader();
void save() const;
private:
RenderBehaviorType m_renderBehavior = SyncWithVideo;
int m_framerate = -1;
bool m_vsync = false;
FilterType m_filter = Nearest;
QList<OpenGLShaderPass> m_shaders;
QString m_glslVersion;
};
#endif

View File

@@ -1,116 +0,0 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* OpenGL renderer options dialog for Qt
*
*
*
* Authors: Teemu Korhonen
*
* Copyright 2022 Teemu Korhonen
*/
#include <QFileDialog>
#include <QMessageBox>
#include <QStringBuilder>
#include <stdexcept>
#include "qt_opengloptionsdialog.hpp"
#include "qt_util.hpp"
#include "ui_qt_opengloptionsdialog.h"
OpenGLOptionsDialog::OpenGLOptionsDialog(QWidget *parent, const OpenGLOptions &options, std::function<OpenGLOptions *()> optionsFactory)
: QDialog(parent)
, ui(new Ui::OpenGLOptionsDialog)
, createOptions(optionsFactory)
{
ui->setupUi(this);
if (options.renderBehavior() == OpenGLOptions::SyncWithVideo)
ui->syncWithVideo->setChecked(true);
else {
ui->syncToFramerate->setChecked(true);
ui->targetFps->setValue(options.framerate());
}
ui->vsync->setChecked(options.vSync());
if (!options.shaders().isEmpty()) {
auto path = options.shaders().first().path();
if (!path.isEmpty())
ui->shader->setPlainText(path);
}
}
OpenGLOptionsDialog::~OpenGLOptionsDialog()
{
delete ui;
}
void
OpenGLOptionsDialog::accept()
{
auto options = createOptions();
options->setRenderBehavior(
ui->syncWithVideo->isChecked()
? OpenGLOptions::SyncWithVideo
: OpenGLOptions::TargetFramerate);
options->setFrameRate(ui->targetFps->value());
options->setVSync(ui->vsync->isChecked());
auto shader = ui->shader->toPlainText();
try {
if (!shader.isEmpty())
options->addShader(shader);
else
options->addDefaultShader();
} catch (const std::runtime_error &e) {
delete options;
QMessageBox msgBox(this);
msgBox.setWindowTitle(tr("Shader error"));
msgBox.setText(tr("Could not load shaders."));
msgBox.setInformativeText(tr("More information in details."));
msgBox.setDetailedText(e.what());
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Close);
msgBox.setDefaultButton(QMessageBox::Close);
msgBox.setStyleSheet("QTextEdit { min-width: 45em; }");
msgBox.exec();
return;
}
options->save();
emit optionsChanged(options);
QDialog::accept();
}
void
OpenGLOptionsDialog::on_addShader_clicked()
{
auto shader = QFileDialog::getOpenFileName(
this,
QString(),
QString(),
tr("OpenGL Shaders") % util::DlgFilter({ "glsl" }, true));
if (shader.isNull())
return;
ui->shader->setPlainText(shader);
}

View File

@@ -1,51 +0,0 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Header for OpenGL renderer options dialog
*
* Authors: Teemu Korhonen
*
* Copyright 2022 Teemu Korhonen
*/
#ifndef QT_OPENGLOPTIONSDIALOG_H
#define QT_OPENGLOPTIONSDIALOG_H
#include <QDialog>
#include <functional>
#include "qt_opengloptions.hpp"
namespace Ui {
class OpenGLOptionsDialog;
}
class OpenGLOptionsDialog : public QDialog {
Q_OBJECT
public:
explicit OpenGLOptionsDialog(QWidget *parent, const OpenGLOptions &options, std::function<OpenGLOptions *()> optionsFactory);
~OpenGLOptionsDialog();
signals:
void optionsChanged(OpenGLOptions *options);
public slots:
void accept() override;
private:
Ui::OpenGLOptionsDialog *ui;
std::function<OpenGLOptions *()> createOptions;
private slots:
void on_addShader_clicked();
};
#endif // QT_OPENGLOPTIONSDIALOG_H

View File

@@ -1,280 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OpenGLOptionsDialog</class>
<widget class="QDialog" name="OpenGLOptionsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>320</height>
</rect>
</property>
<property name="windowTitle">
<string>OpenGL 3.0 renderer options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Render behavior</string>
</property>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="3,1">
<item row="1" column="0">
<widget class="QRadioButton" name="syncToFramerate">
<property name="text">
<string>Use target framerate:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="targetFps">
<property name="enabled">
<bool>false</bool>
</property>
<property name="suffix">
<string> fps</string>
</property>
<property name="minimum">
<number>15</number>
</property>
<property name="maximum">
<number>240</number>
</property>
<property name="value">
<number>60</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="vsync">
<property name="text">
<string>VSync</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRadioButton" name="syncWithVideo">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Render each frame immediately, in sync with the emulated display.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;This is the recommended option if the shaders in use don't utilize frametime for animated effects.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Synchronize with video</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QSlider" name="fpsSlider">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>15</number>
</property>
<property name="maximum">
<number>240</number>
</property>
<property name="value">
<number>60</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="tickPosition">
<enum>QSlider::NoTicks</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Shaders</string>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="3,1">
<item row="2" column="1">
<widget class="QPushButton" name="removeShader">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item row="3" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" rowspan="3">
<widget class="QTextEdit" name="shader">
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string>No shader selected</string>
</property>
</widget>
</item>
<item row="1" column="1" alignment="Qt::AlignTop">
<widget class="QPushButton" name="addShader">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>syncWithVideo</tabstop>
<tabstop>syncToFramerate</tabstop>
<tabstop>fpsSlider</tabstop>
<tabstop>targetFps</tabstop>
<tabstop>vsync</tabstop>
<tabstop>shader</tabstop>
<tabstop>addShader</tabstop>
<tabstop>removeShader</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>OpenGLOptionsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>257</x>
<y>310</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>OpenGLOptionsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>325</x>
<y>310</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>syncToFramerate</sender>
<signal>toggled(bool)</signal>
<receiver>targetFps</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>140</x>
<y>71</y>
</hint>
<hint type="destinationlabel">
<x>380</x>
<y>98</y>
</hint>
</hints>
</connection>
<connection>
<sender>syncToFramerate</sender>
<signal>toggled(bool)</signal>
<receiver>fpsSlider</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>158</x>
<y>66</y>
</hint>
<hint type="destinationlabel">
<x>168</x>
<y>87</y>
</hint>
</hints>
</connection>
<connection>
<sender>fpsSlider</sender>
<signal>valueChanged(int)</signal>
<receiver>targetFps</receiver>
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>252</x>
<y>90</y>
</hint>
<hint type="destinationlabel">
<x>308</x>
<y>89</y>
</hint>
</hints>
</connection>
<connection>
<sender>targetFps</sender>
<signal>valueChanged(int)</signal>
<receiver>fpsSlider</receiver>
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>364</x>
<y>93</y>
</hint>
<hint type="destinationlabel">
<x>134</x>
<y>93</y>
</hint>
</hints>
</connection>
<connection>
<sender>removeShader</sender>
<signal>clicked()</signal>
<receiver>shader</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>333</x>
<y>201</y>
</hint>
<hint type="destinationlabel">
<x>235</x>
<y>208</y>
</hint>
</hints>
</connection>
</connections>
</ui>

File diff suppressed because it is too large Load Diff

View File

@@ -11,12 +11,14 @@
* *
* *
* Authors: Teemu Korhonen * Authors: Teemu Korhonen
* Cacodemon345
* *
* Copyright 2022 Teemu Korhonen * Copyright 2022 Teemu Korhonen
* Copyright 2025 Cacodemon345
*/ */
#ifndef QT_OPENGLRENDERER_HPP #ifndef QT_OpenGLRenderer_HPP
#define QT_OPENGLRENDERER_HPP #define QT_OpenGLRenderer_HPP
#if defined Q_OS_MACOS || __arm__ #if defined Q_OS_MACOS || __arm__
# define NO_BUFFER_STORAGE # define NO_BUFFER_STORAGE
@@ -37,12 +39,24 @@
#include <tuple> #include <tuple>
#include <vector> #include <vector>
#include "qt_opengloptions.hpp"
#include "qt_renderercommon.hpp" #include "qt_renderercommon.hpp"
typedef void(QOPENGLF_APIENTRYP PFNGLBUFFERSTORAGEEXTPROC_LOCAL)(GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); extern "C"
{
#include <86box/qt-glslp-parser.h>
}
class OpenGLRenderer : public QWindow, protected QOpenGLExtraFunctions, public RendererCommon { struct render_data {
int pass;
struct glsl_shader *shader;
struct shader_pass *shader_pass;
GLfloat *output_size;
struct shader_pass *orig_pass;
GLint texture;
int frame_count;
};
class OpenGLRenderer : public QWindow, public RendererCommon {
Q_OBJECT Q_OBJECT
public: public:
@@ -56,7 +70,7 @@ public:
void finalize() override final; void finalize() override final;
bool hasOptions() const override { return true; } bool hasOptions() const override { return true; }
QDialog *getOptions(QWidget *parent) override; QDialog *getOptions(QWidget *parent) override;
void reloadOptions() override; bool reloadRendererOption() { return true; }
signals: signals:
void initialized(); void initialized();
@@ -71,47 +85,64 @@ protected:
bool event(QEvent *event) override; bool event(QEvent *event) override;
private: private:
static constexpr int INIT_WIDTH = 640;
static constexpr int INIT_HEIGHT = 400; std::array<std::unique_ptr<uint8_t>, 2> imagebufs;
static constexpr int ROW_LENGTH = 2048;
static constexpr int BUFFERPIXELS = 4194304;
static constexpr int BUFFERBYTES = 16777216; /* Pixel is 4 bytes. */
static constexpr int BUFFERCOUNT = 3; /* How many buffers to use for pixel transfer (2-3 is commonly recommended). */
QTimer *renderTimer; QTimer *renderTimer;
OpenGLOptions *options;
QString glslVersion; QString glslVersion = "";
bool isInitialized = false; bool isInitialized = false;
bool isFinalized = false; bool isFinalized = false;
GLuint unpackBufferID = 0; int max_texture_size = 65536;
GLuint vertexArrayID = 0;
GLuint vertexBufferID = 0;
GLuint textureID = 0;
int frameCounter = 0; int frameCounter = 0;
OpenGLOptions::FilterType currentFilter; QOpenGLExtraFunctions glw;
struct shader_texture scene_texture;
glsl_t *active_shader;
void *unpackBuffer = nullptr; void *unpackBuffer = nullptr;
int glsl_version[2] = { 0, 0 };
void initialize(); void initialize();
void initializeExtensions(); void initializeExtensions();
void initializeBuffers(); void initializeBuffers();
void applyOptions(); void applyOptions();
void applyShader(const OpenGLShaderPass &shader);
bool notReady() const { return !isInitialized || isFinalized; }
/* GL_ARB_buffer_storage */ void create_scene_shader();
bool hasBufferStorage = false; void create_texture(struct shader_texture *tex);
#ifndef NO_BUFFER_STORAGE void create_fbo(struct shader_fbo *fbo);
PFNGLBUFFERSTORAGEEXTPROC_LOCAL glBufferStorage = nullptr; void recreate_fbo(struct shader_fbo *fbo, int width, int height);
#endif void setup_fbo(struct shader *shader, struct shader_fbo *fbo);
bool notReady() const { return !isInitialized || isFinalized; }
glsl_t* load_glslp(glsl_t *glsl, int num_shader, const char *f);
glsl_t* load_shaders(int num, char shaders[MAX_USER_SHADERS][512]);
int compile_shader(GLenum shader_type, const char *prepend, const char *program, int *dst);
int create_default_shader_tex(struct shader_pass *pass);
int create_default_shader_color(struct shader_pass *pass);
int create_program(struct shader_program *program);
GLuint get_uniform(GLuint program, const char *name);
GLuint get_attrib(GLuint program, const char *name);
void find_uniforms(struct glsl_shader *glsl, int num_pass);
void delete_texture(struct shader_texture *tex);
void delete_fbo(struct shader_fbo *fbo);
void delete_program(struct shader_program *program);
void delete_vbo(struct shader_vbo *vbo);
void delete_pass(struct shader_pass *pass);
void delete_prev(struct shader_prev *prev);
void delete_shader(struct glsl_shader *glsl);
void delete_glsl(glsl_t *glsl);
void read_shader_config();
void render_pass(struct render_data *data);
private slots: private slots:
void render(); void render();
void updateOptions(OpenGLOptions *newOptions);
}; };
class opengl_init_error : public std::runtime_error { class opengl_init_error : public std::runtime_error {

View File

@@ -0,0 +1,85 @@
#include "qt_openglshaderconfig.hpp"
#include "ui_qt_openglshaderconfig.h"
#include "qt_mainwindow.hpp"
extern MainWindow* main_window;
extern "C"
{
#include <86box/86box.h>
#include <86box/plat.h>
#include <86box/config.h>
}
OpenGLShaderConfig::OpenGLShaderConfig(QWidget *parent, glslp_t* shader)
: QDialog(parent)
, ui(new Ui::OpenGLShaderConfig)
{
ui->setupUi(this);
currentShader = shader;
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
glslp_read_shader_config(currentShader);
for (int i = 0; i < currentShader->num_parameters; i++) {
auto spinBox = new QDoubleSpinBox;
spinBox->setObjectName(currentShader->parameters[i].id);
spinBox->setRange(currentShader->parameters[i].min, currentShader->parameters[i].max);
spinBox->setValue(currentShader->parameters[i].value);
spinBox->setSingleStep(currentShader->parameters[i].step);
QFormLayout* layout = (QFormLayout*)ui->scrollAreaWidgetContents->layout();
layout->addRow(currentShader->parameters[i].description, spinBox);
}
}
OpenGLShaderConfig::~OpenGLShaderConfig()
{
delete ui;
}
void OpenGLShaderConfig::on_buttonBox_clicked(QAbstractButton *button)
{
if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::ResetRole) {
for (int i = 0; i < currentShader->num_parameters; i++) {
QDoubleSpinBox* box = this->findChild<QDoubleSpinBox*>(QString(currentShader->parameters[i].id));
if (box) {
box->setValue(currentShader->parameters[i].default_value);
}
}
} else if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::ApplyRole) {
startblit();
for (int i = 0; i < currentShader->num_parameters; i++) {
QDoubleSpinBox* box = this->findChild<QDoubleSpinBox*>(QString(currentShader->parameters[i].id));
if (box) {
float val = (float)box->value();
currentShader->parameters[i].value = val;
}
}
glslp_write_shader_config(currentShader);
config_save();
endblit();
main_window->reloadAllRenderers();
}
}
void OpenGLShaderConfig::on_OpenGLShaderConfig_accepted()
{
startblit();
for (int i = 0; i < currentShader->num_parameters; i++) {
QDoubleSpinBox* box = (QDoubleSpinBox*)this->findChild<QDoubleSpinBox*>(QString(currentShader->parameters[i].id));
if (box) {
float val = (float)box->value();
currentShader->parameters[i].value = val;
}
}
glslp_write_shader_config(currentShader);
config_save();
endblit();
main_window->reloadAllRenderers();
}

View File

@@ -0,0 +1,40 @@
#ifndef QT_OPENGLSHADERCONFIG_HPP
#define QT_OPENGLSHADERCONFIG_HPP
#include <QDialog>
#include <QLabel>
#include <QDoubleSpinBox>
#include <QAbstractButton>
#include <map>
#include <string>
extern "C"
{
#include <86box/qt-glslp-parser.h>
}
namespace Ui {
class OpenGLShaderConfig;
}
class OpenGLShaderConfig : public QDialog {
Q_OBJECT
public:
explicit OpenGLShaderConfig(QWidget *parent = nullptr, glslp_t* shader = nullptr);
~OpenGLShaderConfig();
private slots:
void on_buttonBox_clicked(QAbstractButton *button);
void on_OpenGLShaderConfig_accepted();
private:
Ui::OpenGLShaderConfig *ui;
glslp_t* currentShader;
std::map<std::string, double> defaultValues;
};
#endif // QT_OPENGLSHADERCONFIG_HPP

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OpenGLShaderConfig</class>
<widget class="QDialog" name="OpenGLShaderConfig">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Shader Configuration</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetMinAndMaxSize</enum>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>380</width>
<height>250</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetMaximumSize</enum>
</property>
<property name="fieldGrowthPolicy">
<enum>QFormLayout::FieldGrowthPolicy::AllNonFixedFieldsGrow</enum>
</property>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok|QDialogButtonBox::StandardButton::Reset</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>OpenGLShaderConfig</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>OpenGLShaderConfig</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,260 @@
#include "qt_openglshadermanagerdialog.hpp"
#include "ui_qt_openglshadermanagerdialog.h"
#include "qt_mainwindow.hpp"
extern MainWindow* main_window;
#include "qt_openglshaderconfig.hpp"
#include <QListWidgetItem>
#include <QFileDialog>
#include <QMessageBox>
extern "C" {
#include <86box/86box.h>
#include <86box/plat.h>
#include <86box/video.h>
#include <86box/path.h>
#include <86box/ini.h>
#include <86box/config.h>
#include <86box/qt-glslp-parser.h>
extern char gl3_shader_file[MAX_USER_SHADERS][512];
}
OpenGLShaderManagerDialog::OpenGLShaderManagerDialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::OpenGLShaderManagerDialog)
{
ui->setupUi(this);
ui->checkBoxVSync->setChecked(!!video_vsync);
ui->radioButtonVideoSync->setChecked(video_framerate == -1);
ui->radioButtonTargetFramerate->setChecked(video_framerate != -1);
if (video_framerate != -1) {
ui->targetFrameRate->setValue(video_framerate);
} else {
ui->targetFrameRate->setDisabled(true);
}
for (int i = 0; i < MAX_USER_SHADERS; i++) {
if (gl3_shader_file[i][0] != 0) {
char* filename = path_get_filename(gl3_shader_file[i]);
if (filename[0] != 0) {
glslp_t* shaderfile = glslp_parse(gl3_shader_file[i]);
if (shaderfile) {
QListWidgetItem* item = new QListWidgetItem(ui->shaderListWidget);
item->setText(filename);
item->setData(Qt::UserRole + 1, QString(gl3_shader_file[i]));
item->setData(Qt::UserRole + 2, (uintptr_t)shaderfile);
}
}
}
}
if (ui->shaderListWidget->count()) {
ui->shaderListWidget->setCurrentRow(ui->shaderListWidget->count() - 1);
auto current = ui->shaderListWidget->currentItem();
if (current) {
glslp_t* shader = (glslp_t*)current->data(Qt::UserRole + 2).toULongLong();
if (shader->num_parameters > 0)
ui->buttonConfigure->setEnabled(true);
else
ui->buttonConfigure->setEnabled(false);
} else {
ui->buttonConfigure->setEnabled(false);
}
} else {
ui->buttonRemove->setDisabled(true);
ui->buttonMoveUp->setDisabled(true);
ui->buttonMoveDown->setDisabled(true);
ui->buttonConfigure->setDisabled(true);
}
}
OpenGLShaderManagerDialog::~OpenGLShaderManagerDialog()
{
for (int i = 0; i < ui->shaderListWidget->count(); i++) {
if (ui->shaderListWidget->item(i) && ui->shaderListWidget->item(i)->data(Qt::UserRole + 2).toULongLong()) {
glslp_free((glslp_t*)ui->shaderListWidget->item(i)->data(Qt::UserRole + 2).toULongLong());
}
}
delete ui;
}
void OpenGLShaderManagerDialog::on_buttonBox_clicked(QAbstractButton *button)
{
if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) {
accept();
} else if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::RejectRole) {
reject();
} else if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::ApplyRole) {
on_OpenGLShaderManagerDialog_accepted();
main_window->reloadAllRenderers();
}
}
void OpenGLShaderManagerDialog::on_buttonMoveUp_clicked()
{
if (ui->shaderListWidget->currentRow() == 0)
return;
int row = ui->shaderListWidget->currentRow();
auto item = ui->shaderListWidget->takeItem(row);
ui->shaderListWidget->insertItem(row - 1, item);
ui->shaderListWidget->setCurrentItem(item);
}
void OpenGLShaderManagerDialog::on_shaderListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
{
if (current == nullptr) {
ui->buttonRemove->setDisabled(true);
ui->buttonMoveUp->setDisabled(true);
ui->buttonMoveDown->setDisabled(true);
ui->buttonConfigure->setDisabled(true);
return;
} else {
ui->buttonRemove->setDisabled(false);
ui->buttonConfigure->setDisabled(true);
if (current) {
glslp_t* shader = (glslp_t*)current->data(Qt::UserRole + 2).toULongLong();
if (shader->num_parameters > 0)
ui->buttonConfigure->setEnabled(true);
}
}
ui->buttonMoveUp->setDisabled(ui->shaderListWidget->currentRow() == 0);
ui->buttonMoveDown->setDisabled(ui->shaderListWidget->currentRow() == (ui->shaderListWidget->count() - 1));
}
void OpenGLShaderManagerDialog::on_shaderListWidget_currentRowChanged(int currentRow)
{
auto current = ui->shaderListWidget->currentItem();
if (current == nullptr) {
ui->buttonRemove->setDisabled(true);
ui->buttonMoveUp->setDisabled(true);
ui->buttonMoveDown->setDisabled(true);
ui->buttonConfigure->setDisabled(true);
return;
} else {
ui->buttonRemove->setDisabled(false);
ui->buttonConfigure->setDisabled(true);
if (current) {
glslp_t* shader = (glslp_t*)current->data(Qt::UserRole + 2).toULongLong();
if (shader->num_parameters > 0)
ui->buttonConfigure->setEnabled(true);
}
}
ui->buttonMoveUp->setDisabled(ui->shaderListWidget->currentRow() == 0);
ui->buttonMoveDown->setDisabled(ui->shaderListWidget->currentRow() == (ui->shaderListWidget->count() - 1));
}
void OpenGLShaderManagerDialog::on_buttonMoveDown_clicked()
{
if (ui->shaderListWidget->currentRow() == (ui->shaderListWidget->count() - 1))
return;
int row = ui->shaderListWidget->currentRow();
auto item = ui->shaderListWidget->takeItem(row);
ui->shaderListWidget->insertItem(row + 1, item);
ui->shaderListWidget->setCurrentItem(item);
}
void OpenGLShaderManagerDialog::on_buttonAdd_clicked()
{
auto res = QFileDialog::getOpenFileName(this, QString(), QString(), "GLSL Shaders (*.glslp *.glsl);;All files (*.*)");
if (!res.isEmpty()) {
auto glslp_file = res.toUtf8();
glslp_t* shaderfile = glslp_parse(glslp_file.data());
if (shaderfile) {
auto filename = path_get_filename(glslp_file.data());
QListWidgetItem* item = new QListWidgetItem(ui->shaderListWidget);
item->setText(filename);
item->setData(Qt::UserRole + 1, res);
item->setData(Qt::UserRole + 2, (uintptr_t)shaderfile);
if (ui->shaderListWidget->count()) {
ui->shaderListWidget->setCurrentRow(ui->shaderListWidget->count() - 1);
}
} else {
QMessageBox::critical(this, tr("GLSL error"), tr("Could not load filename %1").arg(res));
}
}
}
void OpenGLShaderManagerDialog::on_buttonRemove_clicked()
{
if (ui->shaderListWidget->currentItem()) {
auto item = ui->shaderListWidget->takeItem(ui->shaderListWidget->currentRow());
if (item->data(Qt::UserRole + 2).toULongLong()) {
glslp_free((glslp_t*)item->data(Qt::UserRole + 2).toULongLong());
}
delete item;
on_shaderListWidget_currentRowChanged(ui->shaderListWidget->currentRow());
}
}
void OpenGLShaderManagerDialog::on_OpenGLShaderManagerDialog_accepted()
{
memset(gl3_shader_file, 0, sizeof(gl3_shader_file));
for (int i = 0; i < ui->shaderListWidget->count(); i++) {
strncpy(gl3_shader_file[i], ui->shaderListWidget->item(i)->data(Qt::UserRole + 1).toString().toUtf8(), 512);
}
startblit();
video_vsync = ui->checkBoxVSync->isChecked();
if (ui->radioButtonTargetFramerate->isChecked()) {
video_framerate = ui->horizontalSliderFramerate->value();
} else {
video_framerate = -1;
}
config_save();
endblit();
}
void OpenGLShaderManagerDialog::on_buttonConfigure_clicked()
{
auto item = ui->shaderListWidget->currentItem();
if (item) {
glslp_t* shader = (glslp_t*)item->data(Qt::UserRole + 2).toULongLong();
auto configDialog = new OpenGLShaderConfig(this, shader);
configDialog->exec();
}
}
void OpenGLShaderManagerDialog::on_radioButtonVideoSync_clicked()
{
ui->targetFrameRate->setDisabled(true);
}
void OpenGLShaderManagerDialog::on_radioButtonTargetFramerate_clicked()
{
ui->targetFrameRate->setDisabled(false);
}
void OpenGLShaderManagerDialog::on_horizontalSliderFramerate_sliderMoved(int position)
{
(void)position;
if (ui->horizontalSliderFramerate->value() != ui->targetFrameRate->value())
ui->targetFrameRate->setValue(ui->horizontalSliderFramerate->value());
}
void OpenGLShaderManagerDialog::on_targetFrameRate_valueChanged(int arg1)
{
(void)arg1;
if (ui->horizontalSliderFramerate->value() != ui->targetFrameRate->value())
ui->horizontalSliderFramerate->setValue(ui->targetFrameRate->value());
}

View File

@@ -0,0 +1,50 @@
#ifndef QT_OPENGLSHADERMANAGERDIALOG_H
#define QT_OPENGLSHADERMANAGERDIALOG_H
#include <QDialog>
#include <QAbstractButton>
#include <QListWidgetItem>
namespace Ui {
class OpenGLShaderManagerDialog;
}
class OpenGLShaderManagerDialog : public QDialog {
Q_OBJECT
public:
explicit OpenGLShaderManagerDialog(QWidget *parent = nullptr);
~OpenGLShaderManagerDialog();
private slots:
void on_buttonBox_clicked(QAbstractButton *button);
void on_buttonMoveUp_clicked();
void on_shaderListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous);
void on_shaderListWidget_currentRowChanged(int currentRow);
void on_buttonMoveDown_clicked();
void on_buttonAdd_clicked();
void on_buttonRemove_clicked();
void on_OpenGLShaderManagerDialog_accepted();
void on_buttonConfigure_clicked();
void on_radioButtonVideoSync_clicked();
void on_radioButtonTargetFramerate_clicked();
void on_horizontalSliderFramerate_sliderMoved(int position);
void on_targetFrameRate_valueChanged(int arg1);
private:
Ui::OpenGLShaderManagerDialog *ui;
};
#endif // QT_OPENGLSHADERMANAGERDIALOG_H

View File

@@ -0,0 +1,204 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OpenGLShaderManagerDialog</class>
<widget class="QDialog" name="OpenGLShaderManagerDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>465</height>
</rect>
</property>
<property name="windowTitle">
<string>Shader Manager</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetFixedSize</enum>
</property>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBoxShaders">
<property name="title">
<string>Shaders</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetFixedSize</enum>
</property>
<item>
<widget class="QListWidget" name="shaderListWidget">
<property name="dragDropMode">
<enum>QAbstractItemView::DragDropMode::InternalMove</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectionBehavior::SelectItems</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetFixedSize</enum>
</property>
<item>
<widget class="QPushButton" name="buttonAdd">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonRemove">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonConfigure">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonMoveUp">
<property name="text">
<string>Move up</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonMoveDown">
<property name="text">
<string>Move down</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
<property name="centerButtons">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Render behavior</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item row="1" column="0">
<widget class="QRadioButton" name="radioButtonTargetFramerate">
<property name="text">
<string>Use target framerate:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QSlider" name="horizontalSliderFramerate">
<property name="minimum">
<number>15</number>
</property>
<property name="maximum">
<number>240</number>
</property>
<property name="value">
<number>60</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="invertedControls">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRadioButton" name="radioButtonVideoSync">
<property name="text">
<string>Synchronize with video</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxVSync">
<property name="text">
<string>VSync</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="targetFrameRate">
<property name="suffix">
<string> fps</string>
</property>
<property name="minimum">
<number>15</number>
</property>
<property name="maximum">
<number>240</number>
</property>
<property name="value">
<number>60</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -385,6 +385,7 @@ plat_munmap(void *ptr, size_t size)
#endif #endif
} }
extern bool cpu_thread_running;
void void
plat_pause(int p) plat_pause(int p)
{ {
@@ -392,6 +393,10 @@ plat_pause(int p)
wchar_t title[1024]; wchar_t title[1024];
wchar_t paused_msg[512]; wchar_t paused_msg[512];
if (!cpu_thread_running && p == 1) {
p = 2;
}
if ((!!p) == dopause) { if ((!!p) == dopause) {
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
if (source_hwnd) if (source_hwnd)

View File

@@ -34,6 +34,8 @@ public:
virtual QDialog *getOptions(QWidget *parent) { return nullptr; } virtual QDialog *getOptions(QWidget *parent) { return nullptr; }
/* Reloads options of renderer */ /* Reloads options of renderer */
virtual void reloadOptions() { } virtual void reloadOptions() { }
/* Make the renderer reload itself */
virtual bool reloadRendererOption() { return false; }
int r_monitor_index = 0; int r_monitor_index = 0;

View File

@@ -337,6 +337,26 @@ RendererStack::createRenderer(Renderer renderer)
current.reset(this->createWindowContainer(hw, this)); current.reset(this->createWindowContainer(hw, this));
break; break;
} }
case Renderer::OpenGL3PCem:
{
this->createWinId();
auto hw = new OpenGLRenderer(this);
rendererWindow = hw;
connect(this, &RendererStack::blitToRenderer, hw, &OpenGLRenderer::onBlit, Qt::QueuedConnection);
connect(hw, &OpenGLRenderer::initialized, [=]() {
/* Buffers are available only after initialization. */
imagebufs = rendererWindow->getBuffers();
endblit();
emit rendererChanged();
});
connect(hw, &OpenGLRenderer::errorInitializing, [=]() {
/* Renderer not could initialize, fallback to software. */
imagebufs = {};
QTimer::singleShot(0, this, [this]() { switchRenderer(Renderer::Software); });
});
current.reset(this->createWindowContainer(hw, this));
break;
}
case Renderer::OpenGL3: case Renderer::OpenGL3:
{ {
this->createWinId(); this->createWinId();
@@ -406,7 +426,7 @@ RendererStack::createRenderer(Renderer renderer)
currentBuf = 0; currentBuf = 0;
if (renderer != Renderer::OpenGL3 && renderer != Renderer::Vulkan) { if (renderer != Renderer::OpenGL3 && renderer != Renderer::Vulkan && renderer != Renderer::OpenGL3PCem) {
imagebufs = rendererWindow->getBuffers(); imagebufs = rendererWindow->getBuffers();
endblit(); endblit();
emit rendererChanged(); emit rendererChanged();

View File

@@ -59,6 +59,7 @@ public:
OpenGLES, OpenGLES,
OpenGL3, OpenGL3,
Vulkan, Vulkan,
OpenGL3PCem = 6,
None = -1 None = -1
}; };
void switchRenderer(Renderer renderer); void switchRenderer(Renderer renderer);
@@ -69,6 +70,8 @@ public:
void reloadOptions() const { return rendererWindow->reloadOptions(); } void reloadOptions() const { return rendererWindow->reloadOptions(); }
/* Returns options dialog for current renderer */ /* Returns options dialog for current renderer */
QDialog *getOptions(QWidget *parent) { return rendererWindow ? rendererWindow->getOptions(parent) : nullptr; } QDialog *getOptions(QWidget *parent) { return rendererWindow ? rendererWindow->getOptions(parent) : nullptr; }
/* Reload the renderer itself */
bool reloadRendererOption() { return rendererWindow ? rendererWindow->reloadRendererOption() : false; }
void setFocusRenderer(); void setFocusRenderer();
void onResize(int width, int height); void onResize(int width, int height);