win_opengl: Add loading shaders from a file and few common uniforms.

plat.h,win.c: Add missing const
This commit is contained in:
ts-korhonen
2021-04-15 16:36:02 +03:00
parent 4e6ea33594
commit afc5122a50
3 changed files with 249 additions and 69 deletions

View File

@@ -93,7 +93,7 @@ extern char *plat_get_basename(const char *path);
extern void plat_get_dirname(char *dest, const char *path); extern void plat_get_dirname(char *dest, const char *path);
extern char *plat_get_filename(char *s); extern char *plat_get_filename(char *s);
extern char *plat_get_extension(char *s); extern char *plat_get_extension(char *s);
extern void plat_append_filename(char *dest, char *s1, char *s2); extern void plat_append_filename(char *dest, const char *s1, const char *s2);
extern void plat_put_backslash(char *s); extern void plat_put_backslash(char *s);
extern void plat_path_slash(char *path); extern void plat_path_slash(char *path);
extern int plat_path_abs(char *path); extern int plat_path_abs(char *path);

View File

@@ -775,7 +775,7 @@ plat_get_extension(char *s)
void void
plat_append_filename(char *dest, char *s1, char *s2) plat_append_filename(char *dest, const char *s1, const char *s2)
{ {
strcpy(dest, s1); strcpy(dest, s1);
plat_path_slash(dest); plat_path_slash(dest);

View File

@@ -15,6 +15,7 @@
* libretro has a sizeable library that could * libretro has a sizeable library that could
* be a good target for compatibility. * be a good target for compatibility.
* (UI) options * (UI) options
* More error handling
* ... * ...
* *
* Authors: Teemu Korhonen * Authors: Teemu Korhonen
@@ -42,8 +43,9 @@
#include <SDL2/SDL_syswm.h> #include <SDL2/SDL_syswm.h>
#include <glad/glad.h> #include <glad/glad.h>
#include <stdatomic.h> #include <stdio.h>
#include <86box/86box.h>
#include <86box/plat.h> #include <86box/plat.h>
#include <86box/video.h> #include <86box/video.h>
#include <86box/win.h> #include <86box/win.h>
@@ -109,7 +111,7 @@ static volatile struct
} resize_info = {}; } resize_info = {};
/** /**
* @brief Identifiers to OpenGL object used. * @brief Identifiers to OpenGL objects and uniforms.
*/ */
typedef struct typedef struct
{ {
@@ -117,7 +119,14 @@ typedef struct
GLuint vertexBufferID; GLuint vertexBufferID;
GLuint textureID; GLuint textureID;
GLuint shader_progID; GLuint shader_progID;
} gl_objects;
/* Uniforms */
GLint input_size;
GLint output_size;
GLint texture_size;
GLint frame_count;
} gl_identifiers;
/** /**
* @brief Userdata to pass onto windows message hook * @brief Userdata to pass onto windows message hook
@@ -131,71 +140,183 @@ typedef struct
/** /**
* @brief Default vertex shader. * @brief Default vertex shader.
*/ */
static const GLchar* v_shader = "#version 330 core\n\ static const GLchar* vertex_shader = "#version 330 core\n\
layout(location = 0) in vec2 pos;\n\ in vec2 VertexCoord;\n\
layout(location = 1) in vec2 tex_in;\n\ in vec2 TexCoord;\n\
out vec2 tex;\n\ out vec2 tex;\n\
void main(){\n\ void main(){\n\
gl_Position = vec4(pos, 0.0, 1.0);\n\ gl_Position = vec4(VertexCoord, 0.0, 1.0);\n\
tex = tex_in;\n\ tex = TexCoord;\n\
}\n"; }\n";
/** /**
* @brief Default fragment shader. * @brief Default fragment shader.
*
* Note: Last two lines in main make it a very simple 50% scanline filter.
*/ */
static const GLchar* f_shader = "#version 330 core\n\ static const GLchar* fragment_shader = "#version 330 core\n\
in vec2 tex;\n\ in vec2 tex;\n\
out vec4 color;\n\ uniform sampler2D texsampler;\n\
uniform sampler2D texs;\n\
void main() {\n\ void main() {\n\
color = texture(texs, tex);\n\ gl_FragColor = texture(texsampler, tex);\n\
if (int(gl_FragCoord.y) % 2 == 0)\n\
color = color * vec4(0.5,0.5,0.5,1.0);\n\
}\n"; }\n";
/** /**
* @brief Load and compile default shaders into a program. * @brief Reads a whole file into a null terminated string.
* @param Path Path to the file relative to executable path.
* @return Pointer to the string or NULL on error. Remember to free() after use.
*/
static char* read_file_to_string(const char* path)
{
char* full_path = (char*)malloc(sizeof(char) * (strlen(path) + strlen(exe_path) + 1));
plat_append_filename(full_path, exe_path, path);
FILE* file_handle = plat_fopen(full_path, "rb");
free(full_path);
if (file_handle != NULL)
{
/* get file size */
fseek(file_handle, 0, SEEK_END);
long file_size = ftell(file_handle);
fseek(file_handle, 0, SEEK_SET);
/* read to buffer and close */
char* content = (char*)malloc(sizeof(char) * (file_size + 1));
size_t length = fread(content, sizeof(char), file_size, file_handle);
fclose(file_handle);
content[length] = 0;
return content;
}
return NULL;
}
/**
* @brief Compile custom shaders into a program.
* @return Shader program identifier. * @return Shader program identifier.
*/ */
static GLuint load_shaders() static GLuint load_custom_shaders()
{ {
GLuint vs_id = glCreateShader(GL_VERTEX_SHADER); GLint status = GL_FALSE;
GLuint fs_id = glCreateShader(GL_FRAGMENT_SHADER);
GLint compile_status = GL_FALSE;
int info_log_length; int info_log_length;
char info_log_text[200];
glShaderSource(vs_id, 1, &v_shader, NULL); /* TODO: get path from config */
glCompileShader(vs_id); char* shader = read_file_to_string("shaders/shader.glsl");
glGetShaderiv(vs_id, GL_COMPILE_STATUS, &compile_status); if (shader != NULL)
glGetShaderiv(vs_id, GL_INFO_LOG_LENGTH, &info_log_length); {
int success = 1;
glGetShaderInfoLog(vs_id, info_log_length, NULL, info_log_text); const char* vertex_sources[2] = { "#define VERTEX\n", shader };
const char* fragment_sources[2] = { "#define FRAGMENT\n", shader };
glShaderSource(fs_id, 1, &f_shader, NULL); GLuint vertex_id = glCreateShader(GL_VERTEX_SHADER);
glCompileShader(fs_id); GLuint fragment_id = glCreateShader(GL_FRAGMENT_SHADER);
glGetShaderiv(fs_id, GL_COMPILE_STATUS, &compile_status); glShaderSource(vertex_id, 2, vertex_sources, NULL);
glGetShaderiv(fs_id, GL_INFO_LOG_LENGTH, &info_log_length); glCompileShader(vertex_id);
glGetShaderiv(vertex_id, GL_COMPILE_STATUS, &status);
glGetShaderInfoLog(fs_id, info_log_length, NULL, info_log_text); if (status == GL_FALSE)
{
glGetShaderiv(vertex_id, GL_INFO_LOG_LENGTH, &info_log_length);
GLchar* info_log_text = (GLchar*)malloc(sizeof(GLchar) * info_log_length);
glGetShaderInfoLog(vertex_id, info_log_length, NULL, info_log_text);
/* TODO: error logging */
free(info_log_text);
success = 0;
}
glShaderSource(fragment_id, 2, fragment_sources, NULL);
glCompileShader(fragment_id);
glGetShaderiv(fragment_id, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
glGetShaderiv(fragment_id, GL_INFO_LOG_LENGTH, &info_log_length);
GLchar* info_log_text = (GLchar*)malloc(sizeof(GLchar) * info_log_length);
glGetShaderInfoLog(fragment_id, info_log_length, NULL, info_log_text);
/* TODO: error logging */
free(info_log_text);
success = 0;
}
free(shader);
GLuint prog_id = 0;
if (success)
{
prog_id = glCreateProgram();
glAttachShader(prog_id, vertex_id);
glAttachShader(prog_id, fragment_id);
glLinkProgram(prog_id);
glGetProgramiv(prog_id, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
glGetProgramiv(prog_id, GL_INFO_LOG_LENGTH, &info_log_length);
GLchar* info_log_text = (GLchar*)malloc(sizeof(GLchar) * info_log_length);
glGetProgramInfoLog(prog_id, info_log_length, NULL, info_log_text);
/* TODO: error logging */
free(info_log_text);
}
glDetachShader(prog_id, vertex_id);
glDetachShader(prog_id, fragment_id);
}
glDeleteShader(vertex_id);
glDeleteShader(fragment_id);
return prog_id;
}
return 0;
}
/**
* @brief Compile default shaders into a program.
* @return Shader program identifier.
*/
static GLuint load_default_shaders()
{
GLuint vertex_id = glCreateShader(GL_VERTEX_SHADER);
GLuint fragment_id = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vertex_id, 1, &vertex_shader, NULL);
glCompileShader(vertex_id);
glShaderSource(fragment_id, 1, &fragment_shader, NULL);
glCompileShader(fragment_id);
GLuint prog_id = glCreateProgram(); GLuint prog_id = glCreateProgram();
glAttachShader(prog_id, vs_id); glAttachShader(prog_id, vertex_id);
glAttachShader(prog_id, fs_id); glAttachShader(prog_id, fragment_id);
glLinkProgram(prog_id); glLinkProgram(prog_id);
glDetachShader(prog_id, vs_id); glDetachShader(prog_id, vertex_id);
glDetachShader(prog_id, fs_id); glDetachShader(prog_id, fragment_id);
glDeleteShader(vs_id); glDeleteShader(vertex_id);
glDeleteShader(fs_id); glDeleteShader(fragment_id);
return prog_id; return prog_id;
} }
@@ -300,56 +421,93 @@ static void winmessage_hook(void* userdata, void* hWnd, unsigned int message, Ui
* @brief Initialize OpenGL context * @brief Initialize OpenGL context
* @return Object identifiers * @return Object identifiers
*/ */
static gl_objects initialize_glcontext() static gl_identifiers initialize_glcontext()
{ {
/* Vertex and texture 2d coordinates making a quad as triangle strip */ /* Vertex, texture 2d coordinates and color (white) making a quad as triangle strip */
static const GLfloat surface[] = { static const GLfloat surface[] = {
-1.f, 1.f, 0.f, 0.f, -1.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f,
1.f, 1.f, 1.f, 0.f, 1.f, 1.f, 1.f, 0.f, 1.f, 1.f, 1.f, 1.f,
-1.f, -1.f, 0.f, 1.f, -1.f, -1.f, 0.f, 1.f, 1.f, 1.f, 1.f, 1.f,
1.f, -1.f, 1.f, 1.f 1.f, -1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f
}; };
gl_objects obj = {}; gl_identifiers gl = {};
glGenVertexArrays(1, &obj.vertexArrayID); glGenVertexArrays(1, &gl.vertexArrayID);
glBindVertexArray(obj.vertexArrayID); glBindVertexArray(gl.vertexArrayID);
glGenBuffers(1, &obj.vertexBufferID); glGenBuffers(1, &gl.vertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, obj.vertexBufferID); glBindBuffer(GL_ARRAY_BUFFER, gl.vertexBufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(surface), surface, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(surface), surface, GL_STATIC_DRAW);
glGenTextures(1, &obj.textureID); glGenTextures(1, &gl.textureID);
glBindTexture(GL_TEXTURE_2D, obj.textureID); glBindTexture(GL_TEXTURE_2D, gl.textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); GLfloat border_color[] = { 0.f, 0.f, 0.f, 1.f };
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, INIT_WIDTH, INIT_HEIGHT, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
glClearColor(0.f, 0.f, 0.f, 1.f); glClearColor(0.f, 0.f, 0.f, 1.f);
obj.shader_progID = load_shaders(); gl.shader_progID = load_custom_shaders();
glUseProgram(obj.shader_progID); if (gl.shader_progID == 0)
gl.shader_progID = load_default_shaders();
glEnableVertexAttribArray(0); glUseProgram(gl.shader_progID);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
glEnableVertexAttribArray(1); GLint vertex_coord = glGetAttribLocation(gl.shader_progID, "VertexCoord");
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat))); if (vertex_coord != -1)
{
glEnableVertexAttribArray(vertex_coord);
glVertexAttribPointer(vertex_coord, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 0);
}
return obj; GLint tex_coord = glGetAttribLocation(gl.shader_progID, "TexCoord");
if (tex_coord != -1)
{
glEnableVertexAttribArray(tex_coord);
glVertexAttribPointer(tex_coord, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
}
GLint color = glGetAttribLocation(gl.shader_progID, "Color");
if (color != -1)
{
glEnableVertexAttribArray(color);
glVertexAttribPointer(color, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(4 * sizeof(GLfloat)));
}
GLint mvp_matrix = glGetUniformLocation(gl.shader_progID, "MVPMatrix");
if (mvp_matrix != -1)
{
static const GLfloat mvp[] = {
1.f, 0.f, 0.f, 0.f,
0.f, 1.f, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
0.f, 0.f, 0.f, 1.f
};
glUniformMatrix4fv(mvp_matrix, 1, GL_FALSE, mvp);
}
gl.input_size = glGetUniformLocation(gl.shader_progID, "InputSize");
gl.output_size = glGetUniformLocation(gl.shader_progID, "OutputSize");
gl.texture_size = glGetUniformLocation(gl.shader_progID, "TextureSize");
gl.frame_count = glGetUniformLocation(gl.shader_progID, "FrameCount");
return gl;
} }
/** /**
* @brief Clean up OpenGL context * @brief Clean up OpenGL context
* @param Object identifiers from initialize * @param Object identifiers from initialize
*/ */
static void finalize_glcontext(gl_objects obj) static void finalize_glcontext(gl_identifiers obj)
{ {
glDeleteProgram(obj.shader_progID); glDeleteProgram(obj.shader_progID);
glDeleteTextures(1, &obj.textureID); glDeleteTextures(1, &obj.textureID);
@@ -393,14 +551,22 @@ static void opengl_main()
gladLoadGLLoader(SDL_GL_GetProcAddress); gladLoadGLLoader(SDL_GL_GetProcAddress);
gl_objects obj = initialize_glcontext(); gl_identifiers gl = initialize_glcontext();
int frame_counter = 0;
if (gl.frame_count != -1)
glUniform1i(gl.frame_count, 0);
/* Render loop */ /* Render loop */
int closing = 0; int closing = 0;
while (!closing) while (!closing)
{ {
/* Now redrawing only after a blit. For some shaders to work properly redraw should be in sync with (emulated) display refresh rate. */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
SDL_GL_SwapWindow(window); SDL_GL_SwapWindow(window);
DWORD wait_result = WAIT_TIMEOUT; DWORD wait_result = WAIT_TIMEOUT;
@@ -430,11 +596,11 @@ static void opengl_main()
} }
else if (sync_event == sync_objects.blit_waiting) else if (sync_event == sync_objects.blit_waiting)
{ {
/* Resize the texture if */ /* Resize the texture */
if (blit_info.resized) if (blit_info.resized)
{ {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, blit_info.w, blit_info.h, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, blit_info.w, blit_info.h, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
video_width = blit_info.w; video_width = blit_info.w;
video_height = blit_info.h; video_height = blit_info.h;
@@ -447,6 +613,14 @@ static void opengl_main()
/* Signal that we're done with the video buffer */ /* Signal that we're done with the video buffer */
SetEvent(blit_done); SetEvent(blit_done);
/* Update uniforms */
if (gl.input_size != -1)
glUniform2f(gl.input_size, video_width, video_height);
if (gl.texture_size != -1)
glUniform2f(gl.texture_size, video_width, video_height);
if (gl.frame_count != -1)
glUniform1i(gl.frame_count, frame_counter = (frame_counter + 1) & 1023);
} }
else if (sync_event == sync_objects.resize) else if (sync_event == sync_objects.resize)
{ {
@@ -502,6 +676,9 @@ static void opengl_main()
} }
glViewport(pad_x / 2, pad_y / 2, width - pad_x, height - pad_y); glViewport(pad_x / 2, pad_y / 2, width - pad_x, height - pad_y);
if (gl.output_size != -1)
glUniform2f(gl.output_size, width - pad_x, height - pad_y);
} }
else else
{ {
@@ -511,11 +688,14 @@ static void opengl_main()
SetWindowPos(window_hwnd, parent, 0, 0, resize_info.width, resize_info.height, SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOACTIVATE); SetWindowPos(window_hwnd, parent, 0, 0, resize_info.width, resize_info.height, SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOACTIVATE);
glViewport(0, 0, resize_info.width, resize_info.height); glViewport(0, 0, resize_info.width, resize_info.height);
if (gl.output_size != -1)
glUniform2f(gl.output_size, resize_info.width, resize_info.height);
} }
} }
} }
finalize_glcontext(obj); finalize_glcontext(gl);
SDL_GL_DeleteContext(context); SDL_GL_DeleteContext(context);