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>
|
2023-03-29 18:24:28 -04:00
|
|
|
#include <sstream>
|
2021-08-09 21:42:05 -04:00
|
|
|
|
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"
|
2022-08-23 19:13:26 -04:00
|
|
|
#include "game/sce/libscf.h"
|
2023-06-04 15:34:37 -04:00
|
|
|
#include "game/system/hid/input_manager.h"
|
|
|
|
#include "game/system/hid/sdl_util.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
|
2024-03-05 22:11:52 -05:00
|
|
|
#include "fmt/core.h"
|
2023-06-04 15:34:37 -04:00
|
|
|
#include "third-party/SDL/include/SDL.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
#include "third-party/imgui/imgui.h"
|
|
|
|
#include "third-party/imgui/imgui_impl_opengl3.h"
|
2023-06-04 15:34:37 -04:00
|
|
|
#include "third-party/imgui/imgui_impl_sdl.h"
|
|
|
|
#include "third-party/imgui/imgui_style.h"
|
2022-07-10 19:03:24 -04:00
|
|
|
#define STBI_WINDOWS_UTF8
|
2023-06-30 21:05:58 -04:00
|
|
|
#include "common/util/dialogs.h"
|
2023-06-04 15:34:37 -04:00
|
|
|
#include "common/util/string_util.h"
|
2021-08-09 19:16:39 -04:00
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
#include "third-party/stb_image/stb_image.h"
|
2021-08-09 19:16:39 -04:00
|
|
|
|
2022-02-27 17:23:12 -05:00
|
|
|
constexpr bool run_dma_copy = false;
|
|
|
|
|
2023-10-11 19:32:12 -04:00
|
|
|
constexpr PerGameVersion<int> fr3_level_count(jak1::LEVEL_TOTAL,
|
|
|
|
jak2::LEVEL_TOTAL,
|
|
|
|
jak3::LEVEL_TOTAL);
|
2022-10-01 13:39:56 -04:00
|
|
|
|
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),
|
2022-09-05 20:29:12 -04:00
|
|
|
texture_pool(std::make_shared<TexturePool>(version)),
|
2022-10-01 13:39:56 -04:00
|
|
|
loader(std::make_shared<Loader>(
|
|
|
|
file_util::get_jak_project_dir() / "out" / game_version_names[version] / "fr3",
|
|
|
|
fr3_level_count[version])),
|
2022-09-05 20:29:12 -04:00
|
|
|
ogl_renderer(texture_pool, loader, version),
|
2023-08-08 20:53:16 -04:00
|
|
|
debug_gui(),
|
2022-06-30 21:11:58 -04:00
|
|
|
version(version) {}
|
2021-08-09 21:42:05 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
std::unique_ptr<GraphicsData> g_gfx_data;
|
|
|
|
|
2021-08-09 19:16:39 -04:00
|
|
|
static bool gl_inited = false;
|
2023-06-04 15:34:37 -04:00
|
|
|
static int gl_init(GfxGlobalSettings& settings) {
|
|
|
|
prof().instant_event("ROOT");
|
2023-06-11 12:35:08 -04:00
|
|
|
Timer gl_init_timer;
|
2023-06-04 15:34:37 -04:00
|
|
|
// Initialize SDL
|
|
|
|
{
|
|
|
|
auto p = scoped_prof("startup::sdl::init_sdl");
|
2023-06-11 12:35:08 -04:00
|
|
|
// remove SDL garbage from hooking signal handler.
|
|
|
|
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
|
2024-02-23 14:44:17 -05:00
|
|
|
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
2023-06-04 15:34:37 -04:00
|
|
|
sdl_util::log_error("Could not initialize SDL, exiting");
|
2023-06-30 21:05:58 -04:00
|
|
|
dialogs::create_error_message_dialog("Critical Error Encountered",
|
|
|
|
"Could not initialize SDL, exiting");
|
2023-06-04 15:34:37 -04:00
|
|
|
return 1;
|
|
|
|
}
|
2021-08-09 19:16:39 -04:00
|
|
|
}
|
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
{
|
|
|
|
auto p = scoped_prof("startup::sdl::get_version_info");
|
|
|
|
SDL_version compiled;
|
|
|
|
SDL_VERSION(&compiled);
|
|
|
|
SDL_version linked;
|
|
|
|
SDL_GetVersion(&linked);
|
|
|
|
lg::info("SDL Initialized, compiled with version - {}.{}.{} | linked with version - {}.{}.{}",
|
|
|
|
compiled.major, compiled.minor, compiled.patch, linked.major, linked.minor,
|
|
|
|
linked.patch);
|
2021-08-09 19:16:39 -04:00
|
|
|
}
|
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
{
|
|
|
|
auto p = scoped_prof("startup::sdl::set_gl_attributes");
|
|
|
|
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
|
|
|
if (settings.debug) {
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
|
|
|
|
} else {
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
|
|
|
|
}
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
2024-01-25 23:49:35 -05:00
|
|
|
#ifndef __APPLE__
|
2023-06-04 15:34:37 -04:00
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
2024-01-25 23:49:35 -05:00
|
|
|
#else
|
2023-07-08 11:53:43 -04:00
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
|
|
|
#endif
|
2023-06-04 15:34:37 -04:00
|
|
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
|
2021-08-13 20:06:16 -04:00
|
|
|
}
|
2023-06-11 12:35:08 -04:00
|
|
|
lg::info("gl init took {:.3f}s\n", gl_init_timer.getSeconds());
|
2021-08-09 19:16:39 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gl_exit() {
|
2021-08-09 21:42:05 -04:00
|
|
|
g_gfx_data.reset();
|
|
|
|
gl_inited = false;
|
2021-08-09 19:16:39 -04:00
|
|
|
}
|
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
static void init_imgui(SDL_Window* window,
|
|
|
|
SDL_GLContext gl_context,
|
|
|
|
const std::string& glsl_version) {
|
2021-08-29 14:54:16 -04:00
|
|
|
// 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();
|
2023-06-04 15:34:37 -04:00
|
|
|
io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; // We manage the mouse cursor!
|
|
|
|
if (!Gfx::g_debug_settings.monospaced_font) {
|
|
|
|
// TODO - add or switch to Noto since it supports the entire unicode range
|
|
|
|
std::string font_path =
|
2023-06-07 20:04:16 -04:00
|
|
|
(file_util::get_jak_project_dir() / "game" / "assets" / "fonts" / "NotoSansJP-Medium.ttf")
|
2023-06-04 15:34:37 -04:00
|
|
|
.string();
|
|
|
|
if (file_util::file_exists(font_path)) {
|
|
|
|
static const ImWchar ranges[] = {
|
|
|
|
0x0020, 0x00FF, // Basic Latin + Latin Supplement
|
|
|
|
0x0400, 0x052F, // Cyrillic + Cyrillic Supplement
|
|
|
|
0x2000, 0x206F, // General Punctuation
|
|
|
|
0x2DE0, 0x2DFF, // Cyrillic Extended-A
|
|
|
|
0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana
|
|
|
|
0x3131, 0x3163, // Korean alphabets
|
|
|
|
0x31F0, 0x31FF, // Katakana Phonetic Extensions
|
2023-06-07 20:04:16 -04:00
|
|
|
0x4E00, 0x9FAF, // CJK Ideograms
|
2023-06-04 15:34:37 -04:00
|
|
|
0xA640, 0xA69F, // Cyrillic Extended-B
|
|
|
|
0xAC00, 0xD7A3, // Korean characters
|
|
|
|
0xFF00, 0xFFEF, // Half-width characters
|
|
|
|
0xFFFD, 0xFFFD, // Invalid
|
|
|
|
0,
|
|
|
|
};
|
|
|
|
io.Fonts->AddFontFromFileTTF(font_path.c_str(), Gfx::g_debug_settings.imgui_font_size,
|
|
|
|
nullptr, ranges);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
if (Gfx::g_debug_settings.alternate_style) {
|
|
|
|
ImGui::applyAlternateStyle();
|
|
|
|
}
|
|
|
|
|
2021-08-29 14:54:16 -04:00
|
|
|
// set up to get inputs for this window
|
2023-06-04 15:34:37 -04:00
|
|
|
ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
|
2021-08-29 14:54:16 -04:00
|
|
|
|
|
|
|
// 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.
|
2023-06-04 15:34:37 -04:00
|
|
|
SDL_ClearError();
|
2021-08-29 14:54:16 -04:00
|
|
|
|
|
|
|
// set up the renderer
|
2023-06-04 15:34:37 -04:00
|
|
|
ImGui_ImplOpenGL3_Init(glsl_version.c_str());
|
2022-06-16 22:46:12 -04:00
|
|
|
}
|
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
static std::shared_ptr<GfxDisplay> gl_make_display(int width,
|
|
|
|
int height,
|
|
|
|
const char* title,
|
2023-06-05 18:56:26 -04:00
|
|
|
GfxGlobalSettings& /*settings*/,
|
2023-06-04 15:34:37 -04:00
|
|
|
GameVersion game_version,
|
|
|
|
bool is_main) {
|
|
|
|
// Setup the window
|
|
|
|
prof().instant_event("ROOT");
|
|
|
|
prof().begin_event("startup::sdl::create_window");
|
|
|
|
// TODO - SDL2 doesn't seem to support HDR (and neither does windows)
|
|
|
|
// Related -
|
|
|
|
// https://answers.microsoft.com/en-us/windows/forum/all/hdr-monitor-low-brightness-after-exiting-full/999f7ee9-7ba3-4f9c-b812-bbeb9ff8dcc1
|
|
|
|
SDL_Window* window =
|
|
|
|
SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height,
|
|
|
|
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
|
|
|
prof().end_event();
|
|
|
|
if (!window) {
|
|
|
|
sdl_util::log_error("gl_make_display failed - Could not create display window");
|
2023-06-30 21:05:58 -04:00
|
|
|
dialogs::create_error_message_dialog(
|
|
|
|
"Critical Error Encountered",
|
|
|
|
"Unable to create OpenGL window.\nOpenGOAL requires OpenGL 4.3.\nEnsure your GPU "
|
|
|
|
"supports this and your drivers are up to date.");
|
2023-06-04 15:34:37 -04:00
|
|
|
return NULL;
|
2022-06-16 22:46:12 -04:00
|
|
|
}
|
2021-08-09 19:16:39 -04:00
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
// Make an OpenGL Context
|
|
|
|
prof().begin_event("startup::sdl::create_context");
|
|
|
|
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
|
|
|
|
prof().end_event();
|
|
|
|
if (!gl_context) {
|
|
|
|
sdl_util::log_error("gl_make_display failed - Could not create OpenGL Context");
|
2023-06-30 21:05:58 -04:00
|
|
|
dialogs::create_error_message_dialog(
|
|
|
|
"Critical Error Encountered",
|
|
|
|
"Unable to create OpenGL context.\nOpenGOAL requires OpenGL 4.3.\nEnsure your GPU "
|
|
|
|
"supports this and your drivers are up to date.");
|
2023-06-04 15:34:37 -04:00
|
|
|
return NULL;
|
2022-08-21 19:00:13 -04:00
|
|
|
}
|
2022-08-19 11:28:06 -04:00
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
{
|
|
|
|
auto p = scoped_prof("startup::sdl::assign_context");
|
|
|
|
if (SDL_GL_MakeCurrent(window, gl_context) != 0) {
|
|
|
|
sdl_util::log_error("gl_make_display failed - Could not associated context with window");
|
2023-06-30 21:05:58 -04:00
|
|
|
dialogs::create_error_message_dialog("Critical Error Encountered",
|
|
|
|
"Unable to create OpenGL window with context.\nOpenGOAL "
|
|
|
|
"requires OpenGL 4.3.\nEnsure your GPU "
|
|
|
|
"supports this and your drivers are up to date.");
|
2023-06-04 15:34:37 -04:00
|
|
|
return NULL;
|
2022-08-19 11:28:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
if (!gl_inited) {
|
|
|
|
{
|
|
|
|
auto p = scoped_prof("startup::sdl::glad_init");
|
|
|
|
gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress);
|
|
|
|
if (!gladLoadGL()) {
|
|
|
|
lg::error("GL init fail");
|
2023-06-30 21:05:58 -04:00
|
|
|
dialogs::create_error_message_dialog("Critical Error Encountered",
|
|
|
|
"Unable to initialize OpenGL API.\nOpenGOAL requires "
|
|
|
|
"OpenGL 4.3.\nEnsure your GPU "
|
|
|
|
"supports this and your drivers are up to date.");
|
2023-06-04 15:34:37 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
2022-07-31 13:31:17 -04:00
|
|
|
}
|
2023-06-04 15:34:37 -04:00
|
|
|
{
|
|
|
|
auto p = scoped_prof("startup::sdl::gfx_data_init");
|
|
|
|
g_gfx_data = std::make_unique<GraphicsData>(game_version);
|
|
|
|
}
|
|
|
|
gl_inited = true;
|
|
|
|
const char* gl_version = (const char*)glGetString(GL_VERSION);
|
|
|
|
lg::info("OpenGL initialized - v{}.{} | Renderer: {}", GLVersion.major, GLVersion.minor,
|
|
|
|
gl_version);
|
2022-07-31 13:31:17 -04:00
|
|
|
}
|
2022-08-19 11:28:06 -04:00
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
{
|
|
|
|
auto p = scoped_prof("startup::sdl::window_extras");
|
|
|
|
// Setup Window Icon
|
|
|
|
// TODO - hiDPI icon
|
|
|
|
// https://sourcegraph.com/github.com/dfranx/SHADERed/-/blob/main.cpp?L422:24&subtree=true
|
|
|
|
int icon_width;
|
|
|
|
int icon_height;
|
|
|
|
std::string image_path =
|
|
|
|
(file_util::get_jak_project_dir() / "game" / "assets" / "appicon.png").string();
|
|
|
|
auto icon_data =
|
|
|
|
stbi_load(image_path.c_str(), &icon_width, &icon_height, nullptr, STBI_rgb_alpha);
|
|
|
|
if (icon_data) {
|
|
|
|
SDL_Surface* icon_surf = SDL_CreateRGBSurfaceWithFormatFrom(
|
|
|
|
(void*)icon_data, icon_width, icon_height, 32, 4 * icon_width, SDL_PIXELFORMAT_RGBA32);
|
|
|
|
SDL_SetWindowIcon(window, icon_surf);
|
|
|
|
SDL_FreeSurface(icon_surf);
|
|
|
|
stbi_image_free(icon_data);
|
|
|
|
} else {
|
|
|
|
lg::error("Could not load icon for OpenGL window");
|
|
|
|
}
|
2022-08-19 11:28:06 -04:00
|
|
|
}
|
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
prof().begin_event("startup::sdl::create_GLDisplay");
|
|
|
|
auto display = std::make_shared<GLDisplay>(window, gl_context, is_main);
|
|
|
|
display->set_imgui_visible(Gfx::g_debug_settings.show_imgui);
|
|
|
|
prof().end_event();
|
2022-07-31 13:31:17 -04:00
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
{
|
|
|
|
auto p = scoped_prof("startup::sdl::init_imgui");
|
|
|
|
// setup imgui
|
2023-07-08 11:53:43 -04:00
|
|
|
#ifdef __APPLE__
|
|
|
|
init_imgui(window, gl_context, "#version 410");
|
|
|
|
#else
|
2023-06-04 15:34:37 -04:00
|
|
|
init_imgui(window, gl_context, "#version 430");
|
2023-07-08 11:53:43 -04:00
|
|
|
#endif
|
2022-07-31 13:31:17 -04:00
|
|
|
}
|
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
return std::static_pointer_cast<GfxDisplay>(display);
|
2022-07-31 13:31:17 -04:00
|
|
|
}
|
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
GLDisplay::GLDisplay(SDL_Window* window, SDL_GLContext gl_context, bool is_main)
|
|
|
|
: m_window(window),
|
|
|
|
m_gl_context(gl_context),
|
2023-06-05 18:56:26 -04:00
|
|
|
m_display_manager(std::make_shared<DisplayManager>(window)),
|
|
|
|
m_input_manager(std::make_shared<InputManager>()) {
|
2023-06-04 15:34:37 -04:00
|
|
|
m_main = is_main;
|
2023-07-08 10:45:56 -04:00
|
|
|
m_display_manager->set_input_manager(m_input_manager);
|
2023-06-04 15:34:37 -04:00
|
|
|
// Register commands
|
|
|
|
m_input_manager->register_command(CommandBinding::Source::KEYBOARD,
|
2023-07-28 21:26:42 -04:00
|
|
|
CommandBinding(Gfx::g_debug_settings.hide_imgui_key, [&]() {
|
2023-06-04 15:34:37 -04:00
|
|
|
if (!Gfx::g_debug_settings.ignore_hide_imgui) {
|
|
|
|
set_imgui_visible(!is_imgui_visible());
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
m_input_manager->register_command(
|
|
|
|
CommandBinding::Source::KEYBOARD,
|
|
|
|
CommandBinding(SDLK_F2, [&]() { m_take_screenshot_next_frame = true; }));
|
2022-08-23 19:13:26 -04:00
|
|
|
}
|
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
GLDisplay::~GLDisplay() {
|
|
|
|
// Cleanup ImGUI
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
io.IniFilename = nullptr;
|
|
|
|
io.LogFilename = nullptr;
|
|
|
|
ImGui_ImplOpenGL3_Shutdown();
|
|
|
|
ImGui_ImplSDL2_Shutdown();
|
|
|
|
ImGui::DestroyContext();
|
|
|
|
// Cleanup SDL
|
|
|
|
SDL_GL_DeleteContext(m_gl_context);
|
|
|
|
SDL_DestroyWindow(m_window);
|
|
|
|
SDL_Quit();
|
|
|
|
if (m_main) {
|
|
|
|
gl_exit();
|
|
|
|
}
|
2022-06-11 11:32:27 -04:00
|
|
|
}
|
|
|
|
|
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,
|
2023-06-04 15:34:37 -04:00
|
|
|
bool take_screenshot) {
|
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);
|
2023-06-18 23:15:33 -04:00
|
|
|
// there's a timeout here, so imgui can still be responsive even if we don't render anything
|
|
|
|
got_chain = g_gfx_data->dma_cv.wait_for(lock, std::chrono::milliseconds(40),
|
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();
|
2023-02-09 20:44:33 -05:00
|
|
|
options.draw_loader_window = g_gfx_data->debug_gui.should_draw_loader_menu();
|
2022-06-11 11:32:27 -04:00
|
|
|
options.draw_subtitle_editor_window = g_gfx_data->debug_gui.should_draw_subtitle_editor();
|
2023-01-02 09:45:38 -05:00
|
|
|
options.draw_filters_window = g_gfx_data->debug_gui.should_draw_filters_menu();
|
2022-07-14 21:37:03 -04:00
|
|
|
options.save_screenshot = false;
|
2023-06-17 18:42:52 -04:00
|
|
|
options.quick_screenshot = false;
|
2023-06-18 23:15:33 -04:00
|
|
|
options.internal_res_screenshot = false;
|
2022-07-17 14:45:00 -04:00
|
|
|
options.gpu_sync = g_gfx_data->debug_gui.should_gl_finish();
|
2022-08-23 19:13:26 -04:00
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
if (take_screenshot) {
|
2022-08-23 19:13:26 -04:00
|
|
|
options.save_screenshot = true;
|
2023-06-17 18:42:52 -04:00
|
|
|
options.quick_screenshot = true;
|
2023-06-04 15:34:37 -04:00
|
|
|
options.screenshot_path = file_util::make_screenshot_filepath(g_game_version);
|
2022-08-23 19:13:26 -04:00
|
|
|
}
|
2022-07-14 21:37:03 -04:00
|
|
|
if (g_gfx_data->debug_gui.get_screenshot_flag()) {
|
|
|
|
options.save_screenshot = true;
|
2024-01-25 23:49:35 -05:00
|
|
|
options.internal_res_screenshot = true;
|
2022-07-14 21:37:03 -04:00
|
|
|
options.game_res_w = g_gfx_data->debug_gui.screenshot_width;
|
|
|
|
options.game_res_h = g_gfx_data->debug_gui.screenshot_height;
|
2024-01-25 23:49:35 -05:00
|
|
|
options.window_framebuffer_width = options.game_res_w;
|
|
|
|
options.window_framebuffer_height = options.game_res_h;
|
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;
|
2023-06-04 15:34:37 -04:00
|
|
|
options.screenshot_path = file_util::make_screenshot_filepath(
|
|
|
|
g_game_version, g_gfx_data->debug_gui.screenshot_name());
|
2022-07-14 21:37:03 -04:00
|
|
|
}
|
2022-08-23 19:13:26 -04:00
|
|
|
|
2023-01-24 15:22:47 -05:00
|
|
|
options.draw_small_profiler_window =
|
|
|
|
g_gfx_data->debug_gui.master_enable && 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;
|
|
|
|
}
|
|
|
|
|
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-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;
|
2023-03-29 18:24:28 -04:00
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
// TODO - the file rotation code had an infinite loop here if it couldn't find anything
|
|
|
|
// matching the format
|
|
|
|
//
|
|
|
|
// Does the existing log rotation code have that problem?
|
2023-03-29 18:24:28 -04:00
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
auto file_path = file_util::get_jak_project_dir() / "profile_data" /
|
|
|
|
fmt::format("prof-{}.json", str_util::current_local_timestamp_no_colons());
|
|
|
|
file_util::create_dir_if_needed_for_file(file_path);
|
|
|
|
prof().dump_to_json(file_path.string());
|
2022-04-17 21:12:24 -04:00
|
|
|
}
|
2022-08-20 10:32:00 -04:00
|
|
|
}
|
|
|
|
|
2023-06-04 15:34:37 -04:00
|
|
|
void GLDisplay::process_sdl_events() {
|
|
|
|
SDL_Event evt;
|
|
|
|
while (SDL_PollEvent(&evt) != 0) {
|
|
|
|
if (evt.type == SDL_QUIT) {
|
|
|
|
m_should_quit = true;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
auto p = scoped_prof("sdl-display-manager");
|
|
|
|
m_display_manager->process_sdl_event(evt);
|
2022-08-20 10:32:00 -04:00
|
|
|
}
|
2023-06-04 15:34:37 -04:00
|
|
|
if (!m_should_quit) {
|
|
|
|
{
|
|
|
|
auto p = scoped_prof("imgui-sdl-process");
|
|
|
|
ImGui_ImplSDL2_ProcessEvent(&evt);
|
|
|
|
}
|
2022-08-20 10:32:00 -04:00
|
|
|
}
|
2023-06-04 15:34:37 -04:00
|
|
|
{
|
2023-07-08 10:45:56 -04:00
|
|
|
auto p = scoped_prof("sdl-input-monitor-process-event");
|
|
|
|
m_input_manager->process_sdl_event(evt);
|
2023-06-04 15:34:37 -04:00
|
|
|
}
|
2022-08-20 10:32:00 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-17 21:12:24 -04:00
|
|
|
/*!
|
|
|
|
* Main function called to render graphics frames. This is called in a loop.
|
|
|
|
*/
|
2022-06-16 22:46:12 -04:00
|
|
|
void GLDisplay::render() {
|
2023-07-08 10:45:56 -04:00
|
|
|
// Before we process the current frames SDL events we for keyboard/mouse button inputs.
|
|
|
|
//
|
|
|
|
// This technically means that keyboard/mouse button inputs will be a frame behind but the
|
2024-02-23 14:44:17 -05:00
|
|
|
// event-based code is limiting (there aren't enough events to achieve a totally stateless
|
|
|
|
// approach). Binding handling is still taken care of by the event code though.
|
2023-07-08 10:45:56 -04:00
|
|
|
{
|
|
|
|
auto p = scoped_prof("sdl-input-monitor-poll-for-kb-mouse");
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
if (io.WantCaptureKeyboard) {
|
|
|
|
m_input_manager->clear_keyboard_actions();
|
|
|
|
} else {
|
|
|
|
m_input_manager->poll_keyboard_data();
|
|
|
|
}
|
|
|
|
if (io.WantCaptureMouse) {
|
|
|
|
m_input_manager->clear_mouse_actions();
|
|
|
|
} else {
|
|
|
|
m_input_manager->poll_mouse_data();
|
|
|
|
}
|
|
|
|
m_input_manager->finish_polling();
|
|
|
|
}
|
|
|
|
// Now process SDL Events
|
2023-06-04 15:34:37 -04:00
|
|
|
process_sdl_events();
|
2023-06-19 16:32:39 -04:00
|
|
|
// Also process any display related events received from the EE (the game)
|
|
|
|
// this is done here so they run from the perspective of the graphics thread
|
|
|
|
{
|
|
|
|
auto p = scoped_prof("display-manager-ee-events");
|
|
|
|
m_display_manager->process_ee_events();
|
|
|
|
}
|
2023-06-23 15:35:32 -04:00
|
|
|
{
|
|
|
|
auto p = scoped_prof("input-manager-ee-events");
|
|
|
|
m_input_manager->process_ee_events();
|
|
|
|
}
|
2021-09-26 11:41:58 -04:00
|
|
|
|
|
|
|
// imgui start of frame
|
2022-04-17 21:12:24 -04:00
|
|
|
{
|
2023-06-04 15:34:37 -04:00
|
|
|
auto p = scoped_prof("imgui-new-frame");
|
2022-04-17 21:12:24 -04:00
|
|
|
ImGui_ImplOpenGL3_NewFrame();
|
2023-06-04 15:34:37 -04:00
|
|
|
ImGui_ImplSDL2_NewFrame();
|
2022-04-17 21:12:24 -04:00
|
|
|
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;
|
2023-06-04 15:34:37 -04:00
|
|
|
SDL_GL_GetDrawableSize(m_window, &fbuf_w, &fbuf_h);
|
2021-09-26 11:41:58 -04:00
|
|
|
|
2022-01-20 00:22:03 -05:00
|
|
|
// render game!
|
2023-01-24 15:22:47 -05:00
|
|
|
g_gfx_data->debug_gui.master_enable = is_imgui_visible();
|
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;
|
|
|
|
}
|
2023-07-08 18:05:03 -04:00
|
|
|
// set the size of the visible/playable portion of the game in the window
|
|
|
|
get_display_manager()->set_game_size(Gfx::g_global_settings.lbox_w,
|
|
|
|
Gfx::g_global_settings.lbox_h);
|
2023-06-04 15:34:37 -04:00
|
|
|
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,
|
|
|
|
m_take_screenshot_next_frame && g_gfx_data->debug_gui.screenshot_hotkey_enabled);
|
|
|
|
// If we took a screenshot, stop taking them now!
|
|
|
|
if (m_take_screenshot_next_frame) {
|
|
|
|
m_take_screenshot_next_frame = false;
|
|
|
|
}
|
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
|
|
|
|
2022-08-20 10:32:00 -04:00
|
|
|
// actual vsync
|
|
|
|
g_gfx_data->debug_gui.finish_frame();
|
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-08-20 10:32:00 -04:00
|
|
|
|
|
|
|
{
|
|
|
|
auto p = scoped_prof("swap-buffers");
|
2023-06-04 15:34:37 -04:00
|
|
|
SDL_GL_SwapWindow(m_window);
|
2022-08-20 10:32:00 -04: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;
|
2023-06-04 15:34:37 -04:00
|
|
|
// NOTE - -1 can be used for adaptive vsync, maybe useful for Jak 2+?
|
|
|
|
// https://wiki.libsdl.org/SDL2/SDL_GL_SetSwapInterval
|
|
|
|
SDL_GL_SetSwapInterval(Gfx::g_global_settings.vsync);
|
2022-07-18 19:54:38 -04:00
|
|
|
}
|
|
|
|
|
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-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
|
2023-06-04 15:34:37 -04:00
|
|
|
if (m_should_quit) {
|
2022-07-17 14:45:00 -04:00
|
|
|
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.
|
2022-08-19 11:28:06 -04:00
|
|
|
// - the copied DMA is much smaller than the entire game memory, so it can be dumped to a
|
|
|
|
// file
|
2021-08-09 21:42:05 -04:00
|
|
|
// separate of the entire RAM.
|
|
|
|
// - it verifies the DMA data is valid early on.
|
2022-08-19 11:28:06 -04:00
|
|
|
// but it may also be pretty expensive. Both the renderer and the game wait on this to
|
|
|
|
// complete.
|
2021-08-09 21:42:05 -04:00
|
|
|
|
2022-08-19 11:28:06 -04:00
|
|
|
// The renderers should just operate on DMA chains, so eliminating this step in the future
|
|
|
|
// may be easy.
|
2021-08-09 21:42:05 -04:00
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-20 10:32:00 -04:00
|
|
|
/*!
|
|
|
|
* Upload texture outside of main DMA chain.
|
|
|
|
* We trust the game to not remove textures that are currently being used, but if the game is messed
|
|
|
|
* up, there is a possible race to updating this texture.
|
|
|
|
*/
|
2021-08-09 21:42:05 -04:00
|
|
|
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.
|
2023-07-14 18:17:54 -04:00
|
|
|
g_gfx_data->texture_pool->handle_upload_now(tpage, mode, g_ee_main_mem, s7_ptr, false);
|
2021-08-09 21:42:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-20 10:32:00 -04:00
|
|
|
/*!
|
|
|
|
* Handle a local->local texture copy. The texture pool can just update texture pointers.
|
|
|
|
* This is called from the main thread and the texture pool itself will handle locking.
|
|
|
|
*/
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-09-22 10:50:16 -04:00
|
|
|
void gl_set_active_levels(const std::vector<std::string>& levels) {
|
|
|
|
g_gfx_data->loader->set_active_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
|
2022-03-02 20:01:37 -05:00
|
|
|
gl_set_levels, // set_levels
|
2023-09-22 10:50:16 -04:00
|
|
|
gl_set_active_levels, // set_active_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
|
|
|
};
|