2021-08-09 19:16:39 -04:00
|
|
|
/*!
|
|
|
|
* @file opengl.cpp
|
2022-06-16 22:46:12 -04:00
|
|
|
* Lower-level OpenGL interface. No actual rendering is performed here!
|
2021-08-09 19:16:39 -04:00
|
|
|
*/
|
|
|
|
|
2022-06-22 23:37:46 -04:00
|
|
|
#include "opengl.h"
|
|
|
|
|
|
|
|
#include <condition_variable>
|
2021-08-09 21:42:05 -04:00
|
|
|
#include <memory>
|
|
|
|
#include <mutex>
|
|
|
|
|
2022-06-22 23:37:46 -04:00
|
|
|
#include "common/dma/dma_copy.h"
|
|
|
|
#include "common/global_profiler/GlobalProfiler.h"
|
|
|
|
#include "common/goal_constants.h"
|
|
|
|
#include "common/log/log.h"
|
|
|
|
#include "common/util/FileUtil.h"
|
|
|
|
#include "common/util/FrameLimiter.h"
|
|
|
|
#include "common/util/Timer.h"
|
|
|
|
#include "common/util/compress.h"
|
2021-08-09 19:16:39 -04:00
|
|
|
|
|
|
|
#include "game/graphics/display.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
#include "game/graphics/gfx.h"
|
2021-08-09 21:42:05 -04:00
|
|
|
#include "game/graphics/opengl_renderer/OpenGLRenderer.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
#include "game/graphics/opengl_renderer/debug_gui.h"
|
2021-08-09 21:42:05 -04:00
|
|
|
#include "game/graphics/texture/TexturePool.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
#include "game/runtime.h"
|
2021-08-14 16:00:50 -04:00
|
|
|
#include "game/system/newpad.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
|
|
|
|
#include "third-party/imgui/imgui.h"
|
|
|
|
#include "third-party/imgui/imgui_impl_glfw.h"
|
|
|
|
#include "third-party/imgui/imgui_impl_opengl3.h"
|
2022-07-10 19:03:24 -04:00
|
|
|
#define STBI_WINDOWS_UTF8
|
2022-06-19 19:48:34 -04:00
|
|
|
#include "third-party/stb_image/stb_image.h"
|
2021-08-09 19:16:39 -04:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2022-02-27 17:23:12 -05:00
|
|
|
constexpr bool run_dma_copy = false;
|
|
|
|
|
2021-08-09 21:42:05 -04:00
|
|
|
struct GraphicsData {
|
|
|
|
// vsync
|
|
|
|
std::mutex sync_mutex;
|
|
|
|
std::condition_variable sync_cv;
|
|
|
|
|
|
|
|
// dma chain transfer
|
|
|
|
std::mutex dma_mutex;
|
|
|
|
std::condition_variable dma_cv;
|
|
|
|
u64 frame_idx = 0;
|
2021-09-26 11:41:58 -04:00
|
|
|
u64 frame_idx_of_input_data = 0;
|
2021-08-09 21:42:05 -04:00
|
|
|
bool has_data_to_render = false;
|
|
|
|
FixedChunkDmaCopier dma_copier;
|
|
|
|
|
|
|
|
// texture pool
|
|
|
|
std::shared_ptr<TexturePool> texture_pool;
|
|
|
|
|
2022-03-02 20:01:37 -05:00
|
|
|
std::shared_ptr<Loader> loader;
|
|
|
|
|
2021-08-09 21:42:05 -04:00
|
|
|
// temporary opengl renderer
|
|
|
|
OpenGLRenderer ogl_renderer;
|
|
|
|
|
2021-09-26 11:41:58 -04:00
|
|
|
OpenGlDebugGui debug_gui;
|
|
|
|
|
2022-01-20 00:22:03 -05:00
|
|
|
FrameLimiter frame_limiter;
|
|
|
|
Timer engine_timer;
|
|
|
|
double last_engine_time = 1. / 60.;
|
2022-03-26 15:53:44 -04:00
|
|
|
float pmode_alp = 0.f;
|
2022-01-20 00:22:03 -05:00
|
|
|
|
2022-06-21 21:26:11 -04:00
|
|
|
std::string imgui_log_filename, imgui_filename;
|
2022-06-30 21:11:58 -04:00
|
|
|
GameVersion version;
|
2022-06-21 21:26:11 -04:00
|
|
|
|
2022-06-30 21:11:58 -04:00
|
|
|
GraphicsData(GameVersion version)
|
2021-08-09 21:42:05 -04:00
|
|
|
: dma_copier(EE_MAIN_MEM_SIZE),
|
|
|
|
texture_pool(std::make_shared<TexturePool>()),
|
2022-06-30 21:11:58 -04:00
|
|
|
loader(std::make_shared<Loader>(file_util::get_jak_project_dir() / "out" /
|
|
|
|
game_version_names[version] / "fr3")),
|
|
|
|
ogl_renderer(texture_pool, loader),
|
|
|
|
version(version) {}
|
2021-08-09 21:42:05 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
std::unique_ptr<GraphicsData> g_gfx_data;
|
|
|
|
|
2022-07-31 13:31:17 -04:00
|
|
|
struct {
|
|
|
|
bool callbacks_registered = false;
|
|
|
|
GLFWmonitor** monitors;
|
|
|
|
int monitor_count;
|
|
|
|
} g_glfw_state;
|
|
|
|
|
|
|
|
void SetGlobalGLFWCallbacks() {
|
|
|
|
if (g_glfw_state.callbacks_registered) {
|
|
|
|
lg::warn("Global GLFW callbacks were already registered!");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get initial state
|
|
|
|
g_glfw_state.monitors = glfwGetMonitors(&g_glfw_state.monitor_count);
|
|
|
|
|
|
|
|
// Listen for events
|
|
|
|
glfwSetMonitorCallback([](GLFWmonitor* /*monitor*/, int /*event*/) {
|
|
|
|
// Reload monitor list
|
|
|
|
g_glfw_state.monitors = glfwGetMonitors(&g_glfw_state.monitor_count);
|
|
|
|
});
|
|
|
|
|
|
|
|
g_glfw_state.callbacks_registered = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClearGlobalGLFWCallbacks() {
|
|
|
|
if (!g_glfw_state.callbacks_registered) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
glfwSetMonitorCallback(NULL);
|
|
|
|
|
|
|
|
g_glfw_state.callbacks_registered = false;
|
2021-08-09 19:16:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void ErrorCallback(int err, const char* msg) {
|
2021-12-30 18:48:37 -05:00
|
|
|
lg::error("GLFW ERR {}: {}", err, std::string(msg));
|
2021-08-09 19:16:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool HasError() {
|
2021-08-29 14:54:16 -04:00
|
|
|
const char* ptr;
|
|
|
|
if (glfwGetError(&ptr) != GLFW_NO_ERROR) {
|
|
|
|
lg::error("glfw error: {}", ptr);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2021-08-09 19:16:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
static bool gl_inited = false;
|
2021-08-13 20:06:16 -04:00
|
|
|
static int gl_init(GfxSettings& settings) {
|
2021-08-09 19:16:39 -04:00
|
|
|
if (glfwSetErrorCallback(ErrorCallback) != NULL) {
|
|
|
|
lg::warn("glfwSetErrorCallback has been re-set!");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (glfwInit() == GLFW_FALSE) {
|
|
|
|
lg::error("glfwInit error");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-10-06 19:32:58 -04:00
|
|
|
// request an OpenGL 4.3 Core context
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // 4.3
|
2021-08-10 23:48:56 -04:00
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
|
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // core profile, not compat
|
2021-08-13 20:06:16 -04:00
|
|
|
// debug check
|
|
|
|
if (settings.debug) {
|
|
|
|
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
|
|
|
|
} else {
|
|
|
|
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_FALSE);
|
|
|
|
}
|
2022-02-15 18:42:48 -05:00
|
|
|
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
|
2021-08-09 19:16:39 -04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gl_exit() {
|
2022-07-31 13:31:17 -04:00
|
|
|
ClearGlobalGLFWCallbacks();
|
2021-08-09 21:42:05 -04:00
|
|
|
g_gfx_data.reset();
|
2021-08-09 19:16:39 -04:00
|
|
|
glfwTerminate();
|
|
|
|
glfwSetErrorCallback(NULL);
|
2021-08-09 21:42:05 -04:00
|
|
|
gl_inited = false;
|
2021-08-09 19:16:39 -04:00
|
|
|
}
|
|
|
|
|
2022-06-16 22:46:12 -04:00
|
|
|
static std::shared_ptr<GfxDisplay> gl_make_display(int width,
|
|
|
|
int height,
|
|
|
|
const char* title,
|
|
|
|
GfxSettings& settings,
|
2022-06-30 21:11:58 -04:00
|
|
|
GameVersion game_version,
|
2022-06-16 22:46:12 -04:00
|
|
|
bool is_main) {
|
2021-08-09 19:16:39 -04:00
|
|
|
GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL);
|
|
|
|
|
|
|
|
if (!window) {
|
2022-06-16 22:46:12 -04:00
|
|
|
lg::error("gl_make_display failed - Could not create display window");
|
2021-08-09 19:16:39 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
glfwMakeContextCurrent(window);
|
2022-06-16 22:46:12 -04:00
|
|
|
if (!gl_inited) {
|
|
|
|
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
|
|
|
|
if (!gladLoadGL()) {
|
|
|
|
lg::error("GL init fail");
|
|
|
|
return NULL;
|
|
|
|
}
|
2022-06-30 21:11:58 -04:00
|
|
|
g_gfx_data = std::make_unique<GraphicsData>(game_version);
|
2022-06-16 22:46:12 -04:00
|
|
|
|
|
|
|
gl_inited = true;
|
2022-02-07 19:15:37 -05:00
|
|
|
}
|
2021-08-09 19:16:39 -04:00
|
|
|
|
2022-06-16 22:46:12 -04:00
|
|
|
// window icon
|
2022-04-03 19:17:03 -04:00
|
|
|
std::string image_path =
|
|
|
|
(file_util::get_jak_project_dir() / "game" / "assets" / "appicon.png").string();
|
2021-11-13 22:41:15 -05:00
|
|
|
|
|
|
|
GLFWimage images[1];
|
2022-07-10 19:03:24 -04:00
|
|
|
auto load_result = stbi_load(image_path.c_str(), &images[0].width, &images[0].height, 0, 4);
|
|
|
|
if (load_result) {
|
|
|
|
images[0].pixels = load_result; // rgba channels
|
|
|
|
glfwSetWindowIcon(window, 1, images);
|
|
|
|
stbi_image_free(images[0].pixels);
|
2022-06-13 18:28:25 -04:00
|
|
|
} else {
|
2022-07-10 19:03:24 -04:00
|
|
|
lg::error("Could not load icon for OpenGL window");
|
2022-06-13 18:28:25 -04:00
|
|
|
}
|
2021-08-09 19:16:39 -04:00
|
|
|
|
2022-07-31 13:31:17 -04:00
|
|
|
SetGlobalGLFWCallbacks();
|
2021-09-26 11:41:58 -04:00
|
|
|
Pad::initialize();
|
2021-08-09 19:16:39 -04:00
|
|
|
|
|
|
|
if (HasError()) {
|
2022-06-16 22:46:12 -04:00
|
|
|
lg::error("gl_make_display error");
|
2021-08-09 19:16:39 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-06-16 22:46:12 -04:00
|
|
|
auto display = std::make_shared<GLDisplay>(window, is_main);
|
2021-08-09 19:16:39 -04:00
|
|
|
// lg::debug("init display #x{:x}", (uintptr_t)display);
|
|
|
|
|
2021-08-29 14:54:16 -04:00
|
|
|
// setup imgui
|
|
|
|
|
|
|
|
// check that version of the library is okay
|
|
|
|
IMGUI_CHECKVERSION();
|
|
|
|
|
|
|
|
// this does initialization for stuff like the font data
|
|
|
|
ImGui::CreateContext();
|
|
|
|
|
2022-06-21 18:22:31 -04:00
|
|
|
// Init ImGui settings
|
2022-06-21 21:26:11 -04:00
|
|
|
g_gfx_data->imgui_filename = file_util::get_file_path({"imgui.ini"});
|
|
|
|
g_gfx_data->imgui_log_filename = file_util::get_file_path({"imgui_log.txt"});
|
2022-06-21 18:22:31 -04:00
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
2022-06-21 21:26:11 -04:00
|
|
|
io.IniFilename = g_gfx_data->imgui_filename.c_str();
|
|
|
|
io.LogFilename = g_gfx_data->imgui_log_filename.c_str();
|
2022-06-21 18:22:31 -04:00
|
|
|
|
2021-08-29 14:54:16 -04:00
|
|
|
// set up to get inputs for this window
|
|
|
|
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
|
|
|
|
|
|
|
// NOTE: imgui's setup calls functions that may fail intentionally, and attempts to disable error
|
|
|
|
// reporting so these errors are invisible. But it does not work, and some weird X11 default
|
|
|
|
// cursor error is set here that we clear.
|
2021-10-06 19:32:58 -04:00
|
|
|
glfwGetError(NULL);
|
2021-08-29 14:54:16 -04:00
|
|
|
|
|
|
|
// set up the renderer
|
2022-06-16 22:46:12 -04:00
|
|
|
ImGui_ImplOpenGL3_Init("#version 430");
|
2021-08-29 14:54:16 -04:00
|
|
|
|
2022-06-16 22:46:12 -04:00
|
|
|
return std::static_pointer_cast<GfxDisplay>(display);
|
2021-08-09 19:16:39 -04:00
|
|
|
}
|
|
|
|
|
2022-06-16 22:46:12 -04:00
|
|
|
GLDisplay::GLDisplay(GLFWwindow* window, bool is_main) : m_window(window) {
|
|
|
|
m_main = is_main;
|
2022-07-31 13:31:17 -04:00
|
|
|
|
|
|
|
// Get initial state
|
|
|
|
get_position(&m_last_windowed_xpos, &m_last_windowed_ypos);
|
|
|
|
get_size(&m_last_windowed_width, &m_last_windowed_height);
|
|
|
|
|
|
|
|
// Listen for window-specific GLFW events
|
2022-06-17 19:39:33 -04:00
|
|
|
glfwSetWindowUserPointer(window, reinterpret_cast<void*>(this));
|
2022-07-31 13:31:17 -04:00
|
|
|
|
|
|
|
glfwSetKeyCallback(window, [](GLFWwindow* window, int key, int scancode, int action, int mods) {
|
|
|
|
GLDisplay* display = reinterpret_cast<GLDisplay*>(glfwGetWindowUserPointer(window));
|
|
|
|
display->on_key(window, key, scancode, action, mods);
|
|
|
|
});
|
|
|
|
|
|
|
|
glfwSetWindowPosCallback(window, [](GLFWwindow* window, int xpos, int ypos) {
|
|
|
|
GLDisplay* display = reinterpret_cast<GLDisplay*>(glfwGetWindowUserPointer(window));
|
|
|
|
display->on_window_pos(window, xpos, ypos);
|
|
|
|
});
|
|
|
|
|
|
|
|
glfwSetWindowSizeCallback(window, [](GLFWwindow* window, int width, int height) {
|
|
|
|
GLDisplay* display = reinterpret_cast<GLDisplay*>(glfwGetWindowUserPointer(window));
|
|
|
|
display->on_window_size(window, width, height);
|
|
|
|
});
|
|
|
|
|
|
|
|
glfwSetWindowIconifyCallback(window, [](GLFWwindow* window, int iconified) {
|
|
|
|
GLDisplay* display = reinterpret_cast<GLDisplay*>(glfwGetWindowUserPointer(window));
|
|
|
|
display->on_iconify(window, iconified);
|
|
|
|
});
|
2022-06-16 22:46:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
GLDisplay::~GLDisplay() {
|
2022-06-21 21:26:11 -04:00
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
io.IniFilename = nullptr;
|
|
|
|
io.LogFilename = nullptr;
|
2022-07-31 13:31:17 -04:00
|
|
|
glfwSetKeyCallback(m_window, NULL);
|
|
|
|
glfwSetWindowPosCallback(m_window, NULL);
|
|
|
|
glfwSetWindowSizeCallback(m_window, NULL);
|
|
|
|
glfwSetWindowIconifyCallback(m_window, NULL);
|
2022-06-17 19:39:33 -04:00
|
|
|
glfwSetWindowUserPointer(m_window, nullptr);
|
2021-08-29 14:54:16 -04:00
|
|
|
ImGui_ImplOpenGL3_Shutdown();
|
|
|
|
ImGui_ImplGlfw_Shutdown();
|
|
|
|
ImGui::DestroyContext();
|
2022-06-16 22:46:12 -04:00
|
|
|
glfwDestroyWindow(m_window);
|
|
|
|
if (m_main) {
|
|
|
|
gl_exit();
|
|
|
|
}
|
2021-08-09 19:16:39 -04:00
|
|
|
}
|
|
|
|
|
2022-07-31 13:31:17 -04:00
|
|
|
void GLDisplay::on_key(GLFWwindow* window, int key, int /*scancode*/, int action, int /*mods*/) {
|
|
|
|
if (action == GlfwKeyAction::Press) {
|
|
|
|
// lg::debug("KEY PRESS: key: {} scancode: {} mods: {:X}", key, scancode, mods);
|
|
|
|
Pad::OnKeyPress(key);
|
|
|
|
} else if (action == GlfwKeyAction::Release) {
|
|
|
|
// lg::debug("KEY RELEASE: key: {} scancode: {} mods: {:X}", key, scancode, mods);
|
|
|
|
Pad::OnKeyRelease(key);
|
|
|
|
if ((key == GLFW_KEY_LEFT_ALT || key == GLFW_KEY_RIGHT_ALT) &&
|
|
|
|
glfwGetWindowAttrib(window, GLFW_FOCUSED)) {
|
|
|
|
set_imgui_visible(!is_imgui_visible());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLDisplay::on_window_pos(GLFWwindow* /*window*/, int xpos, int ypos) {
|
|
|
|
if (m_fullscreen_target_mode == GfxDisplayMode::Windowed) {
|
|
|
|
m_last_windowed_xpos = xpos;
|
|
|
|
m_last_windowed_ypos = ypos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLDisplay::on_window_size(GLFWwindow* /*window*/, int width, int height) {
|
|
|
|
if (m_fullscreen_target_mode == GfxDisplayMode::Windowed) {
|
|
|
|
m_last_windowed_width = width;
|
|
|
|
m_last_windowed_height = height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLDisplay::on_iconify(GLFWwindow* window, int iconified) {
|
|
|
|
m_minimized = iconified == GLFW_TRUE;
|
|
|
|
}
|
|
|
|
|
2021-10-10 20:07:03 -04:00
|
|
|
namespace {
|
|
|
|
std::string make_output_file_name(const std::string& file_name) {
|
|
|
|
file_util::create_dir_if_needed(file_util::get_file_path({"gfx_dumps"}));
|
|
|
|
return file_util::get_file_path({"gfx_dumps", file_name});
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2022-06-11 11:32:27 -04:00
|
|
|
static bool endsWith(std::string_view str, std::string_view suffix) {
|
|
|
|
return str.size() >= suffix.size() &&
|
|
|
|
0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
|
|
|
|
}
|
|
|
|
|
2022-07-14 21:37:03 -04:00
|
|
|
void render_game_frame(int game_width,
|
|
|
|
int game_height,
|
2022-07-17 14:45:00 -04:00
|
|
|
int window_fb_width,
|
|
|
|
int window_fb_height,
|
|
|
|
int draw_region_width,
|
|
|
|
int draw_region_height,
|
|
|
|
int msaa_samples,
|
|
|
|
bool windows_borderless_hack) {
|
2021-08-09 21:42:05 -04:00
|
|
|
// wait for a copied chain.
|
|
|
|
bool got_chain = false;
|
|
|
|
{
|
2022-04-17 21:12:24 -04:00
|
|
|
auto p = scoped_prof("wait-for-dma");
|
2021-08-09 21:42:05 -04:00
|
|
|
std::unique_lock<std::mutex> lock(g_gfx_data->dma_mutex);
|
|
|
|
// note: there's a timeout here. If the engine is messed up and not sending us frames,
|
|
|
|
// we still want to run the glfw loop.
|
2022-01-20 00:22:03 -05:00
|
|
|
got_chain = g_gfx_data->dma_cv.wait_for(lock, std::chrono::milliseconds(50),
|
2021-08-09 21:42:05 -04:00
|
|
|
[=] { return g_gfx_data->has_data_to_render; });
|
|
|
|
}
|
|
|
|
// render that chain.
|
|
|
|
if (got_chain) {
|
2021-09-26 11:41:58 -04:00
|
|
|
g_gfx_data->frame_idx_of_input_data = g_gfx_data->frame_idx;
|
2021-10-10 20:07:03 -04:00
|
|
|
RenderOptions options;
|
2022-07-14 21:37:03 -04:00
|
|
|
options.game_res_w = game_width;
|
|
|
|
options.game_res_h = game_height;
|
2022-07-17 14:45:00 -04:00
|
|
|
options.window_framebuffer_width = window_fb_width;
|
|
|
|
options.window_framebuffer_height = window_fb_height;
|
|
|
|
options.draw_region_width = draw_region_width;
|
2022-07-19 18:41:48 -04:00
|
|
|
options.draw_region_height = draw_region_height;
|
2022-07-14 21:37:03 -04:00
|
|
|
options.msaa_samples = msaa_samples;
|
2021-10-10 20:07:03 -04:00
|
|
|
options.draw_render_debug_window = g_gfx_data->debug_gui.should_draw_render_debug();
|
|
|
|
options.draw_profiler_window = g_gfx_data->debug_gui.should_draw_profiler();
|
2022-06-11 11:32:27 -04:00
|
|
|
options.draw_subtitle_editor_window = g_gfx_data->debug_gui.should_draw_subtitle_editor();
|
2022-07-14 21:37:03 -04:00
|
|
|
options.save_screenshot = false;
|
2022-07-17 14:45:00 -04:00
|
|
|
options.gpu_sync = g_gfx_data->debug_gui.should_gl_finish();
|
|
|
|
options.borderless_windows_hacks = windows_borderless_hack;
|
2022-07-14 21:37:03 -04:00
|
|
|
if (g_gfx_data->debug_gui.get_screenshot_flag()) {
|
|
|
|
options.save_screenshot = true;
|
|
|
|
options.game_res_w = g_gfx_data->debug_gui.screenshot_width;
|
|
|
|
options.game_res_h = g_gfx_data->debug_gui.screenshot_height;
|
2022-07-17 14:45:00 -04:00
|
|
|
options.draw_region_width = options.game_res_w;
|
|
|
|
options.draw_region_height = options.game_res_h;
|
2022-07-14 21:37:03 -04:00
|
|
|
options.msaa_samples = g_gfx_data->debug_gui.screenshot_samples;
|
|
|
|
}
|
2022-02-25 23:25:49 -05:00
|
|
|
options.draw_small_profiler_window = g_gfx_data->debug_gui.small_profiler;
|
2022-03-26 15:53:44 -04:00
|
|
|
options.pmode_alp_register = g_gfx_data->pmode_alp;
|
2022-07-14 21:37:03 -04:00
|
|
|
|
|
|
|
GLint msaa_max;
|
|
|
|
glGetIntegerv(GL_MAX_SAMPLES, &msaa_max);
|
|
|
|
if (options.msaa_samples > msaa_max) {
|
|
|
|
options.msaa_samples = msaa_max;
|
|
|
|
}
|
|
|
|
|
2021-10-10 20:07:03 -04:00
|
|
|
if (options.save_screenshot) {
|
2022-06-11 11:32:27 -04:00
|
|
|
// ensure the screenshot has an extension
|
|
|
|
std::string temp_path = g_gfx_data->debug_gui.screenshot_name();
|
2022-06-11 16:52:27 -04:00
|
|
|
if (!endsWith(temp_path, ".png")) {
|
2022-06-11 11:32:27 -04:00
|
|
|
temp_path += ".png";
|
|
|
|
}
|
|
|
|
options.screenshot_path = make_output_file_name(temp_path);
|
2021-10-10 20:07:03 -04:00
|
|
|
}
|
2022-02-27 17:23:12 -05:00
|
|
|
if constexpr (run_dma_copy) {
|
|
|
|
auto& chain = g_gfx_data->dma_copier.get_last_result();
|
|
|
|
g_gfx_data->ogl_renderer.render(DmaFollower(chain.data.data(), chain.start_offset), options);
|
|
|
|
} else {
|
2022-04-17 21:12:24 -04:00
|
|
|
auto p = scoped_prof("ogl-render");
|
2022-02-27 17:23:12 -05:00
|
|
|
g_gfx_data->ogl_renderer.render(DmaFollower(g_gfx_data->dma_copier.get_last_input_data(),
|
|
|
|
g_gfx_data->dma_copier.get_last_input_offset()),
|
|
|
|
options);
|
|
|
|
}
|
2021-08-09 21:42:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// before vsync, mark the chain as rendered.
|
|
|
|
{
|
|
|
|
// should be fine to remove this mutex if the game actually waits for vsync to call
|
|
|
|
// send_chain again. but let's be safe for now.
|
|
|
|
std::unique_lock<std::mutex> lock(g_gfx_data->dma_mutex);
|
2022-01-20 00:22:03 -05:00
|
|
|
g_gfx_data->engine_timer.start();
|
2021-08-09 21:42:05 -04:00
|
|
|
g_gfx_data->has_data_to_render = false;
|
2021-08-10 21:31:15 -04:00
|
|
|
g_gfx_data->sync_cv.notify_all();
|
2021-08-09 21:42:05 -04:00
|
|
|
}
|
2021-09-26 11:41:58 -04:00
|
|
|
}
|
|
|
|
|
2022-06-16 22:46:12 -04:00
|
|
|
void GLDisplay::get_position(int* x, int* y) {
|
|
|
|
glfwGetWindowPos(m_window, x, y);
|
2021-12-30 18:48:37 -05:00
|
|
|
}
|
|
|
|
|
2022-06-16 22:46:12 -04:00
|
|
|
void GLDisplay::get_size(int* width, int* height) {
|
|
|
|
glfwGetFramebufferSize(m_window, width, height);
|
2021-12-30 18:48:37 -05:00
|
|
|
}
|
|
|
|
|
2022-06-16 22:46:12 -04:00
|
|
|
void GLDisplay::get_scale(float* xs, float* ys) {
|
|
|
|
glfwGetWindowContentScale(m_window, xs, ys);
|
2021-12-30 18:48:37 -05:00
|
|
|
}
|
|
|
|
|
2022-06-16 22:46:12 -04:00
|
|
|
void GLDisplay::set_size(int width, int height) {
|
|
|
|
glfwSetWindowSize(m_window, width, height);
|
2022-07-31 13:31:17 -04:00
|
|
|
|
|
|
|
if (windowed()) {
|
|
|
|
m_last_windowed_width = width;
|
|
|
|
m_last_windowed_height = height;
|
|
|
|
}
|
2021-12-30 18:48:37 -05:00
|
|
|
}
|
|
|
|
|
2022-07-31 13:31:17 -04:00
|
|
|
void GLDisplay::update_fullscreen(GfxDisplayMode mode, int screen) {
|
|
|
|
GLFWmonitor* monitor = get_monitor(screen);
|
|
|
|
|
2021-12-30 18:48:37 -05:00
|
|
|
switch (mode) {
|
2022-06-16 22:46:12 -04:00
|
|
|
case GfxDisplayMode::Windowed: {
|
2021-12-30 18:48:37 -05:00
|
|
|
// windowed
|
2022-07-31 13:31:17 -04:00
|
|
|
int x, y, width, height;
|
|
|
|
|
|
|
|
if (m_last_fullscreen_mode == GfxDisplayMode::Windowed) {
|
|
|
|
// windowed -> windowed, keep position and size
|
|
|
|
width = m_last_windowed_width;
|
|
|
|
height = m_last_windowed_height;
|
|
|
|
x = m_last_windowed_xpos;
|
|
|
|
y = m_last_windowed_ypos;
|
|
|
|
} else {
|
|
|
|
// fullscreen -> windowed, use last windowed size but on the monitor previously fullscreened
|
|
|
|
int monitorX, monitorY, monitorWidth, monitorHeight;
|
|
|
|
glfwGetMonitorWorkarea(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight);
|
|
|
|
|
|
|
|
width = m_last_windowed_width;
|
|
|
|
height = m_last_windowed_height;
|
|
|
|
x = monitorX + (monitorWidth / 2) - (width / 2);
|
|
|
|
y = monitorY + (monitorHeight / 2) - (height / 2);
|
|
|
|
}
|
|
|
|
|
2022-06-16 22:46:12 -04:00
|
|
|
glfwSetWindowAttrib(m_window, GLFW_DECORATED, GLFW_TRUE);
|
|
|
|
glfwSetWindowFocusCallback(m_window, NULL);
|
|
|
|
glfwSetWindowAttrib(m_window, GLFW_FLOATING, GLFW_FALSE);
|
2022-07-31 13:31:17 -04:00
|
|
|
glfwSetWindowMonitor(m_window, NULL, x, y, width, height, GLFW_DONT_CARE);
|
2022-06-17 19:39:33 -04:00
|
|
|
set_imgui_visible(true);
|
2022-07-31 13:31:17 -04:00
|
|
|
|
|
|
|
// these might have changed
|
|
|
|
m_last_windowed_width = width;
|
|
|
|
m_last_windowed_height = height;
|
|
|
|
m_last_windowed_xpos = x;
|
|
|
|
m_last_windowed_ypos = y;
|
2021-12-30 18:48:37 -05:00
|
|
|
} break;
|
2022-06-16 22:46:12 -04:00
|
|
|
case GfxDisplayMode::Fullscreen: {
|
2021-12-30 18:48:37 -05:00
|
|
|
// fullscreen
|
|
|
|
const GLFWvidmode* vmode = glfwGetVideoMode(monitor);
|
2022-07-15 10:01:54 -04:00
|
|
|
glfwSetWindowAttrib(m_window, GLFW_DECORATED, GLFW_FALSE);
|
2022-06-16 22:46:12 -04:00
|
|
|
glfwSetWindowFocusCallback(m_window, NULL);
|
|
|
|
glfwSetWindowAttrib(m_window, GLFW_FLOATING, GLFW_FALSE);
|
2022-07-15 10:01:54 -04:00
|
|
|
glfwSetWindowMonitor(m_window, monitor, 0, 0, vmode->width, vmode->height, GLFW_DONT_CARE);
|
2022-06-17 19:39:33 -04:00
|
|
|
set_imgui_visible(false);
|
2021-12-30 18:48:37 -05:00
|
|
|
} break;
|
2022-06-16 22:46:12 -04:00
|
|
|
case GfxDisplayMode::Borderless: {
|
2021-12-30 18:48:37 -05:00
|
|
|
// borderless fullscreen
|
|
|
|
int x, y;
|
|
|
|
glfwGetMonitorPos(monitor, &x, &y);
|
|
|
|
const GLFWvidmode* vmode = glfwGetVideoMode(monitor);
|
2022-06-16 22:46:12 -04:00
|
|
|
glfwSetWindowAttrib(m_window, GLFW_DECORATED, GLFW_FALSE);
|
|
|
|
// glfwSetWindowAttrib(m_window, GLFW_FLOATING, GLFW_TRUE);
|
|
|
|
// glfwSetWindowFocusCallback(m_window, FocusCallback);
|
2021-12-30 18:48:37 -05:00
|
|
|
#ifdef _WIN32
|
2022-06-16 22:46:12 -04:00
|
|
|
glfwSetWindowMonitor(m_window, NULL, x, y, vmode->width, vmode->height + 1, GLFW_DONT_CARE);
|
2021-12-30 18:48:37 -05:00
|
|
|
#else
|
2022-06-16 22:46:12 -04:00
|
|
|
glfwSetWindowMonitor(m_window, NULL, x, y, vmode->width, vmode->height, GLFW_DONT_CARE);
|
2021-12-30 18:48:37 -05:00
|
|
|
#endif
|
2022-06-17 19:39:33 -04:00
|
|
|
set_imgui_visible(false);
|
2021-12-30 18:48:37 -05:00
|
|
|
} break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-25 11:05:20 -04:00
|
|
|
int GLDisplay::get_screen_vmode_count() {
|
2022-04-11 18:38:54 -04:00
|
|
|
int count = 0;
|
2022-07-31 13:31:17 -04:00
|
|
|
glfwGetVideoModes(get_monitor(fullscreen_screen()), &count);
|
2022-06-25 11:05:20 -04:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLDisplay::get_screen_size(int vmode_idx, s32* w_out, s32* h_out) {
|
2022-07-31 13:31:17 -04:00
|
|
|
GLFWmonitor* monitor = get_monitor(fullscreen_screen());
|
|
|
|
auto vmode = glfwGetVideoMode(monitor);
|
2022-06-25 11:05:20 -04:00
|
|
|
int count = 0;
|
2022-07-31 13:31:17 -04:00
|
|
|
auto vmodes = glfwGetVideoModes(monitor, &count);
|
2022-06-25 11:05:20 -04:00
|
|
|
if (vmode_idx >= 0) {
|
|
|
|
vmode = &vmodes[vmode_idx];
|
2022-07-31 13:31:17 -04:00
|
|
|
} else if (fullscreen_mode() == GfxDisplayMode::Fullscreen) {
|
2022-06-25 11:05:20 -04:00
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
if (!vmode || vmode->height < vmodes[i].height) {
|
|
|
|
vmode = &vmodes[i];
|
2022-04-11 18:38:54 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (w_out) {
|
|
|
|
*w_out = vmode->width;
|
|
|
|
}
|
|
|
|
if (h_out) {
|
|
|
|
*h_out = vmode->height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-25 11:05:20 -04:00
|
|
|
int GLDisplay::get_screen_rate(int vmode_idx) {
|
2022-07-31 13:31:17 -04:00
|
|
|
GLFWmonitor* monitor = get_monitor(fullscreen_screen());
|
|
|
|
auto vmode = glfwGetVideoMode(monitor);
|
2022-06-25 11:05:20 -04:00
|
|
|
int count = 0;
|
2022-07-31 13:31:17 -04:00
|
|
|
auto vmodes = glfwGetVideoModes(monitor, &count);
|
2022-06-25 11:05:20 -04:00
|
|
|
if (vmode_idx >= 0) {
|
|
|
|
vmode = &vmodes[vmode_idx];
|
2022-07-31 13:31:17 -04:00
|
|
|
} else if (fullscreen_mode() == GfxDisplayMode::Fullscreen) {
|
2022-06-25 11:05:20 -04:00
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
if (!vmode || vmode->refreshRate < vmodes[i].refreshRate) {
|
|
|
|
vmode = &vmodes[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return vmode->refreshRate;
|
|
|
|
}
|
|
|
|
|
2022-07-31 13:31:17 -04:00
|
|
|
GLFWmonitor* GLDisplay::get_monitor(int index) {
|
|
|
|
if (index < 0 || index >= g_glfw_state.monitor_count) {
|
|
|
|
// out of bounds, default to primary monitor
|
|
|
|
index = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return g_glfw_state.monitors[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
int GLDisplay::get_monitor_count() {
|
|
|
|
return g_glfw_state.monitor_count;
|
|
|
|
}
|
|
|
|
|
2022-06-25 11:05:20 -04:00
|
|
|
bool GLDisplay::minimized() {
|
2022-07-31 13:31:17 -04:00
|
|
|
return m_minimized;
|
2022-06-25 11:05:20 -04:00
|
|
|
}
|
|
|
|
|
2022-06-21 19:21:51 -04:00
|
|
|
void GLDisplay::set_lock(bool lock) {
|
|
|
|
glfwSetWindowAttrib(m_window, GLFW_RESIZABLE, lock ? GLFW_TRUE : GLFW_FALSE);
|
|
|
|
}
|
|
|
|
|
2022-07-31 13:31:17 -04:00
|
|
|
bool GLDisplay::fullscreen_pending() {
|
|
|
|
GLFWmonitor* monitor = get_monitor(fullscreen_screen());
|
|
|
|
auto vmode = glfwGetVideoMode(monitor);
|
|
|
|
|
|
|
|
return GfxDisplay::fullscreen_pending() ||
|
|
|
|
(vmode->width != m_last_video_mode.width || vmode->height != m_last_video_mode.height ||
|
|
|
|
vmode->refreshRate != m_last_video_mode.refreshRate);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLDisplay::fullscreen_flush() {
|
|
|
|
GfxDisplay::fullscreen_flush();
|
|
|
|
|
|
|
|
GLFWmonitor* monitor = get_monitor(fullscreen_screen());
|
|
|
|
auto vmode = glfwGetVideoMode(monitor);
|
|
|
|
|
|
|
|
m_last_video_mode = *vmode;
|
|
|
|
}
|
|
|
|
|
2022-04-17 21:12:24 -04:00
|
|
|
void update_global_profiler() {
|
|
|
|
if (g_gfx_data->debug_gui.dump_events) {
|
|
|
|
prof().set_enable(false);
|
|
|
|
g_gfx_data->debug_gui.dump_events = false;
|
|
|
|
prof().dump_to_json((file_util::get_jak_project_dir() / "prof.json").string());
|
|
|
|
}
|
|
|
|
prof().set_enable(g_gfx_data->debug_gui.record_events);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Main function called to render graphics frames. This is called in a loop.
|
|
|
|
*/
|
2022-06-16 22:46:12 -04:00
|
|
|
void GLDisplay::render() {
|
2021-09-26 11:41:58 -04:00
|
|
|
// poll events
|
2022-04-17 21:12:24 -04:00
|
|
|
{
|
|
|
|
auto p = scoped_prof("poll-gamepads");
|
|
|
|
glfwPollEvents();
|
2022-06-16 22:46:12 -04:00
|
|
|
glfwMakeContextCurrent(m_window);
|
2022-04-17 21:12:24 -04:00
|
|
|
Pad::update_gamepads();
|
|
|
|
}
|
2021-09-26 11:41:58 -04:00
|
|
|
|
|
|
|
// imgui start of frame
|
2022-04-17 21:12:24 -04:00
|
|
|
{
|
|
|
|
auto p = scoped_prof("imgui-init");
|
|
|
|
ImGui_ImplOpenGL3_NewFrame();
|
|
|
|
ImGui_ImplGlfw_NewFrame();
|
|
|
|
ImGui::NewFrame();
|
|
|
|
}
|
2021-09-26 11:41:58 -04:00
|
|
|
|
2022-07-17 14:45:00 -04:00
|
|
|
// framebuffer size
|
2021-12-30 18:48:37 -05:00
|
|
|
int fbuf_w, fbuf_h;
|
2022-06-16 22:46:12 -04:00
|
|
|
glfwGetFramebufferSize(m_window, &fbuf_w, &fbuf_h);
|
2022-07-17 14:45:00 -04:00
|
|
|
bool windows_borderless_hacks = false;
|
2021-12-30 18:48:37 -05:00
|
|
|
#ifdef _WIN32
|
2022-06-16 22:46:12 -04:00
|
|
|
if (last_fullscreen_mode() == GfxDisplayMode::Borderless) {
|
2022-07-17 14:45:00 -04:00
|
|
|
windows_borderless_hacks = true;
|
2022-02-07 19:15:37 -05:00
|
|
|
}
|
|
|
|
#endif
|
2021-09-26 11:41:58 -04:00
|
|
|
|
2022-01-20 00:22:03 -05:00
|
|
|
// render game!
|
2022-03-06 19:56:43 -05:00
|
|
|
if (g_gfx_data->debug_gui.should_advance_frame()) {
|
2022-04-17 21:12:24 -04:00
|
|
|
auto p = scoped_prof("game-render");
|
2022-07-17 14:45:00 -04:00
|
|
|
int game_res_w = Gfx::g_global_settings.game_res_w;
|
|
|
|
int game_res_h = Gfx::g_global_settings.game_res_h;
|
|
|
|
if (game_res_w <= 0 || game_res_h <= 0) {
|
|
|
|
// if the window size reports 0, the game will ask for a 0 sized window, and nothing likes
|
|
|
|
// that.
|
|
|
|
game_res_w = 640;
|
|
|
|
game_res_h = 480;
|
|
|
|
}
|
|
|
|
render_game_frame(game_res_w, game_res_h, fbuf_w, fbuf_h, Gfx::g_global_settings.lbox_w,
|
|
|
|
Gfx::g_global_settings.lbox_h, Gfx::g_global_settings.msaa_samples,
|
|
|
|
windows_borderless_hacks);
|
2021-12-30 19:38:18 -05:00
|
|
|
}
|
|
|
|
|
2022-04-17 21:12:24 -04:00
|
|
|
// render debug
|
2022-06-17 19:39:33 -04:00
|
|
|
if (is_imgui_visible()) {
|
2022-04-17 21:12:24 -04:00
|
|
|
auto p = scoped_prof("debug-gui");
|
|
|
|
g_gfx_data->debug_gui.draw(g_gfx_data->dma_copier.get_last_result().stats);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
auto p = scoped_prof("imgui-render");
|
|
|
|
ImGui::Render();
|
|
|
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
|
|
|
}
|
2021-08-29 14:54:16 -04:00
|
|
|
|
2021-08-09 21:42:05 -04:00
|
|
|
// actual vsync
|
2021-09-26 11:41:58 -04:00
|
|
|
g_gfx_data->debug_gui.finish_frame();
|
2022-04-17 21:12:24 -04:00
|
|
|
{
|
|
|
|
auto p = scoped_prof("swap-buffers");
|
2022-06-16 22:46:12 -04:00
|
|
|
glfwSwapBuffers(m_window);
|
2022-04-17 21:12:24 -04:00
|
|
|
}
|
2022-06-25 11:05:20 -04:00
|
|
|
if (Gfx::g_global_settings.framelimiter) {
|
2022-04-17 21:12:24 -04:00
|
|
|
auto p = scoped_prof("frame-limiter");
|
2022-02-05 16:30:50 -05:00
|
|
|
g_gfx_data->frame_limiter.run(
|
2022-06-25 11:05:20 -04:00
|
|
|
Gfx::g_global_settings.target_fps, Gfx::g_global_settings.experimental_accurate_lag,
|
|
|
|
Gfx::g_global_settings.sleep_in_frame_limiter, g_gfx_data->last_engine_time);
|
2022-01-20 00:22:03 -05:00
|
|
|
}
|
2022-07-17 14:45:00 -04:00
|
|
|
// actually wait for vsync
|
|
|
|
if (g_gfx_data->debug_gui.should_gl_finish()) {
|
|
|
|
glFinish();
|
|
|
|
}
|
|
|
|
|
2022-07-18 19:54:38 -04:00
|
|
|
// switch vsync modes, if requested
|
|
|
|
if (Gfx::g_global_settings.vsync != Gfx::g_global_settings.old_vsync) {
|
|
|
|
Gfx::g_global_settings.old_vsync = Gfx::g_global_settings.vsync;
|
|
|
|
glfwSwapInterval(Gfx::g_global_settings.vsync);
|
|
|
|
}
|
|
|
|
|
2022-07-17 14:45:00 -04:00
|
|
|
// Start timing for the next frame.
|
2021-09-26 11:41:58 -04:00
|
|
|
g_gfx_data->debug_gui.start_frame();
|
2022-04-17 21:12:24 -04:00
|
|
|
prof().instant_event("ROOT");
|
|
|
|
update_global_profiler();
|
2021-08-09 19:16:39 -04:00
|
|
|
|
2021-08-09 21:42:05 -04:00
|
|
|
// toggle even odd and wake up engine waiting on vsync.
|
2022-07-17 14:45:00 -04:00
|
|
|
// TODO: we could play with moving this earlier, right after the final bucket renderer.
|
|
|
|
// it breaks the VIF-interrupt profiling though.
|
2022-03-02 20:01:37 -05:00
|
|
|
{
|
2022-07-17 14:45:00 -04:00
|
|
|
prof().instant_event("engine-notify");
|
2021-08-09 21:42:05 -04:00
|
|
|
std::unique_lock<std::mutex> lock(g_gfx_data->sync_mutex);
|
|
|
|
g_gfx_data->frame_idx++;
|
|
|
|
g_gfx_data->sync_cv.notify_all();
|
|
|
|
}
|
2021-08-09 19:16:39 -04:00
|
|
|
|
2022-07-31 13:31:17 -04:00
|
|
|
// update fullscreen mode, if requested
|
2022-07-17 14:45:00 -04:00
|
|
|
{
|
|
|
|
auto p = scoped_prof("fullscreen-update");
|
2022-07-31 13:31:17 -04:00
|
|
|
update_last_fullscreen_mode();
|
|
|
|
|
|
|
|
if (fullscreen_pending() && !minimized()) {
|
2022-07-17 14:45:00 -04:00
|
|
|
fullscreen_flush();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-30 14:55:13 -04:00
|
|
|
// reboot whole game, if requested
|
|
|
|
if (g_gfx_data->debug_gui.want_reboot_in_debug) {
|
|
|
|
g_gfx_data->debug_gui.want_reboot_in_debug = false;
|
|
|
|
MasterExit = RuntimeExitStatus::RESTART_IN_DEBUG;
|
|
|
|
}
|
|
|
|
|
2022-07-17 14:45:00 -04:00
|
|
|
{
|
|
|
|
auto p = scoped_prof("check-close-window");
|
|
|
|
// exit if display window was closed
|
|
|
|
if (glfwWindowShouldClose(m_window)) {
|
|
|
|
std::unique_lock<std::mutex> lock(g_gfx_data->sync_mutex);
|
|
|
|
MasterExit = RuntimeExitStatus::EXIT;
|
|
|
|
g_gfx_data->sync_cv.notify_all();
|
|
|
|
}
|
2021-08-09 19:16:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-09 21:42:05 -04:00
|
|
|
/*!
|
|
|
|
* Wait for the next vsync. Returns 0 or 1 depending on if frame is even or odd.
|
|
|
|
* Called from the game thread, on a GOAL stack.
|
|
|
|
*/
|
|
|
|
u32 gl_vsync() {
|
|
|
|
if (!g_gfx_data) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
std::unique_lock<std::mutex> lock(g_gfx_data->sync_mutex);
|
2021-09-26 11:41:58 -04:00
|
|
|
auto init_frame = g_gfx_data->frame_idx_of_input_data;
|
2022-04-30 14:55:13 -04:00
|
|
|
g_gfx_data->sync_cv.wait(lock, [=] {
|
|
|
|
return (MasterExit != RuntimeExitStatus::RUNNING) || g_gfx_data->frame_idx > init_frame;
|
|
|
|
});
|
2021-08-09 21:42:05 -04:00
|
|
|
return g_gfx_data->frame_idx & 1;
|
|
|
|
}
|
|
|
|
|
2021-08-10 21:31:15 -04:00
|
|
|
u32 gl_sync_path() {
|
|
|
|
if (!g_gfx_data) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
std::unique_lock<std::mutex> lock(g_gfx_data->sync_mutex);
|
2022-01-20 00:22:03 -05:00
|
|
|
g_gfx_data->last_engine_time = g_gfx_data->engine_timer.getSeconds();
|
2021-08-10 21:31:15 -04:00
|
|
|
if (!g_gfx_data->has_data_to_render) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
g_gfx_data->sync_cv.wait(lock, [=] { return !g_gfx_data->has_data_to_render; });
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-09 21:42:05 -04:00
|
|
|
/*!
|
|
|
|
* Send DMA to the renderer.
|
|
|
|
* Called from the game thread, on a GOAL stack.
|
|
|
|
*/
|
|
|
|
void gl_send_chain(const void* data, u32 offset) {
|
|
|
|
if (g_gfx_data) {
|
|
|
|
std::unique_lock<std::mutex> lock(g_gfx_data->dma_mutex);
|
|
|
|
if (g_gfx_data->has_data_to_render) {
|
|
|
|
lg::error(
|
|
|
|
"Gfx::send_chain called when the graphics renderer has pending data. Was this called "
|
|
|
|
"multiple times per frame?");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we copy the dma data and give a copy of it to the render.
|
|
|
|
// the copy has a few advantages:
|
|
|
|
// - if the game code has a bug and corrupts the DMA buffer, the renderer won't see it.
|
|
|
|
// - the copied DMA is much smaller than the entire game memory, so it can be dumped to a file
|
|
|
|
// separate of the entire RAM.
|
|
|
|
// - it verifies the DMA data is valid early on.
|
|
|
|
// but it may also be pretty expensive. Both the renderer and the game wait on this to complete.
|
|
|
|
|
|
|
|
// The renderers should just operate on DMA chains, so eliminating this step in the future may
|
|
|
|
// be easy.
|
|
|
|
|
2022-02-27 17:23:12 -05:00
|
|
|
g_gfx_data->dma_copier.set_input_data(data, offset, run_dma_copy);
|
2021-08-09 21:42:05 -04:00
|
|
|
|
|
|
|
g_gfx_data->has_data_to_render = true;
|
|
|
|
g_gfx_data->dma_cv.notify_all();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void gl_texture_upload_now(const u8* tpage, int mode, u32 s7_ptr) {
|
2021-09-26 11:41:58 -04:00
|
|
|
// block
|
2022-03-02 20:01:37 -05:00
|
|
|
if (g_gfx_data) {
|
2021-08-09 21:42:05 -04:00
|
|
|
// just pass it to the texture pool.
|
|
|
|
// the texture pool will take care of locking.
|
|
|
|
// we don't want to lock here for the entire duration of the conversion.
|
|
|
|
g_gfx_data->texture_pool->handle_upload_now(tpage, mode, g_ee_main_mem, s7_ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-11 19:36:15 -04:00
|
|
|
void gl_texture_relocate(u32 destination, u32 source, u32 format) {
|
2022-03-02 20:01:37 -05:00
|
|
|
if (g_gfx_data) {
|
2021-08-11 19:36:15 -04:00
|
|
|
g_gfx_data->texture_pool->relocate(destination, source, format);
|
2021-08-09 21:42:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-15 22:50:36 -04:00
|
|
|
void gl_poll_events() {
|
|
|
|
glfwPollEvents();
|
|
|
|
}
|
|
|
|
|
2022-03-02 20:01:37 -05:00
|
|
|
void gl_set_levels(const std::vector<std::string>& levels) {
|
|
|
|
g_gfx_data->loader->set_want_levels(levels);
|
|
|
|
}
|
|
|
|
|
2022-03-26 15:53:44 -04:00
|
|
|
void gl_set_pmode_alp(float val) {
|
|
|
|
g_gfx_data->pmode_alp = val;
|
|
|
|
}
|
|
|
|
|
2022-06-16 22:46:12 -04:00
|
|
|
const GfxRendererModule gRendererOpenGL = {
|
2021-08-09 21:42:05 -04:00
|
|
|
gl_init, // init
|
2022-06-16 22:46:12 -04:00
|
|
|
gl_make_display, // make_display
|
2021-08-09 21:42:05 -04:00
|
|
|
gl_exit, // exit
|
|
|
|
gl_vsync, // vsync
|
2021-08-10 21:31:15 -04:00
|
|
|
gl_sync_path, // sync_path
|
2021-08-09 21:42:05 -04:00
|
|
|
gl_send_chain, // send_chain
|
|
|
|
gl_texture_upload_now, // texture_upload_now
|
|
|
|
gl_texture_relocate, // texture_relocate
|
2021-08-15 22:50:36 -04:00
|
|
|
gl_poll_events, // poll_events
|
2022-03-02 20:01:37 -05:00
|
|
|
gl_set_levels, // set_levels
|
2022-03-26 15:53:44 -04:00
|
|
|
gl_set_pmode_alp, // set_pmode_alp
|
2021-08-09 21:42:05 -04:00
|
|
|
GfxPipeline::OpenGL, // pipeline
|
2021-12-30 18:48:37 -05:00
|
|
|
"OpenGL 4.3" // name
|
2021-08-09 19:16:39 -04:00
|
|
|
};
|