2021-04-10 01:35:17 +03:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* Rendering module for OpenGL
|
|
|
|
|
*
|
2021-04-16 18:36:59 +03:00
|
|
|
* TODO: More shader features
|
|
|
|
|
* - scaling
|
|
|
|
|
* - multipass
|
|
|
|
|
* - previous frames
|
2021-04-10 17:41:51 +03:00
|
|
|
* (UI) options
|
2021-04-15 16:36:02 +03:00
|
|
|
* More error handling
|
2021-04-10 17:41:51 +03:00
|
|
|
*
|
2021-04-10 01:35:17 +03:00
|
|
|
* Authors: Teemu Korhonen
|
|
|
|
|
*
|
|
|
|
|
* Copyright 2021 Teemu Korhonen
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
*/
|
|
|
|
|
#define UNICODE
|
|
|
|
|
#include <Windows.h>
|
2021-04-24 01:01:45 +03:00
|
|
|
#include <process.h>
|
2021-04-10 01:35:17 +03:00
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
|
#include <SDL2/SDL_syswm.h>
|
|
|
|
|
#include <glad/glad.h>
|
|
|
|
|
|
2021-04-17 00:12:49 +03:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stdint.h>
|
2021-04-21 01:33:01 +03:00
|
|
|
#include <string.h>
|
2021-04-17 00:12:49 +03:00
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
#if !defined(_MSC_VER) || defined(__clang__)
|
|
|
|
|
#include <stdatomic.h>
|
|
|
|
|
#else
|
|
|
|
|
typedef LONG atomic_flag;
|
|
|
|
|
#define atomic_flag_clear(OBJ) InterlockedExchange(OBJ, 0)
|
|
|
|
|
#define atomic_flag_test_and_set(OBJ) InterlockedExchange(OBJ, 1)
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-04-15 16:36:02 +03:00
|
|
|
#include <86box/86box.h>
|
2021-04-10 01:35:17 +03:00
|
|
|
#include <86box/plat.h>
|
|
|
|
|
#include <86box/video.h>
|
2021-04-12 17:01:58 +03:00
|
|
|
#include <86box/win.h>
|
2021-04-10 01:35:17 +03:00
|
|
|
#include <86box/win_opengl.h>
|
2021-04-16 19:42:02 +03:00
|
|
|
#include <86box/win_opengl_glslp.h>
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-13 15:56:27 +03:00
|
|
|
static const int INIT_WIDTH = 640;
|
|
|
|
|
static const int INIT_HEIGHT = 400;
|
2021-04-29 13:47:05 +03:00
|
|
|
static const int BUFFERPIXELS = 4460544; /* Same size as render_buffer, pow(2048+64,2). */
|
|
|
|
|
static const int BUFFERBYTES = 17842176; /* Pixel is 4 bytes. */
|
|
|
|
|
static const int BUFFERCOUNT = 3; /* How many buffers to use for pixel transfer (2-3 is commonly recommended). */
|
2021-09-14 08:56:26 +03:00
|
|
|
static const int ROW_LENGTH = 2112; /* Source buffer row lenght (including padding) */
|
2021-04-13 15:56:27 +03:00
|
|
|
|
2021-04-10 17:41:51 +03:00
|
|
|
/**
|
|
|
|
|
* @brief A dedicated OpenGL thread.
|
|
|
|
|
* OpenGL context's don't handle multiple threads well.
|
|
|
|
|
*/
|
2021-04-10 01:35:17 +03:00
|
|
|
static thread_t* thread = NULL;
|
|
|
|
|
|
2021-04-10 17:41:51 +03:00
|
|
|
/**
|
|
|
|
|
* @brief A window usable with an OpenGL context
|
|
|
|
|
*/
|
2021-04-10 01:35:17 +03:00
|
|
|
static SDL_Window* window = NULL;
|
2021-04-10 17:41:51 +03:00
|
|
|
|
|
|
|
|
/**
|
2021-04-13 01:25:42 +03:00
|
|
|
* @brief SDL window handle
|
2021-04-10 17:41:51 +03:00
|
|
|
*/
|
2021-04-13 01:25:42 +03:00
|
|
|
static HWND window_hwnd = NULL;
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-10 17:41:51 +03:00
|
|
|
/**
|
2021-04-13 01:25:42 +03:00
|
|
|
* @brief Parent window handle (hwndRender from win_ui)
|
2021-04-10 17:41:51 +03:00
|
|
|
*/
|
2021-04-10 01:35:17 +03:00
|
|
|
static HWND parent = NULL;
|
2021-04-10 17:41:51 +03:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Events listened in OpenGL thread.
|
|
|
|
|
*/
|
2021-04-10 01:35:17 +03:00
|
|
|
static union
|
|
|
|
|
{
|
|
|
|
|
struct
|
|
|
|
|
{
|
|
|
|
|
HANDLE closing;
|
|
|
|
|
HANDLE resize;
|
2021-04-21 01:33:01 +03:00
|
|
|
HANDLE reload;
|
2021-04-10 01:35:17 +03:00
|
|
|
HANDLE blit_waiting;
|
|
|
|
|
};
|
2021-04-21 01:33:01 +03:00
|
|
|
HANDLE asArray[4];
|
2021-04-17 00:12:49 +03:00
|
|
|
} sync_objects = { 0 };
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-10 17:41:51 +03:00
|
|
|
/**
|
2021-04-29 13:47:05 +03:00
|
|
|
* @brief Blit event parameters.
|
2021-04-10 17:41:51 +03:00
|
|
|
*/
|
2021-04-29 13:47:05 +03:00
|
|
|
typedef struct
|
|
|
|
|
{
|
2021-09-14 08:56:26 +03:00
|
|
|
int w, h;
|
2021-04-29 13:47:05 +03:00
|
|
|
void* buffer; /* Buffer for pixel transfer, allocated by gpu driver. */
|
|
|
|
|
volatile atomic_flag in_use; /* Is buffer currently in use. */
|
|
|
|
|
GLsync sync; /* Fence sync object used by opengl thread to track pixel transfer completion. */
|
|
|
|
|
} blit_info_t;
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-10 17:41:51 +03:00
|
|
|
/**
|
2021-04-29 13:47:05 +03:00
|
|
|
* @brief Array of blit_infos, one for each buffer.
|
2021-04-10 17:41:51 +03:00
|
|
|
*/
|
2021-04-29 13:47:05 +03:00
|
|
|
static blit_info_t* blit_info = NULL;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Buffer index of next write operation.
|
|
|
|
|
*/
|
|
|
|
|
static int write_pos = 0;
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-11 22:29:00 +03:00
|
|
|
/**
|
|
|
|
|
* @brief Resize event parameters.
|
|
|
|
|
*/
|
2021-04-22 01:15:10 +03:00
|
|
|
static struct
|
2021-04-11 22:29:00 +03:00
|
|
|
{
|
2021-04-13 15:56:27 +03:00
|
|
|
int width, height, fullscreen, scaling_mode;
|
2021-04-22 01:15:10 +03:00
|
|
|
mutex_t* mutex;
|
2021-04-17 00:12:49 +03:00
|
|
|
} resize_info = { 0 };
|
2021-04-11 22:29:00 +03:00
|
|
|
|
2021-04-21 01:33:01 +03:00
|
|
|
/**
|
|
|
|
|
* @brief Renderer options
|
|
|
|
|
*/
|
2021-04-23 16:09:45 +03:00
|
|
|
static struct
|
2021-04-21 01:33:01 +03:00
|
|
|
{
|
|
|
|
|
int vsync; /* Vertical sync; 0 = off, 1 = on */
|
2021-04-23 16:09:45 +03:00
|
|
|
int frametime; /* Frametime in microseconds, or -1 to sync with blitter */
|
2021-04-21 01:33:01 +03:00
|
|
|
char shaderfile[512]; /* Shader file path. Match the length of openfilestring in win_dialog.c */
|
2021-04-23 16:09:45 +03:00
|
|
|
int shaderfile_changed; /* Has shader file path changed. To prevent unnecessary shader recompilation. */
|
2021-05-01 20:49:23 +03:00
|
|
|
int filter; /* 0 = Nearest, 1 = Linear */
|
|
|
|
|
int filter_changed; /* Has filter changed. */
|
2021-04-23 16:09:45 +03:00
|
|
|
mutex_t* mutex;
|
2021-04-21 01:33:01 +03:00
|
|
|
} options = { 0 };
|
|
|
|
|
|
2021-04-10 17:41:51 +03:00
|
|
|
/**
|
2021-04-15 16:36:02 +03:00
|
|
|
* @brief Identifiers to OpenGL objects and uniforms.
|
2021-04-10 17:41:51 +03:00
|
|
|
*/
|
2021-04-10 01:35:17 +03:00
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
GLuint vertexArrayID;
|
|
|
|
|
GLuint vertexBufferID;
|
|
|
|
|
GLuint textureID;
|
2021-04-29 13:47:05 +03:00
|
|
|
GLuint unpackBufferID;
|
2021-04-10 01:35:17 +03:00
|
|
|
GLuint shader_progID;
|
2021-04-15 16:36:02 +03:00
|
|
|
|
|
|
|
|
/* Uniforms */
|
|
|
|
|
|
|
|
|
|
GLint input_size;
|
|
|
|
|
GLint output_size;
|
|
|
|
|
GLint texture_size;
|
|
|
|
|
GLint frame_count;
|
|
|
|
|
} gl_identifiers;
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-10 17:41:51 +03:00
|
|
|
/**
|
|
|
|
|
* @brief Set or unset OpenGL context window as a child window.
|
|
|
|
|
*
|
2021-04-11 22:29:00 +03:00
|
|
|
* Modifies the window style and sets the parent window.
|
|
|
|
|
* WS_EX_NOACTIVATE keeps the window from stealing input focus.
|
2021-04-10 17:41:51 +03:00
|
|
|
*/
|
2021-04-12 00:53:24 +03:00
|
|
|
static void set_parent_binding(int enable)
|
2021-04-10 01:35:17 +03:00
|
|
|
{
|
2021-04-13 01:25:42 +03:00
|
|
|
long style = GetWindowLong(window_hwnd, GWL_STYLE);
|
|
|
|
|
long ex_style = GetWindowLong(window_hwnd, GWL_EXSTYLE);
|
2021-04-11 22:29:00 +03:00
|
|
|
|
2021-04-10 01:35:17 +03:00
|
|
|
if (enable)
|
2021-04-11 22:29:00 +03:00
|
|
|
{
|
2021-04-10 01:35:17 +03:00
|
|
|
style |= WS_CHILD;
|
2021-04-11 22:29:00 +03:00
|
|
|
ex_style |= WS_EX_NOACTIVATE;
|
|
|
|
|
}
|
2021-04-10 01:35:17 +03:00
|
|
|
else
|
2021-04-11 22:29:00 +03:00
|
|
|
{
|
2021-04-10 01:35:17 +03:00
|
|
|
style &= ~WS_CHILD;
|
2021-04-11 22:29:00 +03:00
|
|
|
ex_style &= ~WS_EX_NOACTIVATE;
|
|
|
|
|
}
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-13 01:25:42 +03:00
|
|
|
SetWindowLong(window_hwnd, GWL_STYLE, style);
|
|
|
|
|
SetWindowLong(window_hwnd, GWL_EXSTYLE, ex_style);
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-13 01:25:42 +03:00
|
|
|
SetParent(window_hwnd, enable ? parent : NULL);
|
2021-04-10 01:35:17 +03:00
|
|
|
}
|
|
|
|
|
|
2021-04-12 00:53:24 +03:00
|
|
|
/**
|
2021-04-19 01:05:53 +03:00
|
|
|
* @brief Windows message handler for our window.
|
2021-04-12 00:53:24 +03:00
|
|
|
* @param message
|
|
|
|
|
* @param wParam
|
|
|
|
|
* @param lParam
|
2021-04-19 01:05:53 +03:00
|
|
|
* @param fullscreen
|
2021-04-12 00:53:24 +03:00
|
|
|
*/
|
2021-04-19 01:05:53 +03:00
|
|
|
static void handle_window_messages(UINT message, WPARAM wParam, LPARAM lParam, int fullscreen)
|
2021-04-12 00:53:24 +03:00
|
|
|
{
|
|
|
|
|
switch (message)
|
|
|
|
|
{
|
|
|
|
|
case WM_LBUTTONUP:
|
2021-04-13 23:20:58 +03:00
|
|
|
case WM_LBUTTONDOWN:
|
2021-04-12 00:53:24 +03:00
|
|
|
case WM_MBUTTONUP:
|
2021-04-13 23:20:58 +03:00
|
|
|
case WM_MBUTTONDOWN:
|
2021-04-24 16:13:21 +03:00
|
|
|
case WM_RBUTTONUP:
|
|
|
|
|
case WM_RBUTTONDOWN:
|
2021-04-19 01:05:53 +03:00
|
|
|
if (!fullscreen)
|
2021-04-12 22:21:28 +03:00
|
|
|
{
|
2021-04-24 16:13:21 +03:00
|
|
|
/* Bring main window to front. */
|
|
|
|
|
SetForegroundWindow(GetAncestor(parent, GA_ROOT));
|
|
|
|
|
|
2021-04-12 22:21:28 +03:00
|
|
|
/* Mouse events that enter and exit capture. */
|
|
|
|
|
PostMessage(parent, message, wParam, lParam);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case WM_KEYDOWN:
|
|
|
|
|
case WM_KEYUP:
|
|
|
|
|
case WM_SYSKEYDOWN:
|
|
|
|
|
case WM_SYSKEYUP:
|
2021-04-19 01:05:53 +03:00
|
|
|
if (fullscreen)
|
2021-04-13 01:25:42 +03:00
|
|
|
{
|
2021-04-12 22:21:28 +03:00
|
|
|
PostMessage(parent, message, wParam, lParam);
|
2021-04-13 01:25:42 +03:00
|
|
|
}
|
2021-04-12 00:53:24 +03:00
|
|
|
break;
|
|
|
|
|
case WM_INPUT:
|
2021-04-19 01:05:53 +03:00
|
|
|
if (fullscreen)
|
2021-04-12 17:01:58 +03:00
|
|
|
{
|
|
|
|
|
/* Raw input handler from win_ui.c : input_proc */
|
|
|
|
|
|
|
|
|
|
UINT size = 0;
|
|
|
|
|
PRAWINPUT raw = NULL;
|
|
|
|
|
|
|
|
|
|
/* Here we read the raw input data */
|
|
|
|
|
GetRawInputData((HRAWINPUT)(LPARAM)lParam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
|
|
|
|
|
raw = (PRAWINPUT)malloc(size);
|
|
|
|
|
if (GetRawInputData((HRAWINPUT)(LPARAM)lParam, RID_INPUT, raw, &size, sizeof(RAWINPUTHEADER)) == size) {
|
|
|
|
|
switch (raw->header.dwType)
|
|
|
|
|
{
|
|
|
|
|
case RIM_TYPEKEYBOARD:
|
|
|
|
|
keyboard_handle(raw);
|
|
|
|
|
break;
|
|
|
|
|
case RIM_TYPEMOUSE:
|
|
|
|
|
win_mouse_handle(raw);
|
|
|
|
|
break;
|
|
|
|
|
case RIM_TYPEHID:
|
|
|
|
|
win_joystick_handle(raw);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free(raw);
|
|
|
|
|
}
|
2021-04-12 00:53:24 +03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-10 17:41:51 +03:00
|
|
|
/**
|
2021-04-23 16:09:45 +03:00
|
|
|
* @brief (Re-)apply shaders to OpenGL context.
|
|
|
|
|
* @param gl Identifiers from initialize
|
2021-04-10 17:41:51 +03:00
|
|
|
*/
|
2021-04-23 16:09:45 +03:00
|
|
|
static void apply_shaders(gl_identifiers* gl)
|
2021-04-10 01:35:17 +03:00
|
|
|
{
|
2021-04-23 16:09:45 +03:00
|
|
|
GLuint old_shader_ID = 0;
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
if (gl->shader_progID != 0)
|
|
|
|
|
old_shader_ID = gl->shader_progID;
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
if (strlen(options.shaderfile) > 0)
|
|
|
|
|
gl->shader_progID = load_custom_shaders(options.shaderfile);
|
|
|
|
|
else
|
|
|
|
|
gl->shader_progID = 0;
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
if (gl->shader_progID == 0)
|
|
|
|
|
gl->shader_progID = load_default_shaders();
|
2021-04-15 16:36:02 +03:00
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
glUseProgram(gl->shader_progID);
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
/* Delete old shader if one exists (changing shader) */
|
|
|
|
|
if (old_shader_ID != 0)
|
|
|
|
|
glDeleteProgram(old_shader_ID);
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
GLint vertex_coord = glGetAttribLocation(gl->shader_progID, "VertexCoord");
|
2021-04-15 16:36:02 +03:00
|
|
|
if (vertex_coord != -1)
|
|
|
|
|
{
|
|
|
|
|
glEnableVertexAttribArray(vertex_coord);
|
|
|
|
|
glVertexAttribPointer(vertex_coord, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 0);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
GLint tex_coord = glGetAttribLocation(gl->shader_progID, "TexCoord");
|
2021-04-15 16:36:02 +03:00
|
|
|
if (tex_coord != -1)
|
|
|
|
|
{
|
|
|
|
|
glEnableVertexAttribArray(tex_coord);
|
|
|
|
|
glVertexAttribPointer(tex_coord, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
|
|
|
|
|
}
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
GLint color = glGetAttribLocation(gl->shader_progID, "Color");
|
2021-04-15 16:36:02 +03:00
|
|
|
if (color != -1)
|
|
|
|
|
{
|
|
|
|
|
glEnableVertexAttribArray(color);
|
|
|
|
|
glVertexAttribPointer(color, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(4 * sizeof(GLfloat)));
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
GLint mvp_matrix = glGetUniformLocation(gl->shader_progID, "MVPMatrix");
|
2021-04-15 16:36:02 +03:00
|
|
|
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);
|
|
|
|
|
}
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
GLint frame_direction = glGetUniformLocation(gl->shader_progID, "FrameDirection");
|
2021-04-16 18:36:59 +03:00
|
|
|
if (frame_direction != -1)
|
|
|
|
|
glUniform1i(frame_direction, 1); /* always forward */
|
|
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Initialize OpenGL context
|
|
|
|
|
* @return Identifiers
|
|
|
|
|
*/
|
2021-04-29 13:47:05 +03:00
|
|
|
static int initialize_glcontext(gl_identifiers* gl)
|
2021-04-23 16:09:45 +03:00
|
|
|
{
|
|
|
|
|
/* Vertex, texture 2d coordinates and color (white) making a quad as triangle strip */
|
|
|
|
|
static const GLfloat surface[] = {
|
|
|
|
|
-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, 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
|
|
|
|
|
};
|
|
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
glGenVertexArrays(1, &gl->vertexArrayID);
|
2021-04-23 16:09:45 +03:00
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
glBindVertexArray(gl->vertexArrayID);
|
2021-04-23 16:09:45 +03:00
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
glGenBuffers(1, &gl->vertexBufferID);
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, gl->vertexBufferID);
|
2021-04-23 16:09:45 +03:00
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(surface), surface, GL_STATIC_DRAW);
|
|
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
glGenTextures(1, &gl->textureID);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, gl->textureID);
|
2021-04-23 16:09:45 +03:00
|
|
|
|
|
|
|
|
static const GLfloat border_color[] = { 0.f, 0.f, 0.f, 1.f };
|
|
|
|
|
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);
|
2021-05-01 20:49:23 +03:00
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, options.filter ? GL_LINEAR : GL_NEAREST);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, options.filter ? GL_LINEAR : GL_NEAREST);
|
2021-04-23 16:09:45 +03:00
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, INIT_WIDTH, INIT_HEIGHT, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
|
|
|
|
|
glGenBuffers(1, &gl->unpackBufferID);
|
|
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl->unpackBufferID);
|
|
|
|
|
|
2021-05-01 10:17:47 +03:00
|
|
|
void* buf_ptr = NULL;
|
|
|
|
|
|
|
|
|
|
if (GLAD_GL_ARB_buffer_storage)
|
|
|
|
|
{
|
|
|
|
|
/* Create persistent buffer for pixel transfer. */
|
|
|
|
|
glBufferStorage(GL_PIXEL_UNPACK_BUFFER, BUFFERBYTES * BUFFERCOUNT, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
|
|
|
|
|
|
|
|
|
|
buf_ptr = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, BUFFERBYTES * BUFFERCOUNT, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Fallback; create our own buffer. */
|
|
|
|
|
buf_ptr = malloc(BUFFERBYTES * BUFFERCOUNT);
|
|
|
|
|
|
|
|
|
|
glBufferData(GL_PIXEL_UNPACK_BUFFER, BUFFERBYTES * BUFFERCOUNT, NULL, GL_STREAM_DRAW);
|
|
|
|
|
}
|
2021-04-29 13:47:05 +03:00
|
|
|
|
|
|
|
|
if (buf_ptr == NULL)
|
2021-05-01 10:17:47 +03:00
|
|
|
return 0; /* Most likely out of memory. */
|
|
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
/* Split the buffer area for each blit_info and set them available for use. */
|
|
|
|
|
for (int i = 0; i < BUFFERCOUNT; i++)
|
|
|
|
|
{
|
|
|
|
|
blit_info[i].buffer = (byte*)buf_ptr + BUFFERBYTES * i;
|
|
|
|
|
atomic_flag_clear(&blit_info[i].in_use);
|
|
|
|
|
}
|
2021-04-23 16:09:45 +03:00
|
|
|
|
|
|
|
|
glClearColor(0.f, 0.f, 0.f, 1.f);
|
|
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
apply_shaders(gl);
|
2021-04-15 16:36:02 +03:00
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
return 1;
|
2021-04-10 01:35:17 +03:00
|
|
|
}
|
|
|
|
|
|
2021-04-10 17:41:51 +03:00
|
|
|
/**
|
2021-04-12 17:01:58 +03:00
|
|
|
* @brief Clean up OpenGL context
|
2021-04-23 16:09:45 +03:00
|
|
|
* @param gl Identifiers from initialize
|
2021-04-10 17:41:51 +03:00
|
|
|
*/
|
2021-04-29 13:47:05 +03:00
|
|
|
static void finalize_glcontext(gl_identifiers* gl)
|
2021-04-10 01:35:17 +03:00
|
|
|
{
|
2021-05-01 10:17:47 +03:00
|
|
|
if (GLAD_GL_ARB_buffer_storage)
|
|
|
|
|
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
|
|
|
|
else
|
|
|
|
|
free(blit_info[0].buffer);
|
|
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
glDeleteProgram(gl->shader_progID);
|
|
|
|
|
glDeleteBuffers(1, &gl->unpackBufferID);
|
|
|
|
|
glDeleteTextures(1, &gl->textureID);
|
|
|
|
|
glDeleteBuffers(1, &gl->vertexBufferID);
|
|
|
|
|
glDeleteVertexArrays(1, &gl->vertexArrayID);
|
2021-04-16 18:36:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Renders a frame and swaps the buffer
|
|
|
|
|
* @param gl Identifiers from initialize
|
|
|
|
|
*/
|
2021-04-29 13:47:05 +03:00
|
|
|
static void render_and_swap(gl_identifiers* gl)
|
2021-04-16 18:36:59 +03:00
|
|
|
{
|
|
|
|
|
static int frame_counter = 0;
|
|
|
|
|
|
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
|
|
|
|
|
|
SDL_GL_SwapWindow(window);
|
|
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
if (gl->frame_count != -1)
|
|
|
|
|
glUniform1i(gl->frame_count, frame_counter = (frame_counter + 1) & 1023);
|
2021-04-10 01:35:17 +03:00
|
|
|
}
|
|
|
|
|
|
2021-04-24 01:01:45 +03:00
|
|
|
/**
|
|
|
|
|
* @brief Handle failure in OpenGL thread.
|
2021-04-29 13:47:05 +03:00
|
|
|
* Keeps the thread sleeping until closing.
|
2021-04-24 01:01:45 +03:00
|
|
|
*/
|
|
|
|
|
static void opengl_fail()
|
|
|
|
|
{
|
|
|
|
|
if (window != NULL)
|
|
|
|
|
{
|
|
|
|
|
SDL_DestroyWindow(window);
|
|
|
|
|
window = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TODO: Notify user. */
|
|
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
WaitForSingleObject(sync_objects.closing, INFINITE);
|
|
|
|
|
|
|
|
|
|
_endthread();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __stdcall opengl_debugmsg_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
|
|
|
|
|
{
|
2021-07-05 23:16:00 +03:00
|
|
|
pclog("OpenGL: %s\n", message);
|
2021-04-24 01:01:45 +03:00
|
|
|
}
|
|
|
|
|
|
2021-04-10 17:41:51 +03:00
|
|
|
/**
|
|
|
|
|
* @brief Main OpenGL thread proc.
|
|
|
|
|
*
|
|
|
|
|
* OpenGL context should be accessed only from this single thread.
|
|
|
|
|
* Events are used to synchronize communication.
|
|
|
|
|
*/
|
2021-04-17 00:12:49 +03:00
|
|
|
static void opengl_main(void* param)
|
2021-04-10 01:35:17 +03:00
|
|
|
{
|
2021-06-17 19:15:35 +03:00
|
|
|
/* Initialize COM library for this thread before SDL does so. */
|
|
|
|
|
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
SDL_InitSubSystem(SDL_INIT_VIDEO);
|
|
|
|
|
|
2021-04-11 22:29:00 +03:00
|
|
|
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); /* Is this actually doing anything...? */
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
2021-07-05 23:16:00 +03:00
|
|
|
|
|
|
|
|
if (GLAD_GL_ARB_debug_output)
|
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
|
|
|
|
|
else
|
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
|
2021-04-29 13:47:05 +03:00
|
|
|
|
2021-04-11 22:29:00 +03:00
|
|
|
window = SDL_CreateWindow("86Box OpenGL Renderer", 0, 0, resize_info.width, resize_info.height, SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS);
|
2021-04-12 00:53:24 +03:00
|
|
|
|
2021-04-24 01:01:45 +03:00
|
|
|
if (window == NULL)
|
2021-07-05 23:16:00 +03:00
|
|
|
{
|
|
|
|
|
pclog("OpenGL: failed to create OpenGL window.\n");
|
2021-04-24 01:01:45 +03:00
|
|
|
opengl_fail();
|
2021-07-05 23:16:00 +03:00
|
|
|
}
|
2021-04-24 01:01:45 +03:00
|
|
|
|
2021-04-13 15:56:27 +03:00
|
|
|
/* Keep track of certain parameters, only changed in this thread to avoid race conditions */
|
2021-04-23 16:09:45 +03:00
|
|
|
int fullscreen = resize_info.fullscreen, video_width = INIT_WIDTH, video_height = INIT_HEIGHT,
|
|
|
|
|
output_width = resize_info.width, output_height = resize_info.height, frametime = options.frametime;
|
2021-04-12 17:01:58 +03:00
|
|
|
|
2021-04-17 00:12:49 +03:00
|
|
|
SDL_SysWMinfo wmi = { 0 };
|
2021-04-10 01:35:17 +03:00
|
|
|
SDL_VERSION(&wmi.version);
|
|
|
|
|
SDL_GetWindowWMInfo(window, &wmi);
|
|
|
|
|
|
2021-04-24 01:01:45 +03:00
|
|
|
if (wmi.subsystem != SDL_SYSWM_WINDOWS)
|
2021-07-05 23:16:00 +03:00
|
|
|
{
|
|
|
|
|
pclog("OpenGL: subsystem is not SDL_SYSWM_WINDOWS.\n");
|
2021-04-24 01:01:45 +03:00
|
|
|
opengl_fail();
|
2021-07-05 23:16:00 +03:00
|
|
|
}
|
2021-04-24 01:01:45 +03:00
|
|
|
|
|
|
|
|
window_hwnd = wmi.info.win.window;
|
2021-04-13 01:25:42 +03:00
|
|
|
|
2021-04-12 17:01:58 +03:00
|
|
|
if (!fullscreen)
|
|
|
|
|
set_parent_binding(1);
|
|
|
|
|
else
|
|
|
|
|
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
2021-04-10 01:35:17 +03:00
|
|
|
|
|
|
|
|
SDL_GLContext context = SDL_GL_CreateContext(window);
|
|
|
|
|
|
2021-04-24 01:01:45 +03:00
|
|
|
if (context == NULL)
|
2021-07-05 23:16:00 +03:00
|
|
|
{
|
|
|
|
|
pclog("OpenGL: failed to create OpenGL context.\n");
|
2021-04-24 01:01:45 +03:00
|
|
|
opengl_fail();
|
2021-07-05 23:16:00 +03:00
|
|
|
}
|
2021-04-24 01:01:45 +03:00
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
SDL_GL_SetSwapInterval(options.vsync);
|
|
|
|
|
|
2021-05-01 10:17:47 +03:00
|
|
|
if (!gladLoadGLLoader(SDL_GL_GetProcAddress))
|
2021-04-24 01:01:45 +03:00
|
|
|
{
|
2021-07-05 23:16:00 +03:00
|
|
|
pclog("OpenGL: failed to set OpenGL loader.\n");
|
2021-04-24 01:01:45 +03:00
|
|
|
SDL_GL_DeleteContext(context);
|
|
|
|
|
opengl_fail();
|
|
|
|
|
}
|
2021-07-05 23:16:00 +03:00
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
if (GLAD_GL_ARB_debug_output)
|
|
|
|
|
{
|
|
|
|
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
|
|
|
|
glDebugMessageControlARB(GL_DONT_CARE, GL_DEBUG_TYPE_PERFORMANCE_ARB, GL_DONT_CARE, 0, 0, GL_FALSE);
|
|
|
|
|
glDebugMessageCallbackARB(opengl_debugmsg_callback, NULL);
|
|
|
|
|
}
|
2021-07-05 23:16:00 +03:00
|
|
|
|
|
|
|
|
pclog("OpenGL vendor: %s\n", glGetString(GL_VENDOR));
|
|
|
|
|
pclog("OpenGL renderer: %s\n", glGetString(GL_RENDERER));
|
|
|
|
|
pclog("OpenGL version: %s\n", glGetString(GL_VERSION));
|
|
|
|
|
pclog("OpenGL shader language version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
|
2021-04-29 13:47:05 +03:00
|
|
|
|
|
|
|
|
gl_identifiers gl = { 0 };
|
|
|
|
|
|
|
|
|
|
if (!initialize_glcontext(&gl))
|
|
|
|
|
{
|
2021-07-05 23:16:00 +03:00
|
|
|
pclog("OpenGL: failed to initialize.\n");
|
2021-04-29 13:47:05 +03:00
|
|
|
finalize_glcontext(&gl);
|
|
|
|
|
SDL_GL_DeleteContext(context);
|
|
|
|
|
opengl_fail();
|
|
|
|
|
}
|
2021-04-15 16:36:02 +03:00
|
|
|
|
|
|
|
|
if (gl.frame_count != -1)
|
|
|
|
|
glUniform1i(gl.frame_count, 0);
|
2021-04-23 16:09:45 +03:00
|
|
|
if (gl.output_size != -1)
|
|
|
|
|
glUniform2f(gl.output_size, output_width, output_height);
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
uint32_t last_swap = plat_get_micro_ticks() - frametime;
|
2021-04-16 18:36:59 +03:00
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
int read_pos = 0; /* Buffer index of next read operation. */
|
|
|
|
|
|
2021-04-10 01:35:17 +03:00
|
|
|
/* Render loop */
|
|
|
|
|
int closing = 0;
|
|
|
|
|
while (!closing)
|
|
|
|
|
{
|
2021-04-23 16:09:45 +03:00
|
|
|
/* Rendering is done right after handling an event. */
|
|
|
|
|
if (frametime < 0)
|
2021-04-29 13:47:05 +03:00
|
|
|
render_and_swap(&gl);
|
2021-04-10 01:35:17 +03:00
|
|
|
|
|
|
|
|
DWORD wait_result = WAIT_TIMEOUT;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
2021-04-23 16:09:45 +03:00
|
|
|
/* Rendering is timed by frame capping. */
|
|
|
|
|
if (frametime >= 0)
|
2021-04-16 18:36:59 +03:00
|
|
|
{
|
2021-04-23 16:09:45 +03:00
|
|
|
uint32_t ticks = plat_get_micro_ticks();
|
|
|
|
|
|
|
|
|
|
uint32_t elapsed = ticks - last_swap;
|
|
|
|
|
|
|
|
|
|
if (elapsed + 1000 > frametime)
|
2021-04-16 18:36:59 +03:00
|
|
|
{
|
2021-04-23 16:09:45 +03:00
|
|
|
/* Spin the remaining time (< 1ms) to next frame */
|
|
|
|
|
while (elapsed < frametime)
|
|
|
|
|
{
|
|
|
|
|
Sleep(0); /* Yield processor time */
|
|
|
|
|
ticks = plat_get_micro_ticks();
|
|
|
|
|
elapsed = ticks - last_swap;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
render_and_swap(&gl);
|
2021-04-16 18:36:59 +03:00
|
|
|
last_swap = ticks;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
/* Check if commands that use buffers have been completed. */
|
|
|
|
|
for (int i = 0; i < BUFFERCOUNT; i++)
|
|
|
|
|
{
|
|
|
|
|
if (blit_info[i].sync != NULL && glClientWaitSync(blit_info[i].sync, GL_SYNC_FLUSH_COMMANDS_BIT, 0) != GL_TIMEOUT_EXPIRED)
|
|
|
|
|
{
|
|
|
|
|
glDeleteSync(blit_info[i].sync);
|
|
|
|
|
blit_info[i].sync = NULL;
|
|
|
|
|
atomic_flag_clear(&blit_info[i].in_use);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-19 01:05:53 +03:00
|
|
|
/* Handle window messages */
|
|
|
|
|
MSG msg;
|
|
|
|
|
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
|
|
|
{
|
|
|
|
|
if (msg.hwnd == window_hwnd)
|
|
|
|
|
handle_window_messages(msg.message, msg.wParam, msg.lParam, fullscreen);
|
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
|
DispatchMessage(&msg);
|
|
|
|
|
}
|
2021-04-12 00:53:24 +03:00
|
|
|
|
2021-04-10 17:41:51 +03:00
|
|
|
/* Wait for synchronized events for 1ms before going back to window events */
|
2021-04-10 01:35:17 +03:00
|
|
|
wait_result = WaitForMultipleObjects(sizeof(sync_objects) / sizeof(HANDLE), sync_objects.asArray, FALSE, 1);
|
|
|
|
|
|
|
|
|
|
} while (wait_result == WAIT_TIMEOUT);
|
|
|
|
|
|
|
|
|
|
HANDLE sync_event = sync_objects.asArray[wait_result - WAIT_OBJECT_0];
|
|
|
|
|
|
|
|
|
|
if (sync_event == sync_objects.closing)
|
|
|
|
|
{
|
|
|
|
|
closing = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (sync_event == sync_objects.blit_waiting)
|
|
|
|
|
{
|
2021-04-29 13:47:05 +03:00
|
|
|
blit_info_t* info = &blit_info[read_pos];
|
|
|
|
|
|
|
|
|
|
if (video_width != info->w || video_height != info->h)
|
2021-04-13 15:56:27 +03:00
|
|
|
{
|
2021-04-29 13:47:05 +03:00
|
|
|
video_width = info->w;
|
|
|
|
|
video_height = info->h;
|
2021-04-15 16:36:02 +03:00
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
/* Resize the texture */
|
|
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, video_width, video_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl.unpackBufferID);
|
2021-04-13 15:56:27 +03:00
|
|
|
|
|
|
|
|
if (fullscreen)
|
|
|
|
|
SetEvent(sync_objects.resize);
|
|
|
|
|
}
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-05-01 10:17:47 +03:00
|
|
|
if (!GLAD_GL_ARB_buffer_storage)
|
|
|
|
|
{
|
|
|
|
|
/* Fallback method, copy data to pixel buffer. */
|
2021-09-14 08:56:26 +03:00
|
|
|
glBufferSubData(GL_PIXEL_UNPACK_BUFFER, BUFFERBYTES * read_pos, info->h * ROW_LENGTH * sizeof(uint32_t), info->buffer);
|
2021-05-01 10:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
/* Update texture from pixel buffer. */
|
|
|
|
|
glPixelStorei(GL_UNPACK_SKIP_PIXELS, BUFFERPIXELS * read_pos);
|
2021-09-14 08:56:26 +03:00
|
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, ROW_LENGTH);
|
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, info->w, info->h, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
/* Add fence to track when above gl commands are complete. */
|
|
|
|
|
info->sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
|
|
|
|
|
|
|
|
read_pos = (read_pos + 1) % BUFFERCOUNT;
|
2021-04-15 16:36:02 +03:00
|
|
|
|
|
|
|
|
/* 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);
|
2021-04-10 01:35:17 +03:00
|
|
|
}
|
|
|
|
|
else if (sync_event == sync_objects.resize)
|
|
|
|
|
{
|
2021-04-22 01:15:10 +03:00
|
|
|
thread_wait_mutex(resize_info.mutex);
|
|
|
|
|
|
2021-04-12 17:01:58 +03:00
|
|
|
if (fullscreen != resize_info.fullscreen)
|
|
|
|
|
{
|
|
|
|
|
fullscreen = resize_info.fullscreen;
|
|
|
|
|
|
|
|
|
|
set_parent_binding(!fullscreen);
|
|
|
|
|
|
|
|
|
|
SDL_SetWindowFullscreen(window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
|
|
|
|
|
|
|
|
|
if (fullscreen)
|
2021-04-13 01:25:42 +03:00
|
|
|
{
|
|
|
|
|
SetForegroundWindow(window_hwnd);
|
|
|
|
|
SetFocus(window_hwnd);
|
|
|
|
|
}
|
2021-04-12 17:01:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fullscreen)
|
|
|
|
|
{
|
2021-04-13 15:56:27 +03:00
|
|
|
int width, height, pad_x = 0, pad_y = 0, px_size = 1;
|
|
|
|
|
float ratio = 0;
|
|
|
|
|
const float ratio43 = 4.f / 3.f;
|
2021-04-12 17:01:58 +03:00
|
|
|
|
|
|
|
|
SDL_GetWindowSize(window, &width, &height);
|
|
|
|
|
|
2021-04-13 15:56:27 +03:00
|
|
|
if (video_width > 0 && video_height > 0)
|
|
|
|
|
{
|
|
|
|
|
switch (resize_info.scaling_mode)
|
|
|
|
|
{
|
|
|
|
|
case FULLSCR_SCALE_INT:
|
|
|
|
|
px_size = max(min(width / video_width, height / video_height), 1);
|
|
|
|
|
|
|
|
|
|
pad_x = width - (video_width * px_size);
|
|
|
|
|
pad_y = height - (video_height * px_size);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FULLSCR_SCALE_KEEPRATIO:
|
|
|
|
|
ratio = (float)video_width / (float)video_height;
|
|
|
|
|
case FULLSCR_SCALE_43:
|
|
|
|
|
if (ratio == 0)
|
|
|
|
|
ratio = ratio43;
|
|
|
|
|
if (ratio < ((float)width / (float)height))
|
|
|
|
|
pad_x = width - (int)roundf((float)height * ratio);
|
|
|
|
|
else
|
|
|
|
|
pad_y = height - (int)roundf((float)width / ratio);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FULLSCR_SCALE_FULL:
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
output_width = width - pad_x;
|
|
|
|
|
output_height = height - pad_y;
|
|
|
|
|
|
|
|
|
|
glViewport(pad_x / 2, pad_y / 2, output_width, output_height);
|
2021-04-15 16:36:02 +03:00
|
|
|
|
|
|
|
|
if (gl.output_size != -1)
|
2021-04-23 16:09:45 +03:00
|
|
|
glUniform2f(gl.output_size, output_width, output_height);
|
2021-04-12 17:01:58 +03:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SDL_SetWindowSize(window, resize_info.width, resize_info.height);
|
|
|
|
|
|
|
|
|
|
/* SWP_NOZORDER is needed for child window and SDL doesn't enable it. */
|
2021-04-13 01:25:42 +03:00
|
|
|
SetWindowPos(window_hwnd, parent, 0, 0, resize_info.width, resize_info.height, SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOACTIVATE);
|
2021-04-12 17:01:58 +03:00
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
output_width = resize_info.width;
|
|
|
|
|
output_height = resize_info.height;
|
|
|
|
|
|
2021-04-12 17:01:58 +03:00
|
|
|
glViewport(0, 0, resize_info.width, resize_info.height);
|
2021-04-15 16:36:02 +03:00
|
|
|
|
|
|
|
|
if (gl.output_size != -1)
|
|
|
|
|
glUniform2f(gl.output_size, resize_info.width, resize_info.height);
|
2021-04-12 17:01:58 +03:00
|
|
|
}
|
2021-04-22 01:15:10 +03:00
|
|
|
|
|
|
|
|
thread_release_mutex(resize_info.mutex);
|
2021-04-10 01:35:17 +03:00
|
|
|
}
|
2021-04-23 16:09:45 +03:00
|
|
|
else if (sync_event == sync_objects.reload)
|
|
|
|
|
{
|
|
|
|
|
thread_wait_mutex(options.mutex);
|
|
|
|
|
|
|
|
|
|
frametime = options.frametime;
|
|
|
|
|
|
|
|
|
|
SDL_GL_SetSwapInterval(options.vsync);
|
|
|
|
|
|
|
|
|
|
if (options.shaderfile_changed)
|
|
|
|
|
{
|
|
|
|
|
/* Change shader program. */
|
|
|
|
|
apply_shaders(&gl);
|
|
|
|
|
|
|
|
|
|
/* Uniforms need to be updated after proram change. */
|
|
|
|
|
if (gl.input_size != -1)
|
|
|
|
|
glUniform2f(gl.input_size, video_width, video_height);
|
|
|
|
|
if (gl.output_size != -1)
|
|
|
|
|
glUniform2f(gl.output_size, output_width, output_height);
|
|
|
|
|
if (gl.texture_size != -1)
|
|
|
|
|
glUniform2f(gl.texture_size, video_width, video_height);
|
|
|
|
|
if (gl.frame_count != -1)
|
|
|
|
|
glUniform1i(gl.frame_count, 0);
|
|
|
|
|
|
|
|
|
|
options.shaderfile_changed = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-01 20:49:23 +03:00
|
|
|
if (options.filter_changed)
|
|
|
|
|
{
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, options.filter ? GL_LINEAR : GL_NEAREST);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, options.filter ? GL_LINEAR : GL_NEAREST);
|
|
|
|
|
|
|
|
|
|
options.filter_changed = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
thread_release_mutex(options.mutex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Keep cursor hidden in full screen and mouse capture */
|
|
|
|
|
int show_cursor = !(fullscreen || !!mouse_capture);
|
|
|
|
|
if (SDL_ShowCursor(-1) != show_cursor)
|
|
|
|
|
SDL_ShowCursor(show_cursor);
|
2021-04-10 01:35:17 +03:00
|
|
|
}
|
|
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
for (int i = 0; i < BUFFERCOUNT; i++)
|
|
|
|
|
{
|
|
|
|
|
if (blit_info[i].sync != NULL)
|
|
|
|
|
glDeleteSync(blit_info[i].sync);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
finalize_glcontext(&gl);
|
2021-04-10 01:35:17 +03:00
|
|
|
|
|
|
|
|
SDL_GL_DeleteContext(context);
|
|
|
|
|
|
2021-04-12 00:53:24 +03:00
|
|
|
set_parent_binding(0);
|
|
|
|
|
|
2021-04-10 01:35:17 +03:00
|
|
|
SDL_DestroyWindow(window);
|
|
|
|
|
|
|
|
|
|
window = NULL;
|
2021-06-17 19:15:35 +03:00
|
|
|
|
|
|
|
|
CoUninitialize();
|
2021-04-10 01:35:17 +03:00
|
|
|
}
|
|
|
|
|
|
2021-09-13 23:19:10 +02:00
|
|
|
static void opengl_blit(int x, int y, int w, int h)
|
2021-04-10 01:35:17 +03:00
|
|
|
{
|
2021-09-13 21:02:15 +02:00
|
|
|
int yy;
|
|
|
|
|
|
2021-09-13 23:19:10 +02:00
|
|
|
if ((h <= 0) || (buffer32 == NULL) || (thread == NULL) ||
|
2021-04-29 13:47:05 +03:00
|
|
|
atomic_flag_test_and_set(&blit_info[write_pos].in_use))
|
2021-04-10 01:35:17 +03:00
|
|
|
{
|
|
|
|
|
video_blit_complete();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-14 08:56:26 +03:00
|
|
|
memcpy(blit_info[write_pos].buffer, &(buffer32->line[y][x]), h * ROW_LENGTH * sizeof(uint32_t));
|
2021-04-10 01:35:17 +03:00
|
|
|
|
|
|
|
|
video_blit_complete();
|
2021-04-29 13:47:05 +03:00
|
|
|
|
|
|
|
|
blit_info[write_pos].w = w;
|
|
|
|
|
blit_info[write_pos].h = h;
|
|
|
|
|
|
|
|
|
|
write_pos = (write_pos + 1) % BUFFERCOUNT;
|
|
|
|
|
|
|
|
|
|
ReleaseSemaphore(sync_objects.blit_waiting, 1, NULL);
|
2021-04-10 01:35:17 +03:00
|
|
|
}
|
|
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
static int framerate_to_frametime(int framerate)
|
|
|
|
|
{
|
|
|
|
|
if (framerate < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return (int)ceilf(1.e6f / (float)framerate);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-10 01:35:17 +03:00
|
|
|
int opengl_init(HWND hwnd)
|
|
|
|
|
{
|
2021-04-22 01:15:10 +03:00
|
|
|
if (thread != NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2021-04-10 01:35:17 +03:00
|
|
|
for (int i = 0; i < sizeof(sync_objects) / sizeof(HANDLE); i++)
|
|
|
|
|
sync_objects.asArray[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
sync_objects.closing = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
|
sync_objects.resize = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
|
sync_objects.reload = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
|
sync_objects.blit_waiting = CreateSemaphore(NULL, 0, BUFFERCOUNT * 2, NULL);
|
2021-04-10 01:35:17 +03:00
|
|
|
|
|
|
|
|
parent = hwnd;
|
2021-04-11 22:29:00 +03:00
|
|
|
|
|
|
|
|
RECT parent_size;
|
|
|
|
|
|
|
|
|
|
GetWindowRect(parent, &parent_size);
|
|
|
|
|
|
|
|
|
|
resize_info.width = parent_size.right - parent_size.left;
|
|
|
|
|
resize_info.height = parent_size.bottom - parent_size.top;
|
2021-04-12 17:01:58 +03:00
|
|
|
resize_info.fullscreen = video_fullscreen & 1;
|
2021-04-13 15:56:27 +03:00
|
|
|
resize_info.scaling_mode = video_fullscreen_scale;
|
2021-04-22 01:15:10 +03:00
|
|
|
resize_info.mutex = thread_create_mutex();
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-21 01:33:01 +03:00
|
|
|
options.vsync = video_vsync;
|
2021-04-23 16:09:45 +03:00
|
|
|
options.frametime = framerate_to_frametime(video_framerate);
|
|
|
|
|
strcpy_s(options.shaderfile, sizeof(options.shaderfile), video_shader);
|
2021-05-01 20:49:23 +03:00
|
|
|
options.shaderfile_changed = 0;
|
|
|
|
|
options.filter = video_filter_method;
|
|
|
|
|
options.filter_changed = 0;
|
2021-04-23 16:09:45 +03:00
|
|
|
options.mutex = thread_create_mutex();
|
2021-04-21 01:33:01 +03:00
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
blit_info = (blit_info_t*)malloc(BUFFERCOUNT * sizeof(blit_info_t));
|
|
|
|
|
memset(blit_info, 0, BUFFERCOUNT * sizeof(blit_info_t));
|
|
|
|
|
|
|
|
|
|
/* Buffers are not yet allocated, set them as in use. */
|
|
|
|
|
for (int i = 0; i < BUFFERCOUNT; i++)
|
|
|
|
|
atomic_flag_test_and_set(&blit_info[i].in_use);
|
|
|
|
|
|
|
|
|
|
write_pos = 0;
|
|
|
|
|
|
2021-04-10 01:35:17 +03:00
|
|
|
thread = thread_create(opengl_main, (void*)NULL);
|
|
|
|
|
|
|
|
|
|
atexit(opengl_close);
|
|
|
|
|
|
|
|
|
|
video_setblit(opengl_blit);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-17 00:12:49 +03:00
|
|
|
int opengl_pause(void)
|
2021-04-10 01:35:17 +03:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-17 00:12:49 +03:00
|
|
|
void opengl_close(void)
|
2021-04-10 01:35:17 +03:00
|
|
|
{
|
|
|
|
|
if (thread == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
SetEvent(sync_objects.closing);
|
|
|
|
|
|
|
|
|
|
thread_wait(thread, -1);
|
|
|
|
|
|
2021-04-22 01:15:10 +03:00
|
|
|
thread_close_mutex(resize_info.mutex);
|
2021-04-23 16:09:45 +03:00
|
|
|
thread_close_mutex(options.mutex);
|
2021-04-22 01:15:10 +03:00
|
|
|
|
2021-04-10 01:35:17 +03:00
|
|
|
thread = NULL;
|
|
|
|
|
|
2021-04-29 13:47:05 +03:00
|
|
|
free(blit_info);
|
2021-04-10 01:35:17 +03:00
|
|
|
|
|
|
|
|
for (int i = 0; i < sizeof(sync_objects) / sizeof(HANDLE); i++)
|
|
|
|
|
{
|
|
|
|
|
CloseHandle(sync_objects.asArray[i]);
|
|
|
|
|
sync_objects.asArray[i] = (HANDLE)NULL;
|
|
|
|
|
}
|
2021-04-11 22:29:00 +03:00
|
|
|
|
|
|
|
|
parent = NULL;
|
2021-04-10 01:35:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void opengl_set_fs(int fs)
|
|
|
|
|
{
|
2021-04-12 17:01:58 +03:00
|
|
|
if (thread == NULL)
|
|
|
|
|
return;
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-22 01:15:10 +03:00
|
|
|
thread_wait_mutex(resize_info.mutex);
|
|
|
|
|
|
2021-04-12 17:01:58 +03:00
|
|
|
resize_info.fullscreen = fs;
|
2021-04-13 15:56:27 +03:00
|
|
|
resize_info.scaling_mode = video_fullscreen_scale;
|
2021-04-12 17:01:58 +03:00
|
|
|
|
2021-04-22 01:15:10 +03:00
|
|
|
thread_release_mutex(resize_info.mutex);
|
|
|
|
|
|
2021-04-12 17:01:58 +03:00
|
|
|
SetEvent(sync_objects.resize);
|
2021-04-10 01:35:17 +03:00
|
|
|
}
|
|
|
|
|
|
2021-04-11 22:29:00 +03:00
|
|
|
void opengl_resize(int w, int h)
|
2021-04-10 01:35:17 +03:00
|
|
|
{
|
2021-04-12 00:53:24 +03:00
|
|
|
if (thread == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
2021-04-22 01:15:10 +03:00
|
|
|
thread_wait_mutex(resize_info.mutex);
|
|
|
|
|
|
2021-04-11 22:29:00 +03:00
|
|
|
resize_info.width = w;
|
|
|
|
|
resize_info.height = h;
|
2021-04-13 15:56:27 +03:00
|
|
|
resize_info.scaling_mode = video_fullscreen_scale;
|
2021-04-10 01:35:17 +03:00
|
|
|
|
2021-04-22 01:15:10 +03:00
|
|
|
thread_release_mutex(resize_info.mutex);
|
|
|
|
|
|
2021-04-11 22:29:00 +03:00
|
|
|
SetEvent(sync_objects.resize);
|
2021-04-21 01:33:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void opengl_reload(void)
|
|
|
|
|
{
|
|
|
|
|
if (thread == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
thread_wait_mutex(options.mutex);
|
|
|
|
|
|
|
|
|
|
options.vsync = video_vsync;
|
|
|
|
|
options.frametime = framerate_to_frametime(video_framerate);
|
|
|
|
|
|
|
|
|
|
if (strcmp(video_shader, options.shaderfile) != 0)
|
|
|
|
|
{
|
|
|
|
|
strcpy_s(options.shaderfile, sizeof(options.shaderfile), video_shader);
|
|
|
|
|
options.shaderfile_changed = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-01 20:49:23 +03:00
|
|
|
if (video_filter_method != options.filter)
|
|
|
|
|
{
|
|
|
|
|
options.filter = video_filter_method;
|
|
|
|
|
options.filter_changed = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 16:09:45 +03:00
|
|
|
thread_release_mutex(options.mutex);
|
|
|
|
|
|
2021-04-21 01:33:01 +03:00
|
|
|
SetEvent(sync_objects.reload);
|
2021-04-10 01:35:17 +03:00
|
|
|
}
|