diff options
Diffstat (limited to 'src')
30 files changed, 212 insertions, 74 deletions
diff --git a/src/citra/config.cpp b/src/citra/config.cpp index fe74fcd72..29462c982 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -66,6 +66,8 @@ void Config::ReadValues() { Settings::values.use_scaled_resolution = sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false); Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false); + Settings::values.toggle_framelimit = + sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true); Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0); Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index b98dc4d83..001b18ac2 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -64,6 +64,10 @@ use_vsync = # 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen layout_option = +#Whether to toggle frame limiter on or off. +# 0: Off , 1 (default): On +toggle_framelimit = + # Swaps the prominent screen with the other screen. # For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen. # 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 384875450..a9dacd5f1 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -65,7 +65,6 @@ set(HEADERS hotkeys.h main.h ui_settings.h - version.h ) set(UIS diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index 3cdfe6443..06a4e9d25 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -47,6 +47,7 @@ void Config::ReadValues() { Settings::values.use_scaled_resolution = qt_config->value("use_scaled_resolution", false).toBool(); Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool(); + Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool(); Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat(); Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat(); @@ -152,6 +153,7 @@ void Config::SaveValues() { qt_config->setValue("use_shader_jit", Settings::values.use_shader_jit); qt_config->setValue("use_scaled_resolution", Settings::values.use_scaled_resolution); qt_config->setValue("use_vsync", Settings::values.use_vsync); + qt_config->setValue("toggle_framelimit", Settings::values.toggle_framelimit); // Cast to double because Qt's written float values are not human-readable qt_config->setValue("bg_red", (double)Settings::values.bg_red); diff --git a/src/citra_qt/configure_graphics.cpp b/src/citra_qt/configure_graphics.cpp index 29834e11b..36f10c8d7 100644 --- a/src/citra_qt/configure_graphics.cpp +++ b/src/citra_qt/configure_graphics.cpp @@ -23,6 +23,7 @@ void ConfigureGraphics::setConfiguration() { ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit); ui->toggle_scaled_resolution->setChecked(Settings::values.use_scaled_resolution); ui->toggle_vsync->setChecked(Settings::values.use_vsync); + ui->toggle_framelimit->setChecked(Settings::values.toggle_framelimit); ui->layout_combobox->setCurrentIndex(static_cast<int>(Settings::values.layout_option)); ui->swap_screen->setChecked(Settings::values.swap_screen); } @@ -32,6 +33,7 @@ void ConfigureGraphics::applyConfiguration() { Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); Settings::values.use_scaled_resolution = ui->toggle_scaled_resolution->isChecked(); Settings::values.use_vsync = ui->toggle_vsync->isChecked(); + Settings::values.toggle_framelimit = ui->toggle_framelimit->isChecked(); Settings::values.layout_option = static_cast<Settings::LayoutOption>(ui->layout_combobox->currentIndex()); Settings::values.swap_screen = ui->swap_screen->isChecked(); diff --git a/src/citra_qt/configure_graphics.ui b/src/citra_qt/configure_graphics.ui index af16a4292..964aa0bbd 100644 --- a/src/citra_qt/configure_graphics.ui +++ b/src/citra_qt/configure_graphics.ui @@ -50,6 +50,13 @@ </property> </widget> </item> + <item> + <widget class="QCheckBox" name="toggle_framelimit"> + <property name="text"> + <string>Limit framerate</string> + </property> + </widget> + </item> </layout> </widget> </item> diff --git a/src/citra_qt/version.h b/src/citra_qt/version.h deleted file mode 100644 index 9d5a2b1a2..000000000 --- a/src/citra_qt/version.h +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -// TODO: Supposed to be generated... -// GENERATED - Do not edit! -#ifndef VERSION_H_ -#define VERSION_H_ -#define __BUILD__ "40" -#define VERSION __BUILD__ -#endif // VERSION_H_ diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 413a8e7e5..b6161f2dc 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -26,9 +26,6 @@ #define stat _stat64 #define fstat _fstat64 #define fileno _fileno -// Windows version, at least Vista is required to obtain AppData Path -#define WINVER 0x0600 -#define _WIN32_WINNT 0x0600 #else #ifdef __APPLE__ #include <sys/param.h> diff --git a/src/common/key_map.cpp b/src/common/key_map.cpp index 79b3fcb18..97cafe9c9 100644 --- a/src/common/key_map.cpp +++ b/src/common/key_map.cpp @@ -49,7 +49,7 @@ static bool circle_pad_right = false; static bool circle_pad_modifier = false; static void UpdateCirclePad(EmuWindow& emu_window) { - constexpr float SQRT_HALF = 0.707106781; + constexpr float SQRT_HALF = 0.707106781f; int x = 0, y = 0; if (circle_pad_right) @@ -61,9 +61,9 @@ static void UpdateCirclePad(EmuWindow& emu_window) { if (circle_pad_down) --y; - float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0; - emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0 : SQRT_HALF), - y * modifier * (x == 0 ? 1.0 : SQRT_HALF)); + float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0f; + emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0f : SQRT_HALF), + y * modifier * (x == 0 ? 1.0f : SQRT_HALF)); } int NewDeviceId() { diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 8011534b8..96d0dfb8c 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -60,7 +60,7 @@ enum class Class : ClassType { Service_AM, ///< The AM (Application manager) service Service_PTM, ///< The PTM (Power status & misc.) service Service_LDR, ///< The LDR (3ds dll loader) service - Service_MIC, ///< The MIC (microphone) service + Service_MIC, ///< The MIC (Microphone) service Service_NDM, ///< The NDM (Network daemon manager) service Service_NIM, ///< The NIM (Network interface manager) service Service_NWM, ///< The NWM (Network wlan manager) service @@ -85,8 +85,7 @@ enum class Class : ClassType { Audio_DSP, ///< The HLE implementation of the DSP Audio_Sink, ///< Emulator audio output backend Loader, ///< ROM loader - - Count ///< Total number of logging classes + Count ///< Total number of logging classes }; /// Logs a message to the global logger. diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp index 18026975f..fe367aca5 100644 --- a/src/core/hle/service/ac_u.cpp +++ b/src/core/hle/service/ac_u.cpp @@ -230,6 +230,24 @@ static void IsConnected(Service::Interface* self) { LOG_WARNING(Service_AC, "(STUBBED) called"); } +/** + * AC_U::SetClientVersion service function + * Inputs: + * 1 : Used SDK Version + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetClientVersion(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const u32 version = cmd_buff[1]; + self->SetVersion(version); + + LOG_WARNING(Service_AC, "(STUBBED) called, version: 0x%08X", version); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010000, CreateDefaultConfig, "CreateDefaultConfig"}, {0x00040006, ConnectAsync, "ConnectAsync"}, @@ -250,7 +268,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"}, {0x003C0042, nullptr, "GetAPSSIDList"}, {0x003E0042, IsConnected, "IsConnected"}, - {0x00400042, nullptr, "SetClientVersion"}, + {0x00400042, SetClientVersion, "SetClientVersion"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp index 1d16f8732..34fdf7f53 100644 --- a/src/core/hle/service/frd/frd.cpp +++ b/src/core/hle/service/frd/frd.cpp @@ -100,6 +100,18 @@ void GetMyScreenName(Service::Interface* self) { LOG_WARNING(Service_FRD, "(STUBBED) called"); } +void SetClientSdkVersion(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const u32 version = cmd_buff[1]; + + self->SetVersion(version); + + LOG_WARNING(Service_FRD, "(STUBBED) called, version: 0x%08X", version); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error +} + void Init() { using namespace Kernel; diff --git a/src/core/hle/service/frd/frd.h b/src/core/hle/service/frd/frd.h index c8283a7f3..e61940ea0 100644 --- a/src/core/hle/service/frd/frd.h +++ b/src/core/hle/service/frd/frd.h @@ -95,6 +95,15 @@ void GetMyFriendKey(Service::Interface* self); */ void GetMyScreenName(Service::Interface* self); +/** + * FRD::SetClientSdkVersion service function + * Inputs: + * 1 : Used SDK Version + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +void SetClientSdkVersion(Service::Interface* self); + /// Initialize FRD service(s) void Init(); diff --git a/src/core/hle/service/frd/frd_u.cpp b/src/core/hle/service/frd/frd_u.cpp index bd1c9c16b..496f29ca9 100644 --- a/src/core/hle/service/frd/frd_u.cpp +++ b/src/core/hle/service/frd/frd_u.cpp @@ -58,7 +58,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x002F0040, nullptr, "AllowHalfAwake"}, {0x00300000, nullptr, "GetServerTypes"}, {0x00310082, nullptr, "GetFriendComment"}, - {0x00320042, nullptr, "SetClientSdkVersion"}, + {0x00320042, SetClientSdkVersion, "SetClientSdkVersion"}, {0x00330000, nullptr, "GetMyApproachContext"}, {0x00340046, nullptr, "AddFriendWithApproach"}, {0x00350082, nullptr, "DecryptApproachContext"}, diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 00edc7622..9ec17b395 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -763,23 +763,27 @@ static void CreateLegacySystemSaveData(Service::Interface* self) { * FS_User::InitializeWithSdkVersion service function. * Inputs: * 0 : 0x08610042 - * 1 : Unknown - * 2 : Unknown - * 3 : Unknown + * 1 : Used SDK Version + * 2 : ProcessId Header + * 3 : placeholder for ProcessId * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ static void InitializeWithSdkVersion(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 unk1 = cmd_buff[1]; - u32 unk2 = cmd_buff[2]; - u32 unk3 = cmd_buff[3]; + const u32 version = cmd_buff[1]; + self->SetVersion(version); - cmd_buff[1] = RESULT_SUCCESS.raw; - - LOG_WARNING(Service_FS, "(STUBBED) called unk1=0x%08X, unk2=0x%08X, unk3=0x%08X", unk1, unk2, - unk3); + if (cmd_buff[2] == IPC::CallingPidDesc()) { + LOG_WARNING(Service_FS, "(STUBBED) called, version: 0x%08X", version); + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_FS, "ProcessId Header must be 0x20"); + cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, + ErrorSummary::WrongArgument, ErrorLevel::Permanent) + .raw; + } } /** diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 710e0e485..78cb761be 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -346,7 +346,7 @@ static void SetAxiConfigQoSMode(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_GSP, "(STUBBED) called mode=0x%08X", mode); + LOG_DEBUG(Service_GSP, "(STUBBED) called mode=0x%08X", mode); } /** diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index 563341504..1f851d328 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp @@ -287,6 +287,24 @@ static void SetAllowShellClosed(Service::Interface* self) { LOG_WARNING(Service_MIC, "(STUBBED) called, allow_shell_closed=%u", allow_shell_closed); } +/** + * MIC_U::SetClientVersion service function + * Inputs: + * 1 : Used SDK Version + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetClientVersion(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const u32 version = cmd_buff[1]; + self->SetVersion(version); + + LOG_WARNING(Service_MIC, "(STUBBED) called, version: 0x%08X", version); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010042, MapSharedMem, "MapSharedMem"}, {0x00020000, UnmapSharedMem, "UnmapSharedMem"}, @@ -303,7 +321,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x000D0040, SetClamp, "SetClamp"}, {0x000E0000, GetClamp, "GetClamp"}, {0x000F0040, SetAllowShellClosed, "SetAllowShellClosed"}, - {0x00100040, nullptr, "SetClientSDKVersion"}, + {0x00100040, SetClientVersion, "SetClientVersion"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 39b5ffaae..29daacfc4 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -29,6 +29,10 @@ public: return GetPortName(); } + virtual void SetVersion(u32 raw_version) { + version.raw = raw_version; + } + typedef void (*Function)(Interface*); struct FunctionInfo { @@ -58,6 +62,14 @@ protected: void Register(const FunctionInfo* functions, size_t n); + union { + u32 raw; + BitField<0, 8, u32> major; + BitField<8, 8, u32> minor; + BitField<16, 8, u32> build; + BitField<24, 8, u32> revision; + } version = {}; + private: boost::container::flat_map<u32, FunctionInfo> m_functions; }; diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 45dedea68..cfba82e51 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -8,7 +8,10 @@ #include "common/color.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/math_util.h" #include "common/microprofile.h" +#include "common/thread.h" +#include "common/timer.h" #include "common/vector_math.h" #include "core/core_timing.h" #include "core/hle/service/gsp_gpu.h" @@ -35,6 +38,14 @@ const u64 frame_ticks = 268123480ull / 60; static int vblank_event; /// Total number of frames drawn static u64 frame_count; +/// Start clock for frame limiter +static u32 time_point; +/// Total delay caused by slow frames +static float time_delay; +constexpr float FIXED_FRAME_TIME = 1000.0f / 60; +// Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher +// values increases time needed to limit frame rate after spikes +constexpr float MAX_LAG_TIME = 18; template <typename T> inline void Read(T& var, const u32 raw_addr) { @@ -512,6 +523,21 @@ template void Write<u32>(u32 addr, const u32 data); template void Write<u16>(u32 addr, const u16 data); template void Write<u8>(u32 addr, const u8 data); +static void FrameLimiter() { + time_delay += FIXED_FRAME_TIME; + time_delay = MathUtil::Clamp(time_delay, -MAX_LAG_TIME, MAX_LAG_TIME); + s32 desired_time = static_cast<s32>(time_delay); + s32 elapsed_time = static_cast<s32>(Common::Timer::GetTimeMs() - time_point); + + if (elapsed_time < desired_time) { + Common::SleepCurrentThread(desired_time - elapsed_time); + } + + u32 frame_time = Common::Timer::GetTimeMs() - time_point; + + time_delay -= frame_time; +} + /// Update hardware static void VBlankCallback(u64 userdata, int cycles_late) { frame_count++; @@ -528,6 +554,12 @@ static void VBlankCallback(u64 userdata, int cycles_late) { // Check for user input updates Service::HID::Update(); + if (!Settings::values.use_vsync && Settings::values.toggle_framelimit) { + FrameLimiter(); + } + + time_point = Common::Timer::GetTimeMs(); + // Reschedule recurrent event CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event); } @@ -563,6 +595,7 @@ void Init() { framebuffer_sub.active_fb = 0; frame_count = 0; + time_point = Common::Timer::GetTimeMs(); vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback); CoreTiming::ScheduleEvent(frame_ticks, vblank_event); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 05f41f798..626e06cd9 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -21,6 +21,7 @@ void Apply() { VideoCore::g_hw_renderer_enabled = values.use_hw_renderer; VideoCore::g_shader_jit_enabled = values.use_shader_jit; VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution; + VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit; if (VideoCore::g_emu_window) { auto layout = VideoCore::g_emu_window->GetFramebufferLayout(); diff --git a/src/core/settings.h b/src/core/settings.h index 7470fdbeb..db4c8fada 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -90,6 +90,7 @@ struct Values { bool use_shader_jit; bool use_scaled_resolution; bool use_vsync; + bool toggle_framelimit; LayoutOption layout_option; bool swap_screen; diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 99bd59a69..b2db609ec 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -40,7 +40,7 @@ namespace Pica { // field offset. Otherwise, the compiler will fail to compile this code. #define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) \ ((typename std::enable_if<backup_workaround_index == PICA_REG_INDEX(field_name), \ - size_t>::type) PICA_REG_INDEX(field_name)) + size_t>::type)PICA_REG_INDEX(field_name)) #endif // _MSC_VER struct Regs { diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index 6c4bbed33..b9f5d4533 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp @@ -562,9 +562,9 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader }; if ((texture.config.wrap_s == Regs::TextureConfig::ClampToBorder && - (s < 0 || s >= texture.config.width)) || + (s < 0 || static_cast<u32>(s) >= texture.config.width)) || (texture.config.wrap_t == Regs::TextureConfig::ClampToBorder && - (t < 0 || t >= texture.config.height))) { + (t < 0 || static_cast<u32>(t) >= texture.config.height))) { auto border_color = texture.config.border_color; texture_color[i] = {border_color.r, border_color.g, border_color.b, border_color.a}; @@ -946,8 +946,8 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader // Blend the fog for (unsigned i = 0; i < 3; i++) { - combiner_output[i] = - fog_factor * combiner_output[i] + (1.0f - fog_factor) * fog_color[i]; + combiner_output[i] = static_cast<u8>(fog_factor * combiner_output[i] + + (1.0f - fog_factor) * fog_color[i]); } } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index d4d5903ce..ba65db419 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -213,12 +213,16 @@ void RasterizerOpenGL::DrawTriangles() { // Scissor checks are window-, not viewport-relative, which means that if the cached texture // sub-rect changes, the scissor bounds also need to be updated. - GLint scissor_x1 = rect.left + regs.scissor_test.x1 * color_surface->res_scale_width; - GLint scissor_y1 = rect.bottom + regs.scissor_test.y1 * color_surface->res_scale_height; + GLint scissor_x1 = + static_cast<GLint>(rect.left + regs.scissor_test.x1 * color_surface->res_scale_width); + GLint scissor_y1 = + static_cast<GLint>(rect.bottom + regs.scissor_test.y1 * color_surface->res_scale_height); // x2, y2 have +1 added to cover the entire pixel area, otherwise you might get cracks when // scaling or doing multisampling. - GLint scissor_x2 = rect.left + (regs.scissor_test.x2 + 1) * color_surface->res_scale_width; - GLint scissor_y2 = rect.bottom + (regs.scissor_test.y2 + 1) * color_surface->res_scale_height; + GLint scissor_x2 = + static_cast<GLint>(rect.left + (regs.scissor_test.x2 + 1) * color_surface->res_scale_width); + GLint scissor_y2 = static_cast<GLint>( + rect.bottom + (regs.scissor_test.y2 + 1) * color_surface->res_scale_height); if (uniform_block_data.data.scissor_x1 != scissor_x1 || uniform_block_data.data.scissor_x2 != scissor_x2 || @@ -711,7 +715,11 @@ bool RasterizerOpenGL::AccelerateDisplayTransfer(const GPU::Regs::DisplayTransfe CachedSurface src_params; src_params.addr = config.GetPhysicalInputAddress(); - src_params.width = config.output_width; + // It's important to use the correct source input width to properly skip over parts of the input + // image which will be cropped from the output but still affect the stride of the input image. + src_params.width = config.input_width; + // Using the output's height is fine because we don't read or skip over the remaining part of + // the image, and it allows for smaller texture cache lookup rectangles. src_params.height = config.output_height; src_params.is_tiled = !config.input_linear; src_params.pixel_format = CachedSurface::PixelFormatFromGPUPixelFormat(config.input_format); @@ -732,6 +740,11 @@ bool RasterizerOpenGL::AccelerateDisplayTransfer(const GPU::Regs::DisplayTransfe return false; } + // Adjust the source rectangle to take into account parts of the input lines being cropped + if (config.input_width > config.output_width) { + src_rect.right -= (config.input_width - config.output_width) * src_surface->res_scale_width; + } + // Require destination surface to have same resolution scale as source to preserve scaling dst_params.res_scale_width = src_surface->res_scale_width; dst_params.res_scale_height = src_surface->res_scale_height; @@ -934,7 +947,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con src_params.addr = framebuffer_addr; src_params.width = config.width; src_params.height = config.height; - src_params.stride = pixel_stride; + src_params.pixel_stride = pixel_stride; src_params.is_tiled = false; src_params.pixel_format = CachedSurface::PixelFormatFromGPUPixelFormat(config.color_format); @@ -1070,7 +1083,9 @@ void RasterizerOpenGL::SetShader() { GLint block_size; glGetActiveUniformBlockiv(current_shader->shader.handle, block_index, GL_UNIFORM_BLOCK_DATA_SIZE, &block_size); - ASSERT_MSG(block_size == sizeof(UniformData), "Uniform block size did not match!"); + ASSERT_MSG(block_size == sizeof(UniformData), + "Uniform block size did not match! Got %d, expected %zu", + static_cast<int>(block_size), sizeof(UniformData)); glUniformBlockBinding(current_shader->shader.handle, block_index, 0); // Update uniforms diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 5cbad9b43..61f6e767f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -158,24 +158,21 @@ bool RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex, buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; } - if (OpenGLState::CheckFBStatus(GL_READ_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return false; - } + bool can_blit = OpenGLState::CheckFBStatus(GL_READ_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE && + OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; - if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return false; + if (can_blit) { + glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, + dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, buffers, + buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST); } - glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left, - dst_rect.top, dst_rect.right, dst_rect.bottom, buffers, - buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST); - // Restore previous framebuffer bindings cur_state.draw.read_framebuffer = old_fbs[0]; cur_state.draw.draw_framebuffer = old_fbs[1]; cur_state.Apply(); - return true; + return can_blit; } bool RasterizerCacheOpenGL::TryBlitSurfaces(CachedSurface* src_surface, @@ -291,6 +288,9 @@ CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bo MICROPROFILE_SCOPE(OpenGL_SurfaceUpload); + // Stride only applies to linear images. + ASSERT(params.pixel_stride == 0 || !params.is_tiled); + std::shared_ptr<CachedSurface> new_surface = std::make_shared<CachedSurface>(); new_surface->addr = params.addr; @@ -299,7 +299,7 @@ CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bo new_surface->texture.Create(); new_surface->width = params.width; new_surface->height = params.height; - new_surface->stride = params.stride; + new_surface->pixel_stride = params.pixel_stride; new_surface->res_scale_width = params.res_scale_width; new_surface->res_scale_height = params.res_scale_height; @@ -325,14 +325,15 @@ CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bo cur_state.Apply(); glActiveTexture(GL_TEXTURE0); - glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)new_surface->stride); if (!new_surface->is_tiled) { // TODO: Ensure this will always be a color format, not a depth or other format ASSERT((size_t)new_surface->pixel_format < fb_format_tuples.size()); const FormatTuple& tuple = fb_format_tuples[(unsigned int)params.pixel_format]; + glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)new_surface->pixel_stride); glTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, params.width, params.height, 0, tuple.format, tuple.type, texture_src_data); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); } else { SurfaceType type = CachedSurface::GetFormatType(new_surface->pixel_format); if (type != SurfaceType::Depth && type != SurfaceType::DepthStencil) { @@ -391,7 +392,6 @@ CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bo 0, tuple.format, tuple.type, temp_fb_depth_buffer.data()); } } - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); // If not 1x scale, blit 1x texture to a new scaled texture and replace texture in surface if (new_surface->res_scale_width != 1.f || new_surface->res_scale_height != 1.f) { @@ -701,13 +701,14 @@ void RasterizerCacheOpenGL::FlushSurface(CachedSurface* surface) { cur_state.Apply(); glActiveTexture(GL_TEXTURE0); - glPixelStorei(GL_PACK_ROW_LENGTH, (GLint)surface->stride); if (!surface->is_tiled) { // TODO: Ensure this will always be a color format, not a depth or other format ASSERT((size_t)surface->pixel_format < fb_format_tuples.size()); const FormatTuple& tuple = fb_format_tuples[(unsigned int)surface->pixel_format]; + glPixelStorei(GL_PACK_ROW_LENGTH, (GLint)surface->pixel_stride); glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, dst_buffer); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); } else { SurfaceType type = CachedSurface::GetFormatType(surface->pixel_format); if (type != SurfaceType::Depth && type != SurfaceType::DepthStencil) { @@ -750,7 +751,6 @@ void RasterizerCacheOpenGL::FlushSurface(CachedSurface* surface) { false); } } - glPixelStorei(GL_PACK_ROW_LENGTH, 0); surface->dirty = false; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 849530d86..32abfbaf5 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -171,7 +171,8 @@ struct CachedSurface { OGLTexture texture; u32 width; u32 height; - u32 stride = 0; + /// Stride between lines, in pixels. Only valid for images in linear format. + u32 pixel_stride = 0; float res_scale_width = 1.f; float res_scale_height = 1.f; diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index fe07aa6eb..4da241d83 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -4,6 +4,7 @@ #include <vector> #include <glad/glad.h> +#include "common/assert.h" #include "common/logging/log.h" #include "video_core/renderer_opengl/gl_shader_util.h" @@ -31,7 +32,7 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) { if (info_log_length > 1) { std::vector<char> vertex_shader_error(info_log_length); glGetShaderInfoLog(vertex_shader_id, info_log_length, nullptr, &vertex_shader_error[0]); - if (result) { + if (result == GL_TRUE) { LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]); } else { LOG_ERROR(Render_OpenGL, "Error compiling vertex shader:\n%s", &vertex_shader_error[0]); @@ -51,7 +52,7 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) { if (info_log_length > 1) { std::vector<char> fragment_shader_error(info_log_length); glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr, &fragment_shader_error[0]); - if (result) { + if (result == GL_TRUE) { LOG_DEBUG(Render_OpenGL, "%s", &fragment_shader_error[0]); } else { LOG_ERROR(Render_OpenGL, "Error compiling fragment shader:\n%s", @@ -75,13 +76,20 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) { if (info_log_length > 1) { std::vector<char> program_error(info_log_length); glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error[0]); - if (result) { + if (result == GL_TRUE) { LOG_DEBUG(Render_OpenGL, "%s", &program_error[0]); } else { LOG_ERROR(Render_OpenGL, "Error linking shader:\n%s", &program_error[0]); } } + // If the program linking failed at least one of the shaders was probably bad + if (result == GL_FALSE) { + LOG_ERROR(Render_OpenGL, "Vertex shader:\n%s", vertex_shader); + LOG_ERROR(Render_OpenGL, "Fragment shader:\n%s", fragment_shader); + } + ASSERT_MSG(result == GL_TRUE, "Shader not linked"); + glDeleteShader(vertex_shader_id); glDeleteShader(fragment_shader_id); diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp index 211c703ab..c96110bb2 100644 --- a/src/video_core/shader/shader_jit_x64.cpp +++ b/src/video_core/shader/shader_jit_x64.cpp @@ -102,11 +102,11 @@ static const X64Reg SETUP = R9; /// The two 32-bit VS address offset registers set by the MOVA instruction static const X64Reg ADDROFFS_REG_0 = R10; static const X64Reg ADDROFFS_REG_1 = R11; -/// VS loop count register +/// VS loop count register (Multiplied by 16) static const X64Reg LOOPCOUNT_REG = R12; /// Current VS loop iteration number (we could probably use LOOPCOUNT_REG, but this quicker) static const X64Reg LOOPCOUNT = RSI; -/// Number to increment LOOPCOUNT_REG by on each loop iteration +/// Number to increment LOOPCOUNT_REG by on each loop iteration (Multiplied by 16) static const X64Reg LOOPINC = RDI; /// Result of the previous CMP instruction for the X-component comparison static const X64Reg COND0 = R13; @@ -491,7 +491,7 @@ void JitShader::Compile_FLR(Instruction instr) { if (Common::GetCPUCaps().sse4_1) { ROUNDFLOORPS(SRC1, R(SRC1)); } else { - CVTPS2DQ(SRC1, R(SRC1)); + CVTTPS2DQ(SRC1, R(SRC1)); CVTDQ2PS(SRC1, R(SRC1)); } @@ -718,15 +718,18 @@ void JitShader::Compile_LOOP(Instruction instr) { looping = true; + // This decodes the fields from the integer uniform at index instr.flow_control.int_uniform_id. + // The Y (LOOPCOUNT_REG) and Z (LOOPINC) component are kept multiplied by 16 (Left shifted by + // 4 bits) to be used as an offset into the 16-byte vector registers later int offset = ShaderSetup::UniformOffset(RegisterType::IntUniform, instr.flow_control.int_uniform_id); MOV(32, R(LOOPCOUNT), MDisp(SETUP, offset)); MOV(32, R(LOOPCOUNT_REG), R(LOOPCOUNT)); - SHR(32, R(LOOPCOUNT_REG), Imm8(8)); - AND(32, R(LOOPCOUNT_REG), Imm32(0xff)); // Y-component is the start + SHR(32, R(LOOPCOUNT_REG), Imm8(4)); + AND(32, R(LOOPCOUNT_REG), Imm32(0xFF0)); // Y-component is the start MOV(32, R(LOOPINC), R(LOOPCOUNT)); - SHR(32, R(LOOPINC), Imm8(16)); - MOVZX(32, 8, LOOPINC, R(LOOPINC)); // Z-component is the incrementer + SHR(32, R(LOOPINC), Imm8(12)); + AND(32, R(LOOPINC), Imm32(0xFF0)); // Z-component is the incrementer MOVZX(32, 8, LOOPCOUNT, R(LOOPCOUNT)); // X-component is iteration count ADD(32, R(LOOPCOUNT), Imm8(1)); // Iteration count is X-component + 1 diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 83e33dfc2..8db882f59 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -21,6 +21,7 @@ std::atomic<bool> g_hw_renderer_enabled; std::atomic<bool> g_shader_jit_enabled; std::atomic<bool> g_scaled_resolution_enabled; std::atomic<bool> g_vsync_enabled; +std::atomic<bool> g_toggle_framelimit_enabled; /// Initialize the video core bool Init(EmuWindow* emu_window) { diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index e2d725ab1..c397c1974 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -38,6 +38,7 @@ extern EmuWindow* g_emu_window; ///< Emu window extern std::atomic<bool> g_hw_renderer_enabled; extern std::atomic<bool> g_shader_jit_enabled; extern std::atomic<bool> g_scaled_resolution_enabled; +extern std::atomic<bool> g_toggle_framelimit_enabled; /// Start the video core void Start(); |