diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index 442a4e8644..b901f2f691 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -21,7 +21,16 @@ import java.lang.ref.WeakReference; */ public final class NativeLibrary { - public static WeakReference sEmulationActivity = new WeakReference<>(null); + private static WeakReference sEmulationActivity = new WeakReference<>(null); + + /** + * Returns the current instance of EmulationActivity. + * There should only ever be one EmulationActivity instantiated. + */ + public static EmulationActivity getEmulationActivity() + { + return sEmulationActivity.get(); + } /** * Button type for use in onTouchEvent diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java index d83c348ea2..babf5069e5 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java @@ -31,7 +31,7 @@ public class Java_GCAdapter private static void RequestPermission() { - Context context = NativeLibrary.sEmulationActivity.get(); + Context context = NativeLibrary.getEmulationActivity(); if (context != null) { HashMap devices = manager.getDeviceList(); @@ -141,7 +141,7 @@ public class Java_GCAdapter } } - final Activity emulationActivity = NativeLibrary.sEmulationActivity.get(); + final Activity emulationActivity = NativeLibrary.getEmulationActivity(); if (emulationActivity != null) { emulationActivity.runOnUiThread(() -> Toast.makeText(emulationActivity, diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java index de11e4fd62..4ecc1a64ca 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java @@ -34,7 +34,7 @@ public class Java_WiimoteAdapter private static void RequestPermission() { - Context context = NativeLibrary.sEmulationActivity.get(); + Context context = NativeLibrary.getEmulationActivity(); if (context != null) { HashMap devices = manager.getDeviceList(); diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 686053496e..9ffa7d2b0d 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -52,6 +52,7 @@ #include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoBackendBase.h" +#include "../../Core/Common/WindowSystemInfo.h" #include "jni/AndroidCommon/AndroidCommon.h" #include "jni/AndroidCommon/IDCache.h" #include "jni/ButtonManager.h" @@ -589,15 +590,57 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ReloadWiimot Wiimote::LoadConfig(); } -static void Run(const std::vector& paths, bool first_open, +// Returns the scale factor for imgui rendering. +// Based on the scaledDensity of the device's display metrics. +static float GetRenderSurfaceScale(JNIEnv* env) +{ + // NativeLibrary emulation_activity = NativeLibrary.getEmulationActivity(); + jclass native_library_class = env->FindClass("org/dolphinemu/dolphinemu/NativeLibrary"); + jmethodID get_emulation_activity_method = + env->GetStaticMethodID(native_library_class, "getEmulationActivity", + "()Lorg/dolphinemu/dolphinemu/activities/EmulationActivity;"); + jobject emulation_activity = + env->CallStaticObjectMethod(native_library_class, get_emulation_activity_method); + + // WindowManager window_manager = emulation_activity.getWindowManager(); + jmethodID get_window_manager_method = + env->GetMethodID(env->GetObjectClass(emulation_activity), "getWindowManager", + "()Landroid/view/WindowManager;"); + jobject window_manager = env->CallObjectMethod(emulation_activity, get_window_manager_method); + + // Display display = window_manager.getDisplay(); + jmethodID get_display_method = env->GetMethodID(env->GetObjectClass(window_manager), + "getDefaultDisplay", "()Landroid/view/Display;"); + jobject display = env->CallObjectMethod(window_manager, get_display_method); + + // DisplayMetrics metrics = new DisplayMetrics(); + jclass display_metrics_class = env->FindClass("android/util/DisplayMetrics"); + jmethodID display_metrics_constructor = env->GetMethodID(display_metrics_class, "", "()V"); + jobject metrics = env->NewObject(display_metrics_class, display_metrics_constructor); + + // display.getMetrics(metrics); + jmethodID get_metrics_method = env->GetMethodID(env->GetObjectClass(display), "getMetrics", + "(Landroid/util/DisplayMetrics;)V"); + env->CallVoidMethod(display, get_metrics_method, metrics); + + // float scaled_density = metrics.scaledDensity; + jfieldID scaled_density_field = + env->GetFieldID(env->GetObjectClass(metrics), "scaledDensity", "F"); + float scaled_density = env->GetFloatField(metrics, scaled_density_field); + __android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Using %f for render surface scale.", + scaled_density); + + // cleanup + env->DeleteLocalRef(metrics); + return scaled_density; +} + +static void Run(JNIEnv* env, const std::vector& paths, bool first_open, std::optional savestate_path = {}, bool delete_savestate = false) { ASSERT(!paths.empty()); __android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Running : %s", paths[0].c_str()); - // Install our callbacks - OSD::AddCallback(OSD::CallbackType::Shutdown, ButtonManager::Shutdown); - RegisterMsgAlertHandler(&MsgAlert); Common::AndroidSetReportHandler(&ReportSend); DolphinAnalytics::AndroidSetGetValFunc(&GetAnalyticValue); @@ -617,6 +660,7 @@ static void Run(const std::vector& paths, bool first_open, std::unique_ptr boot = BootParameters::GenerateFromFile(paths, savestate_path); boot->delete_savestate = delete_savestate; WindowSystemInfo wsi(WindowSystemType::Android, nullptr, s_surf); + wsi.render_surface_scale = GetRenderSurfaceScale(env); if (BootManager::BootCore(std::move(boot), wsi)) { ButtonManager::Init(SConfig::GetInstance().GetGameID()); @@ -639,6 +683,7 @@ static void Run(const std::vector& paths, bool first_open, } Core::Shutdown(); + ButtonManager::Shutdown(); UICommon::Shutdown(); guard.unlock(); @@ -652,14 +697,14 @@ static void Run(const std::vector& paths, bool first_open, JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2Z( JNIEnv* env, jobject obj, jobjectArray jPaths, jboolean jfirstOpen) { - Run(JStringArrayToVector(env, jPaths), jfirstOpen); + Run(env, JStringArrayToVector(env, jPaths), jfirstOpen); } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2Ljava_lang_String_2Z( JNIEnv* env, jobject obj, jobjectArray jPaths, jstring jSavestate, jboolean jDeleteSavestate) { - Run(JStringArrayToVector(env, jPaths), false, GetJString(env, jSavestate), jDeleteSavestate); + Run(env, JStringArrayToVector(env, jPaths), false, GetJString(env, jSavestate), jDeleteSavestate); } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ChangeDisc(JNIEnv* env, diff --git a/Source/Core/Common/WindowSystemInfo.h b/Source/Core/Common/WindowSystemInfo.h index 897d034c2c..bea853d445 100644 --- a/Source/Core/Common/WindowSystemInfo.h +++ b/Source/Core/Common/WindowSystemInfo.h @@ -32,4 +32,7 @@ struct WindowSystemInfo // on the platform. e.g. HWND for Windows, Window for X11. If the surface is // set to nullptr, the video backend will run in headless mode. void* render_surface = nullptr; + + // Scale of the render surface. For hidpi systems, this will be >1. + float render_surface_scale = 1.0f; }; diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index 8e6deec6f8..3d987cae90 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -143,6 +143,7 @@ PRIVATE core Qt5::Widgets uicommon + imgui ) if(WIN32) diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index bd0346edd7..694f7b9f13 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -463,6 +463,9 @@ {29f29a19-f141-45ad-9679-5a2923b49da3} + + {4c3b2264-ea73-4a7b-9cfe-65b0fd635ebb} + diff --git a/Source/Core/DolphinQt/HotkeyScheduler.cpp b/Source/Core/DolphinQt/HotkeyScheduler.cpp index cd5c51822f..7cab2eb8a7 100644 --- a/Source/Core/DolphinQt/HotkeyScheduler.cpp +++ b/Source/Core/DolphinQt/HotkeyScheduler.cpp @@ -5,6 +5,7 @@ #include "DolphinQt/HotkeyScheduler.h" #include +#include #include #include @@ -26,6 +27,7 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VideoConfig.h" @@ -286,43 +288,62 @@ void HotkeyScheduler::Run() else if (IsHotkey(HK_NEXT_GAME_WIIMOTE_PROFILE_4)) m_profile_cycler.NextWiimoteProfileForGame(3); - const auto show_msg = [](OSDMessage message) { - if (g_renderer) - g_renderer->ShowOSDMessage(message); + auto ShowVolume = []() { + OSD::AddMessage(std::string("Volume: ") + + (SConfig::GetInstance().m_IsMuted ? + "Muted" : + std::to_string(SConfig::GetInstance().m_Volume)) + + "%"); }; // Volume if (IsHotkey(HK_VOLUME_DOWN)) { - show_msg(OSDMessage::VolumeChanged); settings.DecreaseVolume(3); + ShowVolume(); } if (IsHotkey(HK_VOLUME_UP)) { - show_msg(OSDMessage::VolumeChanged); settings.IncreaseVolume(3); + ShowVolume(); } if (IsHotkey(HK_VOLUME_TOGGLE_MUTE)) { - show_msg(OSDMessage::VolumeChanged); AudioCommon::ToggleMuteVolume(); + ShowVolume(); } // Graphics const auto efb_scale = Config::Get(Config::GFX_EFB_SCALE); + auto ShowEFBScale = []() { + switch (Config::Get(Config::GFX_EFB_SCALE)) + { + case EFB_SCALE_AUTO_INTEGRAL: + OSD::AddMessage("Internal Resolution: Auto (integral)"); + break; + case 1: + OSD::AddMessage("Internal Resolution: Native"); + break; + default: + OSD::AddMessage("Internal Resolution: %dx", g_Config.iEFBScale); + break; + } + }; if (IsHotkey(HK_INCREASE_IR)) { - show_msg(OSDMessage::IRChanged); Config::SetCurrent(Config::GFX_EFB_SCALE, efb_scale + 1); + ShowEFBScale(); } if (IsHotkey(HK_DECREASE_IR)) { - show_msg(OSDMessage::IRChanged); if (efb_scale > EFB_SCALE_AUTO_INTEGRAL) + { Config::SetCurrent(Config::GFX_EFB_SCALE, efb_scale - 1); + ShowEFBScale(); + } } if (IsHotkey(HK_TOGGLE_CROP)) @@ -330,34 +351,55 @@ void HotkeyScheduler::Run() if (IsHotkey(HK_TOGGLE_AR)) { - show_msg(OSDMessage::ARToggled); const int aspect_ratio = (static_cast(Config::Get(Config::GFX_ASPECT_RATIO)) + 1) & 3; Config::SetCurrent(Config::GFX_ASPECT_RATIO, static_cast(aspect_ratio)); + switch (static_cast(aspect_ratio)) + { + case AspectMode::Stretch: + OSD::AddMessage("Stretch"); + break; + case AspectMode::Analog: + OSD::AddMessage("Force 4:3"); + break; + case AspectMode::AnalogWide: + OSD::AddMessage("Force 16:9"); + break; + case AspectMode::Auto: + default: + OSD::AddMessage("Auto"); + break; + } } if (IsHotkey(HK_TOGGLE_EFBCOPIES)) { - show_msg(OSDMessage::EFBCopyToggled); - Config::SetCurrent(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM, - !Config::Get(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM)); + const bool new_value = !Config::Get(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM); + Config::SetCurrent(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM, new_value); + OSD::AddMessage(StringFromFormat("Copy EFB: %s", new_value ? "to Texture" : "to RAM")); } + auto ShowXFBCopies = []() { + OSD::AddMessage(StringFromFormat( + "Copy XFB: %s%s", Config::Get(Config::GFX_HACK_IMMEDIATE_XFB) ? " (Immediate)" : "", + Config::Get(Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM) ? "to Texture" : "to RAM")); + }; + if (IsHotkey(HK_TOGGLE_XFBCOPIES)) { - show_msg(OSDMessage::XFBChanged); Config::SetCurrent(Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM, !Config::Get(Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM)); + ShowXFBCopies(); } if (IsHotkey(HK_TOGGLE_IMMEDIATE_XFB)) { - show_msg(OSDMessage::XFBChanged); - Config::SetCurrent(Config::GFX_HACK_IMMEDIATE_XFB, !Config::Get(Config::GFX_HACK_IMMEDIATE_XFB)); + ShowXFBCopies(); } if (IsHotkey(HK_TOGGLE_FOG)) { - show_msg(OSDMessage::FogToggled); - Config::SetCurrent(Config::GFX_DISABLE_FOG, !Config::Get(Config::GFX_DISABLE_FOG)); + const bool new_value = !Config::Get(Config::GFX_DISABLE_FOG); + Config::SetCurrent(Config::GFX_DISABLE_FOG, new_value); + OSD::AddMessage(StringFromFormat("Fog: %s", new_value ? "Enabled" : "Disabled")); } if (IsHotkey(HK_TOGGLE_DUMPTEXTURES)) @@ -368,22 +410,28 @@ void HotkeyScheduler::Run() Core::SetIsThrottlerTempDisabled(IsHotkey(HK_TOGGLE_THROTTLE, true)); + auto ShowEmulationSpeed = []() { + OSD::AddMessage( + SConfig::GetInstance().m_EmulationSpeed <= 0 ? + "Speed Limit: Unlimited" : + StringFromFormat("Speed Limit: %li%%", + std::lround(SConfig::GetInstance().m_EmulationSpeed * 100.f))); + }; + if (IsHotkey(HK_DECREASE_EMULATION_SPEED)) { - show_msg(OSDMessage::SpeedChanged); - auto speed = SConfig::GetInstance().m_EmulationSpeed - 0.1; speed = (speed <= 0 || (speed >= 0.95 && speed <= 1.05)) ? 1.0 : speed; SConfig::GetInstance().m_EmulationSpeed = speed; + ShowEmulationSpeed(); } if (IsHotkey(HK_INCREASE_EMULATION_SPEED)) { - show_msg(OSDMessage::SpeedChanged); - auto speed = SConfig::GetInstance().m_EmulationSpeed + 0.1; speed = (speed >= 0.95 && speed <= 1.05) ? 1.0 : speed; SConfig::GetInstance().m_EmulationSpeed = speed; + ShowEmulationSpeed(); } // Slot Saving / Loading diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index ef9557e3db..9caf98d966 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -164,6 +164,7 @@ static WindowSystemInfo GetWindowSystemInfo(QWindow* window) else wsi.render_surface = window ? reinterpret_cast(window->winId()) : nullptr; #endif + wsi.render_surface_scale = window ? static_cast(window->devicePixelRatio()) : 1.0f; return wsi; } diff --git a/Source/Core/DolphinQt/RenderWidget.cpp b/Source/Core/DolphinQt/RenderWidget.cpp index 4be5f26b1d..6953de0798 100644 --- a/Source/Core/DolphinQt/RenderWidget.cpp +++ b/Source/Core/DolphinQt/RenderWidget.cpp @@ -17,6 +17,8 @@ #include #include +#include "imgui.h" + #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/State.h" @@ -26,6 +28,7 @@ #include "DolphinQt/Resources.h" #include "DolphinQt/Settings.h" +#include "VideoCommon/RenderBase.h" #include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VideoConfig.h" @@ -49,6 +52,8 @@ RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent) connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) { SetFillBackground(SConfig::GetInstance().bRenderToMain && state == Core::State::Uninitialized); + if (state == Core::State::Running) + SetImGuiKeyMap(); }); // We have to use Qt::DirectConnection here because we don't want those signals to get queued @@ -153,6 +158,8 @@ void RenderWidget::showFullScreen() bool RenderWidget::event(QEvent* event) { + PassEventToImGui(event); + switch (event->type()) { case QEvent::Paint: @@ -244,3 +251,83 @@ void RenderWidget::OnFreeLookMouseMove(QMouseEvent* event) m_last_mouse[0] = event->x(); m_last_mouse[1] = event->y(); } + +void RenderWidget::PassEventToImGui(const QEvent* event) +{ + if (!Core::IsRunningAndStarted()) + return; + + switch (event->type()) + { + case QEvent::KeyPress: + case QEvent::KeyRelease: + { + // As the imgui KeysDown array is only 512 elements wide, and some Qt keys which + // we need to track (e.g. alt) are above this value, we mask the lower 9 bits. + // Even masked, the key codes are still unique, so conflicts aren't an issue. + // The actual text input goes through AddInputCharactersUTF8(). + const QKeyEvent* key_event = static_cast(event); + const bool is_down = event->type() == QEvent::KeyPress; + const int key = (key_event->key() & 0x1FF); + auto lock = g_renderer->GetImGuiLock(); + if (key < ArraySize(ImGui::GetIO().KeysDown)) + ImGui::GetIO().KeysDown[key] = is_down; + + if (is_down) + { + auto utf8 = key_event->text().toUtf8(); + ImGui::GetIO().AddInputCharactersUTF8(utf8.constData()); + } + } + break; + + case QEvent::MouseMove: + { + auto lock = g_renderer->GetImGuiLock(); + ImGui::GetIO().MousePos.x = static_cast(event)->x(); + ImGui::GetIO().MousePos.y = static_cast(event)->y(); + } + break; + + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + { + auto lock = g_renderer->GetImGuiLock(); + const u32 button_mask = static_cast(static_cast(event)->buttons()); + for (size_t i = 0; i < ArraySize(ImGui::GetIO().MouseDown); i++) + ImGui::GetIO().MouseDown[i] = (button_mask & (1u << i)) != 0; + } + break; + + default: + break; + } +} + +void RenderWidget::SetImGuiKeyMap() +{ + static const int key_map[][2] = {{ImGuiKey_Tab, Qt::Key_Tab}, + {ImGuiKey_LeftArrow, Qt::Key_Left}, + {ImGuiKey_RightArrow, Qt::Key_Right}, + {ImGuiKey_UpArrow, Qt::Key_Up}, + {ImGuiKey_DownArrow, Qt::Key_Down}, + {ImGuiKey_PageUp, Qt::Key_PageUp}, + {ImGuiKey_PageDown, Qt::Key_PageDown}, + {ImGuiKey_Home, Qt::Key_Home}, + {ImGuiKey_End, Qt::Key_End}, + {ImGuiKey_Insert, Qt::Key_Insert}, + {ImGuiKey_Delete, Qt::Key_Delete}, + {ImGuiKey_Backspace, Qt::Key_Backspace}, + {ImGuiKey_Space, Qt::Key_Space}, + {ImGuiKey_Enter, Qt::Key_Enter}, + {ImGuiKey_Escape, Qt::Key_Escape}, + {ImGuiKey_A, Qt::Key_A}, + {ImGuiKey_C, Qt::Key_C}, + {ImGuiKey_V, Qt::Key_V}, + {ImGuiKey_X, Qt::Key_X}, + {ImGuiKey_Y, Qt::Key_Y}, + {ImGuiKey_Z, Qt::Key_Z}}; + auto lock = g_renderer->GetImGuiLock(); + for (size_t i = 0; i < ArraySize(key_map); i++) + ImGui::GetIO().KeyMap[key_map[i][0]] = (key_map[i][1] & 0x1FF); +} diff --git a/Source/Core/DolphinQt/RenderWidget.h b/Source/Core/DolphinQt/RenderWidget.h index 4d7e53f24e..a9099fd5a7 100644 --- a/Source/Core/DolphinQt/RenderWidget.h +++ b/Source/Core/DolphinQt/RenderWidget.h @@ -36,6 +36,8 @@ private: void OnKeepOnTopChanged(bool top); void SetFillBackground(bool fill); void OnFreeLookMouseMove(QMouseEvent* event); + void PassEventToImGui(const QEvent* event); + void SetImGuiKeyMap(); void dragEnterEvent(QDragEnterEvent* event) override; void dropEvent(QDropEvent* event) override; diff --git a/Source/Core/VideoBackends/D3D/D3DUtil.cpp b/Source/Core/VideoBackends/D3D/D3DUtil.cpp index 5663a8f3ae..0ad02c1ca4 100644 --- a/Source/Core/VideoBackends/D3D/D3DUtil.cpp +++ b/Source/Core/VideoBackends/D3D/D3DUtil.cpp @@ -98,390 +98,7 @@ private: std::list observers; }; -class CD3DFont -{ -public: - CD3DFont(); - // 2D text drawing function - // Initializing and destroying device-dependent objects - int Init(); - int Shutdown(); - int DrawTextScaled(float x, float y, float size, float spacing, u32 dwColor, - const std::string& text); - -private: - ID3D11ShaderResourceView* m_pTexture; - ID3D11Buffer* m_pVB; - ID3D11InputLayout* m_InputLayout; - ID3D11PixelShader* m_pshader; - ID3D11VertexShader* m_vshader; - ID3D11BlendState* m_blendstate; - ID3D11RasterizerState* m_raststate; - const int m_dwTexWidth; - const int m_dwTexHeight; - unsigned int m_LineHeight; - float m_fTexCoords[128 - 32][4]; -}; - -static CD3DFont font; static UtilVertexBuffer* util_vbuf = nullptr; - -#define MAX_NUM_VERTICES 50 * 6 -struct FONT2DVERTEX -{ - float x, y, z; - float col[4]; - float tu, tv; -}; - -inline FONT2DVERTEX InitFont2DVertex(float x, float y, u32 color, float tu, float tv) -{ - FONT2DVERTEX v; - v.x = x; - v.y = y; - v.z = 0; - v.tu = tu; - v.tv = tv; - v.col[0] = ((float)((color >> 16) & 0xFF)) / 255.f; - v.col[1] = ((float)((color >> 8) & 0xFF)) / 255.f; - v.col[2] = ((float)((color >> 0) & 0xFF)) / 255.f; - v.col[3] = ((float)((color >> 24) & 0xFF)) / 255.f; - return v; -} - -CD3DFont::CD3DFont() : m_dwTexWidth(512), m_dwTexHeight(512) -{ - m_pTexture = nullptr; - m_pVB = nullptr; - m_InputLayout = nullptr; - m_pshader = nullptr; - m_vshader = nullptr; -} - -const char fontpixshader[] = {"Texture2D tex2D;\n" - "SamplerState linearSampler\n" - "{\n" - " Filter = MIN_MAG_MIP_LINEAR;\n" - " AddressU = D3D11_TEXTURE_ADDRESS_BORDER;\n" - " AddressV = D3D11_TEXTURE_ADDRESS_BORDER;\n" - " BorderColor = float4(0.f, 0.f, 0.f, 0.f);\n" - "};\n" - "struct PS_INPUT\n" - "{\n" - " float4 pos : SV_POSITION;\n" - " float4 col : COLOR;\n" - " float2 tex : TEXCOORD;\n" - "};\n" - "float4 main( PS_INPUT input ) : SV_Target\n" - "{\n" - " return tex2D.Sample( linearSampler, input.tex ) * input.col;\n" - "};\n"}; - -const char fontvertshader[] = {"struct VS_INPUT\n" - "{\n" - " float4 pos : POSITION;\n" - " float4 col : COLOR;\n" - " float2 tex : TEXCOORD;\n" - "};\n" - "struct PS_INPUT\n" - "{\n" - " float4 pos : SV_POSITION;\n" - " float4 col : COLOR;\n" - " float2 tex : TEXCOORD;\n" - "};\n" - "PS_INPUT main( VS_INPUT input )\n" - "{\n" - " PS_INPUT output;\n" - " output.pos = input.pos;\n" - " output.col = input.col;\n" - " output.tex = input.tex;\n" - " return output;\n" - "};\n"}; - -int CD3DFont::Init() -{ - // Create vertex buffer for the letters - HRESULT hr; - - // Prepare to create a bitmap - unsigned int* pBitmapBits; - BITMAPINFO bmi; - ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER)); - bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bmi.bmiHeader.biWidth = (int)m_dwTexWidth; - bmi.bmiHeader.biHeight = -(int)m_dwTexHeight; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biBitCount = 32; - - // Create a DC and a bitmap for the font - HDC hDC = CreateCompatibleDC(nullptr); - HBITMAP hbmBitmap = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, (void**)&pBitmapBits, nullptr, 0); - SetMapMode(hDC, MM_TEXT); - - // create a GDI font - HFONT hFont = - CreateFont(24, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, - CLIP_DEFAULT_PRECIS, PROOF_QUALITY, VARIABLE_PITCH, _T("Tahoma")); - if (nullptr == hFont) - return E_FAIL; - - HGDIOBJ hOldbmBitmap = SelectObject(hDC, hbmBitmap); - HGDIOBJ hOldFont = SelectObject(hDC, hFont); - - // Set text properties - SetTextColor(hDC, 0xFFFFFF); - SetBkColor(hDC, 0); - SetTextAlign(hDC, TA_TOP); - - TEXTMETRICW tm; - GetTextMetricsW(hDC, &tm); - m_LineHeight = tm.tmHeight; - - // Loop through all printable characters and output them to the bitmap - // Meanwhile, keep track of the corresponding tex coords for each character. - int x = 0, y = 0; - char str[2] = "\0"; - for (int c = 0; c < 127 - 32; c++) - { - str[0] = c + 32; - SIZE size; - GetTextExtentPoint32A(hDC, str, 1, &size); - if ((int)(x + size.cx + 1) > m_dwTexWidth) - { - x = 0; - y += m_LineHeight; - } - - ExtTextOutA(hDC, x + 1, y + 0, ETO_OPAQUE | ETO_CLIPPED, nullptr, str, 1, nullptr); - m_fTexCoords[c][0] = ((float)(x + 0)) / m_dwTexWidth; - m_fTexCoords[c][1] = ((float)(y + 0)) / m_dwTexHeight; - m_fTexCoords[c][2] = ((float)(x + 0 + size.cx)) / m_dwTexWidth; - m_fTexCoords[c][3] = ((float)(y + 0 + size.cy)) / m_dwTexHeight; - - x += size.cx + 3; // 3 to work around annoying ij conflict (part of the j ends up with the i) - } - - // Create a new texture for the font - // possible optimization: store the converted data in a buffer and fill the texture on creation. - // That way, we can use a static texture - ID3D11Texture2D* buftex; - D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC( - DXGI_FORMAT_R8G8B8A8_UNORM, m_dwTexWidth, m_dwTexHeight, 1, 1, D3D11_BIND_SHADER_RESOURCE, - D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); - hr = device->CreateTexture2D(&texdesc, nullptr, &buftex); - if (FAILED(hr)) - { - PanicAlert("Failed to create font texture"); - return hr; - } - D3D::SetDebugObjectName(buftex, "texture of a CD3DFont object"); - - // Lock the surface and write the alpha values for the set pixels - D3D11_MAPPED_SUBRESOURCE texmap; - hr = context->Map(buftex, 0, D3D11_MAP_WRITE_DISCARD, 0, &texmap); - if (FAILED(hr)) - PanicAlert("Failed to map a texture at %s %d\n", __FILE__, __LINE__); - - for (y = 0; y < m_dwTexHeight; y++) - { - u32* pDst32 = (u32*)((u8*)texmap.pData + y * texmap.RowPitch); - for (x = 0; x < m_dwTexWidth; x++) - { - const u8 bAlpha = (pBitmapBits[m_dwTexWidth * y + x] & 0xff); - *pDst32++ = (((bAlpha << 4) | bAlpha) << 24) | 0xFFFFFF; - } - } - - // Done updating texture, so clean up used objects - context->Unmap(buftex, 0); - hr = D3D::device->CreateShaderResourceView(buftex, nullptr, &m_pTexture); - if (FAILED(hr)) - PanicAlert("Failed to create shader resource view at %s %d\n", __FILE__, __LINE__); - SAFE_RELEASE(buftex); - - SelectObject(hDC, hOldbmBitmap); - DeleteObject(hbmBitmap); - - SelectObject(hDC, hOldFont); - DeleteObject(hFont); - - // setup device objects for drawing - m_pshader = D3D::CompileAndCreatePixelShader(fontpixshader); - if (m_pshader == nullptr) - PanicAlert("Failed to create pixel shader, %s %d\n", __FILE__, __LINE__); - D3D::SetDebugObjectName(m_pshader, "pixel shader of a CD3DFont object"); - - D3DBlob* vsbytecode; - D3D::CompileVertexShader(fontvertshader, &vsbytecode); - if (vsbytecode == nullptr) - PanicAlert("Failed to compile vertex shader, %s %d\n", __FILE__, __LINE__); - m_vshader = D3D::CreateVertexShaderFromByteCode(vsbytecode); - if (m_vshader == nullptr) - PanicAlert("Failed to create vertex shader, %s %d\n", __FILE__, __LINE__); - D3D::SetDebugObjectName(m_vshader, "vertex shader of a CD3DFont object"); - - const D3D11_INPUT_ELEMENT_DESC desc[] = { - {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, - {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, - {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0}, - }; - hr = D3D::device->CreateInputLayout(desc, 3, vsbytecode->Data(), vsbytecode->Size(), - &m_InputLayout); - if (FAILED(hr)) - PanicAlert("Failed to create input layout, %s %d\n", __FILE__, __LINE__); - SAFE_RELEASE(vsbytecode); - - D3D11_BLEND_DESC blenddesc; - blenddesc.AlphaToCoverageEnable = FALSE; - blenddesc.IndependentBlendEnable = FALSE; - blenddesc.RenderTarget[0].BlendEnable = TRUE; - blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; - blenddesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; - blenddesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; - blenddesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; - blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA; - blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; - blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; - hr = D3D::device->CreateBlendState(&blenddesc, &m_blendstate); - CHECK(hr == S_OK, "Create font blend state"); - D3D::SetDebugObjectName(m_blendstate, "blend state of a CD3DFont object"); - - D3D11_RASTERIZER_DESC rastdesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false, - 0, 0.f, 0.f, false, false, false, false); - hr = D3D::device->CreateRasterizerState(&rastdesc, &m_raststate); - CHECK(hr == S_OK, "Create font rasterizer state"); - D3D::SetDebugObjectName(m_raststate, "rasterizer state of a CD3DFont object"); - - D3D11_BUFFER_DESC vbdesc = - CD3D11_BUFFER_DESC(MAX_NUM_VERTICES * sizeof(FONT2DVERTEX), D3D11_BIND_VERTEX_BUFFER, - D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); - if (FAILED(hr = device->CreateBuffer(&vbdesc, nullptr, &m_pVB))) - { - PanicAlert("Failed to create font vertex buffer at %s, line %d\n", __FILE__, __LINE__); - return hr; - } - D3D::SetDebugObjectName(m_pVB, "vertex buffer of a CD3DFont object"); - return S_OK; -} - -int CD3DFont::Shutdown() -{ - SAFE_RELEASE(m_pVB); - SAFE_RELEASE(m_pTexture); - SAFE_RELEASE(m_InputLayout); - SAFE_RELEASE(m_pshader); - SAFE_RELEASE(m_vshader); - - SAFE_RELEASE(m_blendstate); - SAFE_RELEASE(m_raststate); - - return S_OK; -} - -int CD3DFont::DrawTextScaled(float x, float y, float size, float spacing, u32 dwColor, - const std::string& text) -{ - if (!m_pVB) - return 0; - - UINT stride = sizeof(FONT2DVERTEX); - UINT bufoffset = 0; - - float scalex = 1.0f / g_renderer->GetBackbufferWidth() * 2.f; - float scaley = 1.0f / g_renderer->GetBackbufferHeight() * 2.f; - float sizeratio = size / m_LineHeight; - - // translate starting positions - float sx = x * scalex - 1.f; - float sy = 1.f - y * scaley; - - // Fill vertex buffer - FONT2DVERTEX* pVertices; - int dwNumTriangles = 0L; - - D3D11_MAPPED_SUBRESOURCE vbmap; - HRESULT hr = context->Map(m_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vbmap); - if (FAILED(hr)) - PanicAlert("Mapping vertex buffer failed, %s %d\n", __FILE__, __LINE__); - pVertices = (D3D::FONT2DVERTEX*)vbmap.pData; - - // set general pipeline state - D3D::stateman->SetBlendState(m_blendstate); - D3D::stateman->SetRasterizerState(m_raststate); - - D3D::stateman->SetPixelShader(m_pshader); - D3D::stateman->SetVertexShader(m_vshader); - D3D::stateman->SetGeometryShader(nullptr); - - D3D::stateman->SetInputLayout(m_InputLayout); - D3D::stateman->SetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - D3D::stateman->SetTexture(0, m_pTexture); - - float fStartX = sx; - for (char c : text) - { - if (c == '\n') - { - sx = fStartX; - sy -= scaley * size; - } - if (!std::isprint(c)) - continue; - - c -= 32; - float tx1 = m_fTexCoords[c][0]; - float ty1 = m_fTexCoords[c][1]; - float tx2 = m_fTexCoords[c][2]; - float ty2 = m_fTexCoords[c][3]; - - float w = (float)(tx2 - tx1) * m_dwTexWidth * scalex * sizeratio; - float h = (float)(ty1 - ty2) * m_dwTexHeight * scaley * sizeratio; - - FONT2DVERTEX v[6]; - v[0] = InitFont2DVertex(sx, sy + h, dwColor, tx1, ty2); - v[1] = InitFont2DVertex(sx, sy, dwColor, tx1, ty1); - v[2] = InitFont2DVertex(sx + w, sy + h, dwColor, tx2, ty2); - v[3] = InitFont2DVertex(sx + w, sy, dwColor, tx2, ty1); - v[4] = v[2]; - v[5] = v[1]; - - memcpy(pVertices, v, 6 * sizeof(FONT2DVERTEX)); - - pVertices += 6; - dwNumTriangles += 2; - - if (dwNumTriangles * 3 > (MAX_NUM_VERTICES - 6)) - { - context->Unmap(m_pVB, 0); - - D3D::stateman->SetVertexBuffer(m_pVB, stride, bufoffset); - - D3D::stateman->Apply(); - D3D::context->Draw(3 * dwNumTriangles, 0); - - dwNumTriangles = 0; - D3D11_MAPPED_SUBRESOURCE _vbmap; - hr = context->Map(m_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &_vbmap); - if (FAILED(hr)) - PanicAlert("Mapping vertex buffer failed, %s %d\n", __FILE__, __LINE__); - pVertices = (D3D::FONT2DVERTEX*)_vbmap.pData; - } - sx += w + spacing * scalex * size; - } - - // Unlock and render the vertex buffer - context->Unmap(m_pVB, 0); - if (dwNumTriangles > 0) - { - D3D::stateman->SetVertexBuffer(m_pVB, stride, bufoffset); - - D3D::stateman->Apply(); - D3D::context->Draw(3 * dwNumTriangles, 0); - } - return S_OK; -} - static ID3D11SamplerState* linear_copy_sampler = nullptr; static ID3D11SamplerState* point_copy_sampler = nullptr; @@ -560,13 +177,10 @@ void InitUtils() util_vbuf->AddWrapObserver(&stq_observer); util_vbuf->AddWrapObserver(&cq_observer); util_vbuf->AddWrapObserver(&clearq_observer); - - font.Init(); } void ShutdownUtils() { - font.Shutdown(); SAFE_RELEASE(point_copy_sampler); SAFE_RELEASE(linear_copy_sampler); SAFE_DELETE(util_vbuf); @@ -788,10 +402,6 @@ void DrawEFBPokeQuads(EFBAccessType type, const EfbPokeData* points, size_t num_ stateman->SetGeometryShader(GeometryShaderCache::GetClearGeometryShader()); } -void DrawTextScaled(float x, float y, float size, float spacing, u32 color, const std::string& text) -{ - font.DrawTextScaled(x, y, size, spacing, color, text); -} } // namespace D3D } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/D3DUtil.h b/Source/Core/VideoBackends/D3D/D3DUtil.h index ade53946b1..cfbe2e4889 100644 --- a/Source/Core/VideoBackends/D3D/D3DUtil.h +++ b/Source/Core/VideoBackends/D3D/D3DUtil.h @@ -28,7 +28,5 @@ void drawClearQuad(u32 Color, float z); void drawColorQuad(u32 Color, float z, float x1, float y1, float x2, float y2); void DrawEFBPokeQuads(EFBAccessType type, const EfbPokeData* points, size_t num_points); -void DrawTextScaled(float x, float y, float size, float spacing, u32 color, - const std::string& text); } } diff --git a/Source/Core/VideoBackends/D3D/FramebufferManager.cpp b/Source/Core/VideoBackends/D3D/FramebufferManager.cpp index e7e8fa9ddc..5a2088f40d 100644 --- a/Source/Core/VideoBackends/D3D/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/D3D/FramebufferManager.cpp @@ -125,6 +125,7 @@ void FramebufferManager::BindEFBRenderTarget(bool bind_depth) FramebufferManager::FramebufferManager(int target_width, int target_height) { + static constexpr std::array clear_color = {0.0f, 0.0f, 0.0f, 1.0f}; m_target_width = static_cast(std::max(target_width, 1)); m_target_height = static_cast(std::max(target_height, 1)); DXGI_SAMPLE_DESC sample_desc; @@ -154,6 +155,7 @@ FramebufferManager::FramebufferManager(int target_width, int target_height) D3D::SetDebugObjectName(m_efb.color_tex->GetTex(), "EFB color texture"); D3D::SetDebugObjectName(m_efb.color_tex->GetSRV(), "EFB color texture shader resource view"); D3D::SetDebugObjectName(m_efb.color_tex->GetRTV(), "EFB color texture render target view"); + D3D::context->ClearRenderTargetView(m_efb.color_tex->GetRTV(), clear_color.data()); // Temporary EFB color texture - used in ReinterpretPixelData texdesc = @@ -173,6 +175,7 @@ FramebufferManager::FramebufferManager(int target_width, int target_height) "EFB color temp texture shader resource view"); D3D::SetDebugObjectName(m_efb.color_temp_tex->GetRTV(), "EFB color temp texture render target view"); + D3D::context->ClearRenderTargetView(m_efb.color_temp_tex->GetRTV(), clear_color.data()); // Integer render targets for EFB, used for logic op CD3D11_RENDER_TARGET_VIEW_DESC int_rtv_desc(m_efb.color_tex->GetTex(), @@ -222,6 +225,7 @@ FramebufferManager::FramebufferManager(int target_width, int target_height) D3D::SetDebugObjectName(m_efb.depth_tex->GetTex(), "EFB depth texture"); D3D::SetDebugObjectName(m_efb.depth_tex->GetDSV(), "EFB depth texture depth stencil view"); D3D::SetDebugObjectName(m_efb.depth_tex->GetSRV(), "EFB depth texture shader resource view"); + D3D::context->ClearDepthStencilView(m_efb.depth_tex->GetDSV(), D3D11_CLEAR_DEPTH, 0.0f, 0); // Render buffer for AccessEFB (depth data) texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R32_FLOAT, 1, 1, 1, 1, D3D11_BIND_RENDER_TARGET); diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index 93098f53fe..2a82924708 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -62,22 +62,14 @@ typedef struct _Nv_Stereo_Image_Header #define NVSTEREO_IMAGE_SIGNATURE 0x4433564e -Renderer::Renderer(int backbuffer_width, int backbuffer_height) - : ::Renderer(backbuffer_width, backbuffer_height, AbstractTextureFormat::RGBA8) +Renderer::Renderer(int backbuffer_width, int backbuffer_height, float backbuffer_scale) + : ::Renderer(backbuffer_width, backbuffer_height, backbuffer_scale, + AbstractTextureFormat::RGBA8) { - m_last_multisamples = g_ActiveConfig.iMultisamples; - m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; m_last_fullscreen_state = D3D::GetFullscreenState(); g_framebuffer_manager = std::make_unique(m_target_width, m_target_height); SetupDeviceObjects(); - // Clear EFB textures - constexpr std::array clear_color{{0.f, 0.f, 0.f, 1.f}}; - D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(), - clear_color.data()); - D3D::context->ClearDepthStencilView(FramebufferManager::GetEFBDepthTexture()->GetDSV(), - D3D11_CLEAR_DEPTH, 0.f, 0); - D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)m_target_width, (float)m_target_height); D3D::context->RSSetViewports(1, &vp); FramebufferManager::BindEFBRenderTarget(); @@ -237,13 +229,6 @@ Renderer::CreateFramebuffer(const AbstractTexture* color_attachment, static_cast(depth_attachment)); } -void Renderer::RenderText(const std::string& text, int left, int top, u32 color) -{ - D3D::DrawTextScaled(static_cast(left + 1), static_cast(top + 1), 20.f, 0.0f, - color & 0xFF000000, text); - D3D::DrawTextScaled(static_cast(left), static_cast(top), 20.f, 0.0f, color, text); -} - std::unique_ptr Renderer::CreateShaderFromSource(ShaderStage stage, const char* source, size_t length) { @@ -566,71 +551,33 @@ void Renderer::ReinterpretPixelData(unsigned int convtype) RestoreAPIState(); } -// This function has the final picture. We adjust the aspect ratio here. -void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks) +void Renderer::BindBackbuffer(const ClearColor& clear_color) { - ResetAPIState(); - - // Prepare to copy the XFBs to our backbuffer CheckForSurfaceChange(); CheckForSurfaceResize(); - UpdateDrawRectangle(); - TargetRectangle targetRc = GetTargetRectangle(); - static constexpr std::array clear_color{{0.f, 0.f, 0.f, 1.f}}; D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr); D3D::context->ClearRenderTargetView(D3D::GetBackBuffer()->GetRTV(), clear_color.data()); m_current_framebuffer = nullptr; m_current_framebuffer_width = m_backbuffer_width; m_current_framebuffer_height = m_backbuffer_height; +} - // activate linear filtering for the buffer copies - D3D::SetLinearCopySampler(); - auto* xfb_texture = static_cast(texture); - - BlitScreen(xfb_region, targetRc, xfb_texture->GetRawTexIdentifier(), - xfb_texture->GetConfig().width, xfb_texture->GetConfig().height); - - // Reset viewport for drawing text - D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.0f, 0.0f, static_cast(m_backbuffer_width), - static_cast(m_backbuffer_height)); - D3D::context->RSSetViewports(1, &vp); - - Renderer::DrawDebugText(); - - OSD::DrawMessages(); - - g_texture_cache->Cleanup(frameCount); - - // Enable configuration changes - UpdateActiveConfig(); - g_texture_cache->OnConfigChanged(g_ActiveConfig); - - // Flip/present backbuffer to frontbuffer here - if (D3D::swapchain) - D3D::Present(); +void Renderer::PresentBackbuffer() +{ + D3D::Present(); +} +void Renderer::OnConfigChanged(u32 bits) +{ // Resize the back buffers NOW to avoid flickering - if (CalculateTargetSize() || m_last_multisamples != g_ActiveConfig.iMultisamples || - m_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off)) + if (bits & (CONFIG_CHANGE_BIT_TARGET_SIZE | CONFIG_CHANGE_BIT_MULTISAMPLES | + CONFIG_CHANGE_BIT_STEREO_MODE)) { - m_last_multisamples = g_ActiveConfig.iMultisamples; - m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; PixelShaderCache::InvalidateMSAAShaders(); - UpdateDrawRectangle(); - g_framebuffer_manager.reset(); g_framebuffer_manager = std::make_unique(m_target_width, m_target_height); - D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(), - clear_color.data()); - D3D::context->ClearDepthStencilView(FramebufferManager::GetEFBDepthTexture()->GetDSV(), - D3D11_CLEAR_DEPTH, 0.f, 0); } - - CheckForHostConfigChanges(); - - // begin next frame - RestoreAPIState(); } void Renderer::CheckForSurfaceChange() @@ -789,28 +736,34 @@ void Renderer::BBoxWrite(int index, u16 _value) BBox::Set(index, value); } -void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture, - u32 src_width, u32 src_height) +void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) { + const CD3D11_RECT source_rc(rc.left, rc.top, rc.right, rc.bottom); + const TargetRectangle target_rc = GetTargetRectangle(); + + // activate linear filtering for the buffer copies + D3D::SetLinearCopySampler(); + if (g_ActiveConfig.stereo_mode == StereoMode::SBS || g_ActiveConfig.stereo_mode == StereoMode::TAB) { - TargetRectangle leftRc, rightRc; - std::tie(leftRc, rightRc) = ConvertStereoRectangle(dst); + TargetRectangle left_rc, right_rc; + std::tie(left_rc, right_rc) = ConvertStereoRectangle(target_rc); - D3D11_VIEWPORT leftVp = CD3D11_VIEWPORT((float)leftRc.left, (float)leftRc.top, - (float)leftRc.GetWidth(), (float)leftRc.GetHeight()); - D3D11_VIEWPORT rightVp = CD3D11_VIEWPORT((float)rightRc.left, (float)rightRc.top, - (float)rightRc.GetWidth(), (float)rightRc.GetHeight()); - - D3D::context->RSSetViewports(1, &leftVp); - D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height, + SetViewport(static_cast(left_rc.left), static_cast(left_rc.top), + static_cast(left_rc.GetWidth()), static_cast(right_rc.GetHeight()), + 0.0f, 1.0f); + D3D::drawShadedTexQuad(static_cast(texture)->GetRawTexIdentifier()->GetSRV(), + &source_rc, texture->GetWidth(), texture->GetHeight(), PixelShaderCache::GetColorCopyProgram(false), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), nullptr, 0); - D3D::context->RSSetViewports(1, &rightVp); - D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height, + SetViewport(static_cast(right_rc.left), static_cast(right_rc.top), + static_cast(right_rc.GetWidth()), static_cast(right_rc.GetHeight()), + 0.0f, 1.0f); + D3D::drawShadedTexQuad(static_cast(texture)->GetRawTexIdentifier()->GetSRV(), + &source_rc, texture->GetWidth(), texture->GetHeight(), PixelShaderCache::GetColorCopyProgram(false), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), nullptr, 1); @@ -820,29 +773,33 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D if (!m_3d_vision_texture) Create3DVisionTexture(m_backbuffer_width, m_backbuffer_height); - D3D11_VIEWPORT leftVp = CD3D11_VIEWPORT((float)dst.left, (float)dst.top, (float)dst.GetWidth(), - (float)dst.GetHeight()); - D3D11_VIEWPORT rightVp = CD3D11_VIEWPORT((float)(dst.left + m_backbuffer_width), (float)dst.top, - (float)dst.GetWidth(), (float)dst.GetHeight()); + const CD3D11_VIEWPORT left_vp( + static_cast(target_rc.left), static_cast(target_rc.top), + static_cast(target_rc.GetWidth()), static_cast(target_rc.GetHeight())); + const CD3D11_VIEWPORT right_vp( + static_cast(target_rc.left + m_backbuffer_width), static_cast(target_rc.top), + static_cast(target_rc.GetWidth()), static_cast(target_rc.GetHeight())); // Render to staging texture which is double the width of the backbuffer D3D::context->OMSetRenderTargets(1, &m_3d_vision_texture->GetRTV(), nullptr); - D3D::context->RSSetViewports(1, &leftVp); - D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height, + D3D::context->RSSetViewports(1, &left_vp); + D3D::drawShadedTexQuad(static_cast(texture)->GetRawTexIdentifier()->GetSRV(), + &source_rc, texture->GetWidth(), texture->GetHeight(), PixelShaderCache::GetColorCopyProgram(false), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), nullptr, 0); - D3D::context->RSSetViewports(1, &rightVp); - D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height, + D3D::context->RSSetViewports(1, &right_vp); + D3D::drawShadedTexQuad(static_cast(texture)->GetRawTexIdentifier()->GetSRV(), + &source_rc, texture->GetWidth(), texture->GetHeight(), PixelShaderCache::GetColorCopyProgram(false), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), nullptr, 1); // Copy the left eye to the backbuffer, if Nvidia 3D Vision is enabled it should // recognize the signature and automatically include the right eye frame. - D3D11_BOX box = CD3D11_BOX(0, 0, 0, m_backbuffer_width, m_backbuffer_height, 1); + const CD3D11_BOX box(0, 0, 0, m_backbuffer_width, m_backbuffer_height, 1); D3D::context->CopySubresourceRegion(D3D::GetBackBuffer()->GetTex(), 0, 0, 0, 0, m_3d_vision_texture->GetTex(), 0, &box); @@ -851,9 +808,9 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D } else { - D3D11_VIEWPORT vp = CD3D11_VIEWPORT((float)dst.left, (float)dst.top, (float)dst.GetWidth(), - (float)dst.GetHeight()); - D3D::context->RSSetViewports(1, &vp); + SetViewport(static_cast(target_rc.left), static_cast(target_rc.top), + static_cast(target_rc.GetWidth()), static_cast(target_rc.GetHeight()), + 0.0f, 1.0f); ID3D11PixelShader* pixelShader = (g_Config.stereo_mode == StereoMode::Anaglyph) ? PixelShaderCache::GetAnaglyphProgram() : @@ -861,7 +818,8 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D ID3D11GeometryShader* geomShader = (g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer) ? GeometryShaderCache::GetCopyGeometryShader() : nullptr; - D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height, pixelShader, + D3D::drawShadedTexQuad(static_cast(texture)->GetRawTexIdentifier()->GetSRV(), + &source_rc, texture->GetWidth(), texture->GetHeight(), pixelShader, VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), geomShader); } diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h index 4750a0e69a..399babffad 100644 --- a/Source/Core/VideoBackends/D3D/Render.h +++ b/Source/Core/VideoBackends/D3D/Render.h @@ -18,7 +18,7 @@ class D3DTexture2D; class Renderer : public ::Renderer { public: - Renderer(int backbuffer_width, int backbuffer_height); + Renderer(int backbuffer_width, int backbuffer_height, float backbuffer_scale); ~Renderer() override; StateCache& GetStateCache() { return m_state_cache; } @@ -52,11 +52,11 @@ public: float far_depth) override; void Draw(u32 base_vertex, u32 num_vertices) override; void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override; + void BindBackbuffer(const ClearColor& clear_color = {}) override; + void PresentBackbuffer() override; void SetFullscreen(bool enable_fullscreen) override; bool IsFullscreen() const override; - void RenderText(const std::string& text, int left, int top, u32 color) override; - u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override; @@ -68,7 +68,8 @@ public: TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; - void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override; + void RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) override; + void OnConfigChanged(u32 bits) override; void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override; @@ -83,9 +84,6 @@ private: void CheckForSurfaceResize(); void UpdateBackbufferSize(); - void BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture, - u32 src_width, u32 src_height); - StateCache m_state_cache; std::array m_clear_blend_states{}; @@ -97,8 +95,6 @@ private: ID3D11Texture2D* m_screenshot_texture = nullptr; D3DTexture2D* m_3d_vision_texture = nullptr; - u32 m_last_multisamples = 1; - bool m_last_stereo_mode = false; bool m_last_fullscreen_state = false; }; } diff --git a/Source/Core/VideoBackends/D3D/main.cpp b/Source/Core/VideoBackends/D3D/main.cpp index 684bc1350b..5795fce3a2 100644 --- a/Source/Core/VideoBackends/D3D/main.cpp +++ b/Source/Core/VideoBackends/D3D/main.cpp @@ -147,7 +147,8 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) } // internal interfaces - g_renderer = std::make_unique(backbuffer_width, backbuffer_height); + g_renderer = + std::make_unique(backbuffer_width, backbuffer_height, wsi.render_surface_scale); g_shader_cache = std::make_unique(); g_texture_cache = std::make_unique(); g_vertex_manager = std::make_unique(); @@ -156,13 +157,14 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) VertexShaderCache::Init(); PixelShaderCache::Init(); GeometryShaderCache::Init(); - if (!g_shader_cache->Initialize()) + + if (!g_renderer->Initialize() || !g_shader_cache->Initialize()) return false; D3D::InitUtils(); BBox::Init(); - return g_renderer->Initialize(); + return true; } void VideoBackend::Shutdown() diff --git a/Source/Core/VideoBackends/Null/Render.cpp b/Source/Core/VideoBackends/Null/Render.cpp index 0fe6f1186e..d076e0f091 100644 --- a/Source/Core/VideoBackends/Null/Render.cpp +++ b/Source/Core/VideoBackends/Null/Render.cpp @@ -14,7 +14,7 @@ namespace Null { // Init functions -Renderer::Renderer() : ::Renderer(1, 1, AbstractTextureFormat::RGBA8) +Renderer::Renderer() : ::Renderer(1, 1, 1.0f, AbstractTextureFormat::RGBA8) { UpdateActiveConfig(); } @@ -82,11 +82,6 @@ Renderer::CreateFramebuffer(const AbstractTexture* color_attachment, static_cast(depth_attachment)); } -void Renderer::RenderText(const std::string& text, int left, int top, u32 color) -{ - NOTICE_LOG(VIDEO, "RenderText: %s", text.c_str()); -} - TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc) { TargetRectangle result; @@ -97,9 +92,4 @@ TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc) return result; } -void Renderer::SwapImpl(AbstractTexture*, const EFBRectangle&, u64) -{ - UpdateActiveConfig(); -} - } // namespace Null diff --git a/Source/Core/VideoBackends/Null/Render.h b/Source/Core/VideoBackends/Null/Render.h index 10f15f0304..75c4adfffc 100644 --- a/Source/Core/VideoBackends/Null/Render.h +++ b/Source/Core/VideoBackends/Null/Render.h @@ -29,15 +29,12 @@ public: size_t length) override; std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config) override; - void RenderText(const std::string& pstr, int left, int top, u32 color) override; u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override { return 0; } void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {} u16 BBoxRead(int index) override { return 0; } void BBoxWrite(int index, u16 value) override {} TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; - void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override; - void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override { diff --git a/Source/Core/VideoBackends/OGL/CMakeLists.txt b/Source/Core/VideoBackends/OGL/CMakeLists.txt index 0fc4f1b4ed..c786ba671f 100644 --- a/Source/Core/VideoBackends/OGL/CMakeLists.txt +++ b/Source/Core/VideoBackends/OGL/CMakeLists.txt @@ -9,7 +9,6 @@ add_library(videoogl PerfQuery.cpp PostProcessing.cpp ProgramShaderCache.cpp - RasterFont.cpp Render.cpp SamplerCache.cpp StreamBuffer.cpp diff --git a/Source/Core/VideoBackends/OGL/OGL.vcxproj b/Source/Core/VideoBackends/OGL/OGL.vcxproj index a947e4454b..38ea16b42e 100644 --- a/Source/Core/VideoBackends/OGL/OGL.vcxproj +++ b/Source/Core/VideoBackends/OGL/OGL.vcxproj @@ -46,7 +46,6 @@ - @@ -64,7 +63,6 @@ - diff --git a/Source/Core/VideoBackends/OGL/OGL.vcxproj.filters b/Source/Core/VideoBackends/OGL/OGL.vcxproj.filters index ea5967fb1e..fbede5caf6 100644 --- a/Source/Core/VideoBackends/OGL/OGL.vcxproj.filters +++ b/Source/Core/VideoBackends/OGL/OGL.vcxproj.filters @@ -7,9 +7,6 @@ {5bfec41c-1031-4925-8f98-38c7b49b1924} - - {00dadfd8-a906-4b0c-b415-d42a69cf3ca7} - {696df73b-378e-4399-8f21-999b65d78dcd} @@ -24,9 +21,6 @@ GLUtil - - Logging - Render @@ -70,9 +64,6 @@ GLUtil - - Logging - Render diff --git a/Source/Core/VideoBackends/OGL/PostProcessing.cpp b/Source/Core/VideoBackends/OGL/PostProcessing.cpp index 9de1a85326..57d8f7c8f8 100644 --- a/Source/Core/VideoBackends/OGL/PostProcessing.cpp +++ b/Source/Core/VideoBackends/OGL/PostProcessing.cpp @@ -198,11 +198,6 @@ void OpenGLPostProcessing::CreateHeader() "#define SampleOffset(offset) textureOffset(samp9, float3(uv0, layer), offset)\n" - "float4 SampleFontLocation(float2 location)\n" - "{\n" - "\treturn texture(samp8, location);\n" - "}\n" - "float2 GetResolution()\n" "{\n" "\treturn resolution.xy;\n" diff --git a/Source/Core/VideoBackends/OGL/RasterFont.cpp b/Source/Core/VideoBackends/OGL/RasterFont.cpp deleted file mode 100644 index f21ac22900..0000000000 --- a/Source/Core/VideoBackends/OGL/RasterFont.cpp +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include - -#include "Common/Common.h" -#include "Common/GL/GLUtil.h" - -#include "VideoBackends/OGL/ProgramShaderCache.h" -#include "VideoBackends/OGL/RasterFont.h" -#include "VideoBackends/OGL/VertexManager.h" - -// globals - -namespace OGL -{ -static const int CHARACTER_WIDTH = 8; -static const int CHARACTER_HEIGHT = 13; -static const int CHARACTER_OFFSET = 32; -static const int CHARACTER_COUNT = 95; - -static const u8 rasters[CHARACTER_COUNT][CHARACTER_HEIGHT] = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36}, - {0x00, 0x00, 0x00, 0x66, 0x66, 0xff, 0x66, 0x66, 0xff, 0x66, 0x66, 0x00, 0x00}, - {0x00, 0x00, 0x18, 0x7e, 0xff, 0x1b, 0x1f, 0x7e, 0xf8, 0xd8, 0xff, 0x7e, 0x18}, - {0x00, 0x00, 0x0e, 0x1b, 0xdb, 0x6e, 0x30, 0x18, 0x0c, 0x76, 0xdb, 0xd8, 0x70}, - {0x00, 0x00, 0x7f, 0xc6, 0xcf, 0xd8, 0x70, 0x70, 0xd8, 0xcc, 0xcc, 0x6c, 0x38}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1c, 0x0c, 0x0e}, - {0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c}, - {0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30}, - {0x00, 0x00, 0x00, 0x00, 0x99, 0x5a, 0x3c, 0xff, 0x3c, 0x5a, 0x99, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00}, - {0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03}, - {0x00, 0x00, 0x3c, 0x66, 0xc3, 0xe3, 0xf3, 0xdb, 0xcf, 0xc7, 0xc3, 0x66, 0x3c}, - {0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x38, 0x18}, - {0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0xe7, 0x7e}, - {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0x07, 0x03, 0x03, 0xe7, 0x7e}, - {0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c}, - {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xff}, - {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e}, - {0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x03, 0x03, 0xff}, - {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e}, - {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x03, 0x7f, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e}, - {0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06}, - {0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60}, - {0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x0c, 0x06, 0x03, 0xc3, 0xc3, 0x7e}, - {0x00, 0x00, 0x3f, 0x60, 0xcf, 0xdb, 0xd3, 0xdd, 0xc3, 0x7e, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18}, - {0x00, 0x00, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe}, - {0x00, 0x00, 0x7e, 0xe7, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e}, - {0x00, 0x00, 0xfc, 0xce, 0xc7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc7, 0xce, 0xfc}, - {0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xff}, - {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xff}, - {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xcf, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e}, - {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, - {0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e}, - {0x00, 0x00, 0x7c, 0xee, 0xc6, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06}, - {0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6, 0xc3}, - {0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0}, - {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xff, 0xff, 0xe7, 0xc3}, - {0x00, 0x00, 0xc7, 0xc7, 0xcf, 0xcf, 0xdf, 0xdb, 0xfb, 0xf3, 0xf3, 0xe3, 0xe3}, - {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe7, 0x7e}, - {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe}, - {0x00, 0x00, 0x3f, 0x6e, 0xdf, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c}, - {0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe}, - {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0xe0, 0xc0, 0xc0, 0xe7, 0x7e}, - {0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff}, - {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, - {0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, - {0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, - {0x00, 0x00, 0xc3, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3}, - {0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3}, - {0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x7e, 0x0c, 0x06, 0x03, 0x03, 0xff}, - {0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c}, - {0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60}, - {0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18}, - {0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x30, 0x70}, - {0x00, 0x00, 0x7f, 0xc3, 0xc3, 0x7f, 0x03, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0}, - {0x00, 0x00, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x03, 0x03, 0x03, 0x03, 0x03}, - {0x00, 0x00, 0x7f, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x33, 0x1e}, - {0x7e, 0xc3, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0}, - {0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00}, - {0x38, 0x6c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x00}, - {0x00, 0x00, 0xc6, 0xcc, 0xf8, 0xf0, 0xd8, 0xcc, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0}, - {0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78}, - {0x00, 0x00, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xfe, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00}, - {0xc0, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0x00, 0x00, 0x00, 0x00}, - {0x03, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, 0xfe, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xfe, 0x03, 0x03, 0x7e, 0xc0, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00}, - {0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xc3, 0xe7, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00}, - {0xc0, 0x60, 0x60, 0x30, 0x18, 0x3c, 0x66, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xff, 0x60, 0x30, 0x18, 0x0c, 0x06, 0xff, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x0f, 0x18, 0x18, 0x18, 0x38, 0xf0, 0x38, 0x18, 0x18, 0x18, 0x0f}, - {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}, - {0x00, 0x00, 0xf0, 0x18, 0x18, 0x18, 0x1c, 0x0f, 0x1c, 0x18, 0x18, 0x18, 0xf0}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x8f, 0xf1, 0x60, 0x00, 0x00, 0x00}}; - -static const char* s_vertexShaderSrc = "uniform vec2 charSize;\n" - "uniform vec2 offset;" - "in vec2 rawpos;\n" - "in vec2 rawtex0;\n" - "out vec2 uv0;\n" - "void main(void) {\n" - " gl_Position = vec4(rawpos + offset,0,1);\n" - " uv0 = rawtex0 * charSize;\n" - "}\n"; - -static const char* s_fragmentShaderSrc = "SAMPLER_BINDING(8) uniform sampler2D samp8;\n" - "uniform vec4 color;\n" - "in vec2 uv0;\n" - "out vec4 ocol0;\n" - "void main(void) {\n" - " ocol0 = texture(samp8,uv0) * color;\n" - "}\n"; - -static SHADER s_shader; - -RasterFont::RasterFont() -{ - // generate the texture - glGenTextures(1, &texture); - glActiveTexture(GL_TEXTURE8); - glBindTexture(GL_TEXTURE_2D, texture); - std::vector texture_data(CHARACTER_WIDTH * CHARACTER_COUNT * CHARACTER_HEIGHT); - for (int y = 0; y < CHARACTER_HEIGHT; y++) - { - for (int c = 0; c < CHARACTER_COUNT; c++) - { - for (int x = 0; x < CHARACTER_WIDTH; x++) - { - bool pixel = (0 != (rasters[c][y] & (1 << (CHARACTER_WIDTH - x - 1)))); - texture_data[CHARACTER_WIDTH * CHARACTER_COUNT * y + CHARACTER_WIDTH * c + x] = - pixel ? -1 : 0; - } - } - } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, CHARACTER_WIDTH * CHARACTER_COUNT, CHARACTER_HEIGHT, 0, - GL_RGBA, GL_UNSIGNED_BYTE, texture_data.data()); - - // generate shader - ProgramShaderCache::CompileShader(s_shader, s_vertexShaderSrc, s_fragmentShaderSrc); - s_shader.Bind(); - - // bound uniforms - glUniform2f(glGetUniformLocation(s_shader.glprogid, "charSize"), 1.0f / GLfloat(CHARACTER_COUNT), - 1.0f); - uniform_color_id = glGetUniformLocation(s_shader.glprogid, "color"); - glUniform4f(uniform_color_id, 1.0f, 1.0f, 1.0f, 1.0f); - uniform_offset_id = glGetUniformLocation(s_shader.glprogid, "offset"); - glUniform2f(uniform_offset_id, 0.0f, 0.0f); - - // generate VBO & VAO - glGenBuffers(1, &VBO); - glGenVertexArrays(1, &VAO); - glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBindVertexArray(VAO); - glEnableVertexAttribArray(SHADER_POSITION_ATTRIB); - glVertexAttribPointer(SHADER_POSITION_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat) * 4, nullptr); - glEnableVertexAttribArray(SHADER_TEXTURE0_ATTRIB); - glVertexAttribPointer(SHADER_TEXTURE0_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat) * 4, - (GLfloat*)nullptr + 2); - glBindBuffer(GL_ARRAY_BUFFER, - static_cast(g_vertex_manager.get())->GetVertexBufferHandle()); - ProgramShaderCache::InvalidateVertexFormat(); -} - -RasterFont::~RasterFont() -{ - glDeleteTextures(1, &texture); - glDeleteBuffers(1, &VBO); - glDeleteVertexArrays(1, &VAO); - s_shader.Destroy(); -} - -void RasterFont::printMultilineText(const std::string& text, double start_x, double start_y, - double z, int bbWidth, int bbHeight, u32 color) -{ - std::vector vertices(text.length() * 6 * 4); - - int usage = 0; - GLfloat delta_x = GLfloat(2 * CHARACTER_WIDTH) / GLfloat(bbWidth); - GLfloat delta_y = GLfloat(2 * CHARACTER_HEIGHT) / GLfloat(bbHeight); - GLfloat border_x = 2.0f / GLfloat(bbWidth); - GLfloat border_y = 4.0f / GLfloat(bbHeight); - - GLfloat x = GLfloat(start_x); - GLfloat y = GLfloat(start_y); - - for (const char& c : text) - { - if (c == '\n') - { - x = GLfloat(start_x); - y -= delta_y + border_y; - continue; - } - - // do not print spaces, they can be skipped easily - if (c == ' ') - { - x += delta_x + border_x; - continue; - } - - if (c < CHARACTER_OFFSET || c >= CHARACTER_COUNT + CHARACTER_OFFSET) - continue; - - vertices[usage++] = x; - vertices[usage++] = y; - vertices[usage++] = GLfloat(c - CHARACTER_OFFSET); - vertices[usage++] = 0.0f; - - vertices[usage++] = x + delta_x; - vertices[usage++] = y; - vertices[usage++] = GLfloat(c - CHARACTER_OFFSET + 1); - vertices[usage++] = 0.0f; - - vertices[usage++] = x + delta_x; - vertices[usage++] = y + delta_y; - vertices[usage++] = GLfloat(c - CHARACTER_OFFSET + 1); - vertices[usage++] = 1.0f; - - vertices[usage++] = x; - vertices[usage++] = y; - vertices[usage++] = GLfloat(c - CHARACTER_OFFSET); - vertices[usage++] = 0.0f; - - vertices[usage++] = x + delta_x; - vertices[usage++] = y + delta_y; - vertices[usage++] = GLfloat(c - CHARACTER_OFFSET + 1); - vertices[usage++] = 1.0f; - - vertices[usage++] = x; - vertices[usage++] = y + delta_y; - vertices[usage++] = GLfloat(c - CHARACTER_OFFSET); - vertices[usage++] = 1.0f; - - x += delta_x + border_x; - } - - if (!usage) - { - return; - } - - glBindVertexArray(VAO); - glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBufferData(GL_ARRAY_BUFFER, usage * sizeof(GLfloat), vertices.data(), GL_STREAM_DRAW); - - s_shader.Bind(); - - // shadows - glUniform2f(uniform_offset_id, 2.0f / GLfloat(bbWidth), -2.0f / GLfloat(bbHeight)); - glUniform4f(uniform_color_id, 0.0f, 0.0f, 0.0f, GLfloat((color >> 24) & 0xff) / 255.f); - glDrawArrays(GL_TRIANGLES, 0, usage / 4); - - glUniform2f(uniform_offset_id, 0.0f, 0.0f); - glUniform4f(uniform_color_id, GLfloat((color >> 16) & 0xff) / 255.f, - GLfloat((color >> 8) & 0xff) / 255.f, GLfloat((color >> 0) & 0xff) / 255.f, - GLfloat((color >> 24) & 0xff) / 255.f); - glDrawArrays(GL_TRIANGLES, 0, usage / 4); - - glBindBuffer(GL_ARRAY_BUFFER, - static_cast(g_vertex_manager.get())->GetVertexBufferHandle()); - ProgramShaderCache::InvalidateVertexFormat(); -} -} diff --git a/Source/Core/VideoBackends/OGL/RasterFont.h b/Source/Core/VideoBackends/OGL/RasterFont.h deleted file mode 100644 index 359e4f8551..0000000000 --- a/Source/Core/VideoBackends/OGL/RasterFont.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include - -#include "Common/CommonTypes.h" - -namespace OGL -{ -class RasterFont -{ -public: - RasterFont(); - ~RasterFont(); - static int debug; - - void printMultilineText(const std::string& text, double x, double y, double z, int bbWidth, - int bbHeight, u32 color); - -private: - u32 VBO; - u32 VAO; - u32 texture; - u32 uniform_color_id; - u32 uniform_offset_id; -}; -} diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index 186b6a7da5..35ec1a6715 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -33,7 +33,6 @@ #include "VideoBackends/OGL/OGLTexture.h" #include "VideoBackends/OGL/PostProcessing.h" #include "VideoBackends/OGL/ProgramShaderCache.h" -#include "VideoBackends/OGL/RasterFont.h" #include "VideoBackends/OGL/SamplerCache.h" #include "VideoBackends/OGL/StreamBuffer.h" #include "VideoBackends/OGL/TextureCache.h" @@ -57,14 +56,9 @@ VideoConfig g_ogl_config; // Declarations and definitions // ---------------------------- -static std::unique_ptr s_raster_font; // 1 for no MSAA. Use s_MSAASamples > 1 to check for MSAA. static int s_MSAASamples = 1; -static u32 s_last_multisamples = 1; -static bool s_last_stereo_mode = false; - -static bool s_vsync; // EFB cache related static const u32 EFB_CACHE_RECT_SIZE = 64; // Cache 64x64 blocks. @@ -353,11 +347,14 @@ static void InitDriverInfo() } // Init functions -Renderer::Renderer(std::unique_ptr main_gl_context) +Renderer::Renderer(std::unique_ptr main_gl_context, float backbuffer_scale) : ::Renderer(static_cast(std::max(main_gl_context->GetBackBufferWidth(), 1u)), static_cast(std::max(main_gl_context->GetBackBufferHeight(), 1u)), - AbstractTextureFormat::RGBA8), - m_main_gl_context(std::move(main_gl_context)) + backbuffer_scale, AbstractTextureFormat::RGBA8), + m_main_gl_context(std::move(main_gl_context)), + m_current_rasterization_state(RenderState::GetInvalidRasterizationState()), + m_current_depth_state(RenderState::GetInvalidDepthState()), + m_current_blend_state(RenderState::GetInvalidBlendingState()) { bool bSuccess = true; @@ -724,9 +721,6 @@ Renderer::Renderer(std::unique_ptr main_gl_context) g_Config.VerifyValidity(); UpdateActiveConfig(); - // Since we modify the config here, we need to update the last host bits, it may have changed. - m_last_host_config_bits = ShaderHostConfig::GetCurrent().bits; - OSD::AddMessage(StringFromFormat("Video Info: %s, %s, %s", g_ogl_config.gl_vendor, g_ogl_config.gl_renderer, g_ogl_config.gl_version), 5000); @@ -755,15 +749,9 @@ Renderer::Renderer(std::unique_ptr main_gl_context) g_ogl_config.bSupportsCopySubImage ? "" : "CopyImageSubData ", g_ActiveConfig.backend_info.bSupportsDepthClamp ? "" : "DepthClamp "); - s_last_multisamples = g_ActiveConfig.iMultisamples; - s_MSAASamples = s_last_multisamples; - - s_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; - // Handle VSync on/off - s_vsync = g_ActiveConfig.IsVSync(); if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC)) - m_main_gl_context->SwapInterval(s_vsync); + m_main_gl_context->SwapInterval(g_ActiveConfig.IsVSync()); // Because of the fixed framebuffer size we need to disable the resolution // options while running @@ -824,8 +812,6 @@ bool Renderer::Initialize() m_current_framebuffer_height = m_target_height; m_post_processor = std::make_unique(); - s_raster_font = std::make_unique(); - return true; } @@ -836,7 +822,6 @@ void Renderer::Shutdown() UpdateActiveConfig(); - s_raster_font.reset(); m_post_processor.reset(); } @@ -859,21 +844,6 @@ Renderer::CreateFramebuffer(const AbstractTexture* color_attachment, static_cast(depth_attachment)); } -void Renderer::RenderText(const std::string& text, int left, int top, u32 color) -{ - int screen_width = m_backbuffer_width; - int screen_height = m_backbuffer_height; - if (screen_width >= 2000) - { - screen_width /= 2; - screen_height /= 2; - } - - s_raster_font->printMultilineText(text, left * 2.0f / static_cast(screen_width) - 1.0f, - 1.0f - top * 2.0f / static_cast(screen_height), 0, - screen_width, screen_height, color); -} - std::unique_ptr Renderer::CreateShaderFromSource(ShaderStage stage, const char* source, size_t length) { @@ -1237,37 +1207,55 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaE ClearEFBCache(); } -void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture, - int src_width, int src_height) +void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) { + TargetRectangle source_rc = rc; + source_rc.top = rc.GetHeight(); + source_rc.bottom = 0; + + // Check if we need to render to a new surface. + TargetRectangle flipped_trc = GetTargetRectangle(); + std::swap(flipped_trc.top, flipped_trc.bottom); + + // Copy the framebuffer to screen. OpenGLPostProcessing* post_processor = static_cast(m_post_processor.get()); if (g_ActiveConfig.stereo_mode == StereoMode::SBS || g_ActiveConfig.stereo_mode == StereoMode::TAB) { - TargetRectangle leftRc, rightRc; + TargetRectangle left_rc, right_rc; // Top-and-Bottom mode needs to compensate for inverted vertical screen coordinates. if (g_ActiveConfig.stereo_mode == StereoMode::TAB) - std::tie(rightRc, leftRc) = ConvertStereoRectangle(dst); + std::tie(right_rc, left_rc) = ConvertStereoRectangle(flipped_trc); else - std::tie(leftRc, rightRc) = ConvertStereoRectangle(dst); + std::tie(left_rc, right_rc) = ConvertStereoRectangle(flipped_trc); - post_processor->BlitFromTexture(src, leftRc, src_texture, src_width, src_height, 0); - post_processor->BlitFromTexture(src, rightRc, src_texture, src_width, src_height, 1); + post_processor->BlitFromTexture(source_rc, left_rc, + static_cast(texture)->GetRawTexIdentifier(), + texture->GetWidth(), texture->GetHeight(), 0); + post_processor->BlitFromTexture(source_rc, right_rc, + static_cast(texture)->GetRawTexIdentifier(), + texture->GetWidth(), texture->GetHeight(), 1); } else if (g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer) { glDrawBuffer(GL_BACK_LEFT); - post_processor->BlitFromTexture(src, dst, src_texture, src_width, src_height, 0); + post_processor->BlitFromTexture(source_rc, flipped_trc, + static_cast(texture)->GetRawTexIdentifier(), + texture->GetWidth(), texture->GetHeight(), 0); glDrawBuffer(GL_BACK_RIGHT); - post_processor->BlitFromTexture(src, dst, src_texture, src_width, src_height, 1); + post_processor->BlitFromTexture(source_rc, flipped_trc, + static_cast(texture)->GetRawTexIdentifier(), + texture->GetWidth(), texture->GetHeight(), 1); glDrawBuffer(GL_BACK); } else { - post_processor->BlitFromTexture(src, dst, src_texture, src_width, src_height, 0); + post_processor->BlitFromTexture(source_rc, flipped_trc, + static_cast(texture)->GetRawTexIdentifier(), + texture->GetWidth(), texture->GetHeight(), 0); } } @@ -1402,8 +1390,20 @@ void Renderer::ApplyBlendingState(const BlendingState state, bool force) m_current_blend_state = state; } -// This function has the final picture. We adjust the aspect ratio here. -void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks) +void Renderer::BindBackbuffer(const ClearColor& clear_color) +{ + CheckForSurfaceChange(); + CheckForSurfaceResize(); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + m_current_framebuffer = nullptr; + m_current_framebuffer_width = m_backbuffer_width; + m_current_framebuffer_height = m_backbuffer_height; +} + +void Renderer::PresentBackbuffer() { if (g_ogl_config.bSupportsDebug) { @@ -1413,78 +1413,22 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region glDisable(GL_DEBUG_OUTPUT); } - auto* xfb_texture = static_cast(texture); + // Swap the back and front buffers, presenting the image. + m_main_gl_context->Swap(); +} - TargetRectangle sourceRc = xfb_region; - sourceRc.top = xfb_region.GetHeight(); - sourceRc.bottom = 0; - - ResetAPIState(); - - // Do our OSD callbacks - OSD::DoCallbacks(OSD::CallbackType::OnFrame); - - // Check if we need to render to a new surface. - CheckForSurfaceChange(); - CheckForSurfaceResize(); - UpdateDrawRectangle(); - TargetRectangle flipped_trc = GetTargetRectangle(); - std::swap(flipped_trc.top, flipped_trc.bottom); - - // Skip screen rendering when running in headless mode. - if (!IsHeadless()) +void Renderer::OnConfigChanged(u32 bits) +{ + if (bits & (CONFIG_CHANGE_BIT_TARGET_SIZE | CONFIG_CHANGE_BIT_MULTISAMPLES | + CONFIG_CHANGE_BIT_STEREO_MODE | CONFIG_CHANGE_BIT_BBOX)) { - // Clear the framebuffer before drawing anything. - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - m_current_framebuffer = nullptr; - m_current_framebuffer_width = m_backbuffer_width; - m_current_framebuffer_height = m_backbuffer_height; - - // Copy the framebuffer to screen. - BlitScreen(sourceRc, flipped_trc, xfb_texture->GetRawTexIdentifier(), - xfb_texture->GetConfig().width, xfb_texture->GetConfig().height); - - // Render OSD messages. - glViewport(0, 0, m_backbuffer_width, m_backbuffer_height); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - DrawDebugText(); - OSD::DrawMessages(); - - // Swap the back and front buffers, presenting the image. - m_main_gl_context->Swap(); - } - else - { - // Since we're not swapping in headless mode, ensure all commands are sent to the GPU. - // Otherwise the driver could batch several frames togehter. - glFlush(); - } - - // Was the size changed since the last frame? - bool target_size_changed = CalculateTargetSize(); - bool stencil_buffer_enabled = - static_cast(g_framebuffer_manager.get())->HasStencilBuffer(); - - bool fb_needs_update = target_size_changed || - s_last_multisamples != g_ActiveConfig.iMultisamples || - stencil_buffer_enabled != BoundingBox::NeedsStencilBuffer() || - s_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off); - - if (fb_needs_update) - { - s_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; - s_last_multisamples = g_ActiveConfig.iMultisamples; - s_MSAASamples = s_last_multisamples; - + s_MSAASamples = g_ActiveConfig.iMultisamples; if (s_MSAASamples > 1 && s_MSAASamples > g_ogl_config.max_samples) { s_MSAASamples = g_ogl_config.max_samples; OSD::AddMessage( StringFromFormat("%d Anti Aliasing samples selected, but only %d supported by your GPU.", - s_last_multisamples, g_ogl_config.max_samples), + s_MSAASamples, g_ogl_config.max_samples), 10000); } @@ -1492,40 +1436,13 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region g_framebuffer_manager = std::make_unique( m_target_width, m_target_height, s_MSAASamples, BoundingBox::NeedsStencilBuffer()); BoundingBox::SetTargetSizeChanged(m_target_width, m_target_height); - UpdateDrawRectangle(); } - if (s_vsync != g_ActiveConfig.IsVSync()) - { - s_vsync = g_ActiveConfig.IsVSync(); - if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC)) - m_main_gl_context->SwapInterval(s_vsync); - } + if (bits & CONFIG_CHANGE_BIT_VSYNC && !DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC)) + m_main_gl_context->SwapInterval(g_ActiveConfig.IsVSync()); - // Clean out old stuff from caches. It's not worth it to clean out the shader caches. - g_texture_cache->Cleanup(frameCount); - - RestoreAPIState(); - - g_Config.iSaveTargetId = 0; - - int old_anisotropy = g_ActiveConfig.iMaxAnisotropy; - UpdateActiveConfig(); - g_texture_cache->OnConfigChanged(g_ActiveConfig); - - if (old_anisotropy != g_ActiveConfig.iMaxAnisotropy) + if (bits & CONFIG_CHANGE_BIT_ANISOTROPY) g_sampler_cache->Clear(); - - // Invalidate shader cache when the host config changes. - CheckForHostConfigChanges(); - - // For testing zbuffer targets. - // Renderer::SetZBufferRender(); - // SaveTexture("tex.png", GL_TEXTURE_2D, s_FakeZTarget, - // GetTargetWidth(), GetTargetHeight()); - - // Invalidate EFB cache - ClearEFBCache(); } void Renderer::Flush() @@ -1558,15 +1475,6 @@ void Renderer::CheckForSurfaceResize() m_backbuffer_height = m_main_gl_context->GetBackBufferHeight(); } -void Renderer::DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc, - const TargetRectangle& source_rc) -{ - // for msaa mode, we must resolve the efb content to non-msaa - GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(source_rc); - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); - BlitScreen(source_rc, target_rc, tex, m_target_width, m_target_height); -} - // ALWAYS call RestoreAPIState for each ResetAPIState call you're doing void Renderer::ResetAPIState() { @@ -1585,6 +1493,9 @@ void Renderer::ResetAPIState() } glDepthMask(GL_FALSE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + m_current_rasterization_state = RenderState::GetInvalidRasterizationState(); + m_current_depth_state = RenderState::GetInvalidDepthState(); + m_current_blend_state = RenderState::GetInvalidBlendingState(); } void Renderer::RestoreAPIState() @@ -1603,10 +1514,6 @@ void Renderer::RestoreAPIState() } BPFunctions::SetScissor(); BPFunctions::SetViewport(); - - ApplyRasterizationState(m_current_rasterization_state, true); - ApplyDepthState(m_current_depth_state, true); - ApplyBlendingState(m_current_blend_state, true); } void Renderer::ApplyRasterizationState(const RasterizationState state, bool force) @@ -1695,6 +1602,7 @@ void Renderer::UnbindTexture(const AbstractTexture* texture) glActiveTexture(static_cast(GL_TEXTURE0 + i)); glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + m_bound_textures[i] = nullptr; } } diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index f520c6faa4..398a49377d 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -83,7 +83,7 @@ extern VideoConfig g_ogl_config; class Renderer : public ::Renderer { public: - Renderer(std::unique_ptr main_gl_context); + Renderer(std::unique_ptr main_gl_context, float backbuffer_scale); ~Renderer() override; bool IsHeadless() const override; @@ -118,8 +118,8 @@ public: float far_depth) override; void Draw(u32 base_vertex, u32 num_vertices) override; void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override; - - void RenderText(const std::string& text, int left, int top, u32 color) override; + void BindBackbuffer(const ClearColor& clear_color = {}) override; + void PresentBackbuffer() override; u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override; @@ -132,8 +132,9 @@ public: TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; - void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override; void Flush() override; + void RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) override; + void OnConfigChanged(u32 bits) override; void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override; @@ -152,12 +153,6 @@ private: void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc, const TargetRectangle& targetPixelRc, const void* data); - void DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc, - const TargetRectangle& source_rc); - - void BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture, int src_width, - int src_height); - void CheckForSurfaceChange(); void CheckForSurfaceResize(); @@ -168,8 +163,8 @@ private: std::unique_ptr m_main_gl_context; std::array m_bound_textures{}; const OGLPipeline* m_graphics_pipeline = nullptr; - RasterizationState m_current_rasterization_state = {}; - DepthState m_current_depth_state = {}; - BlendingState m_current_blend_state = {}; + RasterizationState m_current_rasterization_state; + DepthState m_current_depth_state; + BlendingState m_current_blend_state; }; } // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/main.cpp b/Source/Core/VideoBackends/OGL/main.cpp index c4ccbdcfd5..3624a7fd7a 100644 --- a/Source/Core/VideoBackends/OGL/main.cpp +++ b/Source/Core/VideoBackends/OGL/main.cpp @@ -172,7 +172,7 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) if (!InitializeGLExtensions(main_gl_context.get()) || !FillBackendInfo()) return false; - g_renderer = std::make_unique(std::move(main_gl_context)); + g_renderer = std::make_unique(std::move(main_gl_context), wsi.render_surface_scale); g_vertex_manager = std::make_unique(); g_perf_query = GetPerfQuery(); ProgramShaderCache::Init(); diff --git a/Source/Core/VideoBackends/Software/SWOGLWindow.cpp b/Source/Core/VideoBackends/Software/SWOGLWindow.cpp index 81216ff7ef..91c8cf0806 100644 --- a/Source/Core/VideoBackends/Software/SWOGLWindow.cpp +++ b/Source/Core/VideoBackends/Software/SWOGLWindow.cpp @@ -84,14 +84,9 @@ bool SWOGLWindow::Initialize(const WindowSystemInfo& wsi) return true; } -void SWOGLWindow::PrintText(const std::string& text, int x, int y, u32 color) +void SWOGLWindow::ShowImage(const AbstractTexture* image, const EFBRectangle& xfb_region) { - m_text.push_back({text, x, y, color}); -} - -void SWOGLWindow::ShowImage(AbstractTexture* image, const EFBRectangle& xfb_region) -{ - SW::SWTexture* sw_image = static_cast(image); + const SW::SWTexture* sw_image = static_cast(image); m_gl_context->Update(); // just updates the render window position and the backbuffer size GLsizei glWidth = (GLsizei)m_gl_context->GetBackBufferWidth(); @@ -116,11 +111,5 @@ void SWOGLWindow::ShowImage(AbstractTexture* image, const EFBRectangle& xfb_regi glBindVertexArray(m_image_vao); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - // TODO: implement OSD - // for (TextData& text : m_text) - // { - // } - m_text.clear(); - m_gl_context->Swap(); } diff --git a/Source/Core/VideoBackends/Software/SWOGLWindow.h b/Source/Core/VideoBackends/Software/SWOGLWindow.h index 38ae316d07..f1eadc4dd5 100644 --- a/Source/Core/VideoBackends/Software/SWOGLWindow.h +++ b/Source/Core/VideoBackends/Software/SWOGLWindow.h @@ -24,11 +24,8 @@ public: GLContext* GetContext() const { return m_gl_context.get(); } bool IsHeadless() const; - // Will be printed on the *next* image - void PrintText(const std::string& text, int x, int y, u32 color); - // Image to show, will be swapped immediately - void ShowImage(AbstractTexture* image, const EFBRectangle& xfb_region); + void ShowImage(const AbstractTexture* image, const EFBRectangle& xfb_region); static std::unique_ptr Create(const WindowSystemInfo& wsi); @@ -37,14 +34,6 @@ private: bool Initialize(const WindowSystemInfo& wsi); - struct TextData - { - std::string text; - int x, y; - u32 color; - }; - std::vector m_text; - u32 m_image_program = 0; u32 m_image_texture = 0; u32 m_image_vao = 0; diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp index 0b64022725..6369b35b20 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.cpp +++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp @@ -25,7 +25,7 @@ #include "VideoCommon/VideoConfig.h" SWRenderer::SWRenderer(std::unique_ptr window) - : ::Renderer(static_cast(MAX_XFB_WIDTH), static_cast(MAX_XFB_HEIGHT), + : ::Renderer(static_cast(MAX_XFB_WIDTH), static_cast(MAX_XFB_HEIGHT), 1.0f, AbstractTextureFormat::RGBA8), m_window(std::move(window)) { @@ -55,11 +55,6 @@ SWRenderer::CreateFramebuffer(const AbstractTexture* color_attachment, static_cast(depth_attachment)); } -void SWRenderer::RenderText(const std::string& pstr, int left, int top, u32 color) -{ - m_window->PrintText(pstr, left, top, color); -} - class SWShader final : public AbstractShader { public: @@ -95,17 +90,10 @@ std::unique_ptr SWRenderer::CreatePipeline(const AbstractPipel } // Called on the GPU thread -void SWRenderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks) +void SWRenderer::RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& xfb_region) { - OSD::DoCallbacks(OSD::CallbackType::OnFrame); - if (!IsHeadless()) - { - DrawDebugText(); m_window->ShowImage(texture, xfb_region); - } - - UpdateActiveConfig(); } u32 SWRenderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData) diff --git a/Source/Core/VideoBackends/Software/SWRenderer.h b/Source/Core/VideoBackends/Software/SWRenderer.h index 936139516f..88c5ece360 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.h +++ b/Source/Core/VideoBackends/Software/SWRenderer.h @@ -32,7 +32,6 @@ public: size_t length) override; std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config) override; - void RenderText(const std::string& pstr, int left, int top, u32 color) override; u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {} u16 BBoxRead(int index) override; @@ -40,7 +39,7 @@ public: TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; - void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override; + void RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) override; void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override; diff --git a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt index 889da03f6f..5746cd1aaa 100644 --- a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt +++ b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt @@ -5,7 +5,6 @@ add_library(videovulkan ObjectCache.cpp PerfQuery.cpp PostProcessing.cpp - RasterFont.cpp Renderer.cpp ShaderCache.cpp ShaderCompiler.cpp diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp index 8f5721b4d3..1de68b8349 100644 --- a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp @@ -54,6 +54,15 @@ bool ObjectCache::Initialize() if (!CreateStaticSamplers()) return false; + m_texture_upload_buffer = + StreamBuffer::Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, INITIAL_TEXTURE_UPLOAD_BUFFER_SIZE, + MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE); + if (!m_texture_upload_buffer) + { + PanicAlert("Failed to create texture upload buffer"); + return false; + } + m_utility_shader_vertex_buffer = StreamBuffer::Create(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 1024 * 1024, 4 * 1024 * 1024); m_utility_shader_uniform_buffer = diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.h b/Source/Core/VideoBackends/Vulkan/ObjectCache.h index 5b7939f95b..c3502f7c66 100644 --- a/Source/Core/VideoBackends/Vulkan/ObjectCache.h +++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.h @@ -58,6 +58,7 @@ public: { return m_utility_shader_uniform_buffer.get(); } + StreamBuffer* GetTextureUploadBuffer() const { return m_texture_upload_buffer.get(); } // Static samplers VkSampler GetPointSampler() const { return m_point_sampler; } @@ -94,6 +95,7 @@ private: std::unique_ptr m_utility_shader_vertex_format; std::unique_ptr m_utility_shader_vertex_buffer; std::unique_ptr m_utility_shader_uniform_buffer; + std::unique_ptr m_texture_upload_buffer; VkSampler m_point_sampler = VK_NULL_HANDLE; VkSampler m_linear_sampler = VK_NULL_HANDLE; diff --git a/Source/Core/VideoBackends/Vulkan/PostProcessing.cpp b/Source/Core/VideoBackends/Vulkan/PostProcessing.cpp index 88bb29eee7..57dbd3e391 100644 --- a/Source/Core/VideoBackends/Vulkan/PostProcessing.cpp +++ b/Source/Core/VideoBackends/Vulkan/PostProcessing.cpp @@ -28,9 +28,8 @@ VulkanPostProcessing::~VulkanPostProcessing() vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_fragment_shader, nullptr); } -bool VulkanPostProcessing::Initialize(const Texture2D* font_texture) +bool VulkanPostProcessing::Initialize() { - m_font_texture = font_texture; if (!CompileDefaultShader()) return false; @@ -63,7 +62,6 @@ void VulkanPostProcessing::BlitFromTexture(const TargetRectangle& dst, const Tar u8* uniforms = draw.AllocatePSUniforms(uniforms_size); FillUniformBuffer(uniforms, src, src_tex, src_layer); draw.CommitPSUniforms(uniforms_size); - draw.SetPSSampler(1, m_font_texture->GetView(), g_object_cache->GetLinearSampler()); } draw.DrawQuad(dst.left, dst.top, dst.GetWidth(), dst.GetHeight(), src.left, src.top, src_layer, @@ -174,11 +172,6 @@ constexpr char POSTPROCESSING_SHADER_HEADER[] = R"( #define SampleOffset(offset) float4(textureOffset(samp0, uv0, offset).xyz, 1.0) - float4 SampleFontLocation(float2 location) - { - return texture(samp1, float3(location, 0.0)); - } - float2 GetResolution() { return options.resolution.xy; diff --git a/Source/Core/VideoBackends/Vulkan/PostProcessing.h b/Source/Core/VideoBackends/Vulkan/PostProcessing.h index 9c07c47772..e9c18b9d2d 100644 --- a/Source/Core/VideoBackends/Vulkan/PostProcessing.h +++ b/Source/Core/VideoBackends/Vulkan/PostProcessing.h @@ -21,7 +21,7 @@ public: VulkanPostProcessing() = default; ~VulkanPostProcessing(); - bool Initialize(const Texture2D* font_texture); + bool Initialize(); void BlitFromTexture(const TargetRectangle& dst, const TargetRectangle& src, const Texture2D* src_tex, int src_layer, VkRenderPass render_pass); @@ -37,7 +37,6 @@ private: bool RecompileShader(); std::string GetGLSLUniformBlock() const; - const Texture2D* m_font_texture = nullptr; VkShaderModule m_fragment_shader = VK_NULL_HANDLE; VkShaderModule m_default_fragment_shader = VK_NULL_HANDLE; }; diff --git a/Source/Core/VideoBackends/Vulkan/RasterFont.cpp b/Source/Core/VideoBackends/Vulkan/RasterFont.cpp deleted file mode 100644 index 7410ba5c86..0000000000 --- a/Source/Core/VideoBackends/Vulkan/RasterFont.cpp +++ /dev/null @@ -1,421 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "VideoBackends/Vulkan/RasterFont.h" - -#include -#include - -#include "VideoBackends/Vulkan/CommandBufferManager.h" -#include "VideoBackends/Vulkan/Texture2D.h" -#include "VideoBackends/Vulkan/Util.h" -#include "VideoBackends/Vulkan/VulkanContext.h" - -// Based on OGL RasterFont -// TODO: We should move this to common. - -namespace Vulkan -{ -constexpr int CHARACTER_WIDTH = 8; -constexpr int CHARACTER_HEIGHT = 13; -constexpr int CHARACTER_OFFSET = 32; -constexpr int CHARACTER_COUNT = 95; - -static const u8 rasters[CHARACTER_COUNT][CHARACTER_HEIGHT] = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36}, - {0x00, 0x00, 0x00, 0x66, 0x66, 0xff, 0x66, 0x66, 0xff, 0x66, 0x66, 0x00, 0x00}, - {0x00, 0x00, 0x18, 0x7e, 0xff, 0x1b, 0x1f, 0x7e, 0xf8, 0xd8, 0xff, 0x7e, 0x18}, - {0x00, 0x00, 0x0e, 0x1b, 0xdb, 0x6e, 0x30, 0x18, 0x0c, 0x76, 0xdb, 0xd8, 0x70}, - {0x00, 0x00, 0x7f, 0xc6, 0xcf, 0xd8, 0x70, 0x70, 0xd8, 0xcc, 0xcc, 0x6c, 0x38}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1c, 0x0c, 0x0e}, - {0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c}, - {0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30}, - {0x00, 0x00, 0x00, 0x00, 0x99, 0x5a, 0x3c, 0xff, 0x3c, 0x5a, 0x99, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00}, - {0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03}, - {0x00, 0x00, 0x3c, 0x66, 0xc3, 0xe3, 0xf3, 0xdb, 0xcf, 0xc7, 0xc3, 0x66, 0x3c}, - {0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x38, 0x18}, - {0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0xe7, 0x7e}, - {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0x07, 0x03, 0x03, 0xe7, 0x7e}, - {0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c}, - {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xff}, - {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e}, - {0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x03, 0x03, 0xff}, - {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e}, - {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x03, 0x7f, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e}, - {0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06}, - {0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60}, - {0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x0c, 0x06, 0x03, 0xc3, 0xc3, 0x7e}, - {0x00, 0x00, 0x3f, 0x60, 0xcf, 0xdb, 0xd3, 0xdd, 0xc3, 0x7e, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18}, - {0x00, 0x00, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe}, - {0x00, 0x00, 0x7e, 0xe7, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e}, - {0x00, 0x00, 0xfc, 0xce, 0xc7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc7, 0xce, 0xfc}, - {0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xff}, - {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xff}, - {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xcf, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e}, - {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, - {0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e}, - {0x00, 0x00, 0x7c, 0xee, 0xc6, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06}, - {0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6, 0xc3}, - {0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0}, - {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xff, 0xff, 0xe7, 0xc3}, - {0x00, 0x00, 0xc7, 0xc7, 0xcf, 0xcf, 0xdf, 0xdb, 0xfb, 0xf3, 0xf3, 0xe3, 0xe3}, - {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe7, 0x7e}, - {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe}, - {0x00, 0x00, 0x3f, 0x6e, 0xdf, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c}, - {0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe}, - {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0xe0, 0xc0, 0xc0, 0xe7, 0x7e}, - {0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff}, - {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, - {0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, - {0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, - {0x00, 0x00, 0xc3, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3}, - {0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3}, - {0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x7e, 0x0c, 0x06, 0x03, 0x03, 0xff}, - {0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c}, - {0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60}, - {0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18}, - {0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x30, 0x70}, - {0x00, 0x00, 0x7f, 0xc3, 0xc3, 0x7f, 0x03, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0}, - {0x00, 0x00, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x03, 0x03, 0x03, 0x03, 0x03}, - {0x00, 0x00, 0x7f, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x33, 0x1e}, - {0x7e, 0xc3, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0}, - {0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00}, - {0x38, 0x6c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x00}, - {0x00, 0x00, 0xc6, 0xcc, 0xf8, 0xf0, 0xd8, 0xcc, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0}, - {0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78}, - {0x00, 0x00, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xfe, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00}, - {0xc0, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0x00, 0x00, 0x00, 0x00}, - {0x03, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, 0xfe, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xfe, 0x03, 0x03, 0x7e, 0xc0, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00}, - {0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xc3, 0xe7, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00}, - {0xc0, 0x60, 0x60, 0x30, 0x18, 0x3c, 0x66, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xff, 0x60, 0x30, 0x18, 0x0c, 0x06, 0xff, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x0f, 0x18, 0x18, 0x18, 0x38, 0xf0, 0x38, 0x18, 0x18, 0x18, 0x0f}, - {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}, - {0x00, 0x00, 0xf0, 0x18, 0x18, 0x18, 0x1c, 0x0f, 0x1c, 0x18, 0x18, 0x18, 0xf0}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x8f, 0xf1, 0x60, 0x00, 0x00, 0x00}}; - -static const char VERTEX_SHADER_SOURCE[] = R"( - -layout(std140, push_constant) uniform PCBlock { - vec2 char_size; - vec2 offset; - vec4 color; -} PC; - -layout(location = 0) in vec4 ipos; -layout(location = 5) in vec4 icol0; -layout(location = 8) in vec3 itex0; - -layout(location = 0) out vec2 uv0; - -void main() -{ - gl_Position = vec4(ipos.xy + PC.offset, 0.0f, 1.0f); - gl_Position.y = -gl_Position.y; - uv0 = itex0.xy * PC.char_size; -} - -)"; - -static const char FRAGMENT_SHADER_SOURCE[] = R"( - -layout(std140, push_constant) uniform PCBlock { - vec2 char_size; - vec2 offset; - vec4 color; -} PC; - -layout(set = 1, binding = 0) uniform sampler2DArray samp0; - -layout(location = 0) in vec2 uv0; - -layout(location = 0) out vec4 ocol0; - -void main() -{ - ocol0 = texture(samp0, float3(uv0, 0.0)) * PC.color; -} - -)"; - -RasterFont::RasterFont() -{ -} - -RasterFont::~RasterFont() -{ - if (m_vertex_shader != VK_NULL_HANDLE) - vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_vertex_shader, nullptr); - if (m_fragment_shader != VK_NULL_HANDLE) - vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_fragment_shader, nullptr); -} - -const Texture2D* RasterFont::GetTexture() const -{ - return m_texture.get(); -} - -bool RasterFont::Initialize() -{ - // Create shaders and texture - if (!CreateShaders() || !CreateTexture()) - return false; - - return true; -} - -bool RasterFont::CreateTexture() -{ - // generate the texture - std::vector texture_data(CHARACTER_WIDTH * CHARACTER_COUNT * CHARACTER_HEIGHT); - for (int y = 0; y < CHARACTER_HEIGHT; y++) - { - for (int c = 0; c < CHARACTER_COUNT; c++) - { - for (int x = 0; x < CHARACTER_WIDTH; x++) - { - bool pixel = (0 != (rasters[c][y] & (1 << (CHARACTER_WIDTH - x - 1)))); - texture_data[CHARACTER_WIDTH * CHARACTER_COUNT * y + CHARACTER_WIDTH * c + x] = - pixel ? -1 : 0; - } - } - } - - // create the actual texture object - m_texture = Texture2D::Create(CHARACTER_WIDTH * CHARACTER_COUNT, CHARACTER_HEIGHT, 1, 1, - VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, - VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); - if (!m_texture) - return false; - - // create temporary buffer for uploading texture - VkBufferCreateInfo buffer_info = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - nullptr, - 0, - static_cast(texture_data.size() * sizeof(u32)), - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_SHARING_MODE_EXCLUSIVE, - 0, - nullptr}; - VkBuffer temp_buffer; - VkResult res = vkCreateBuffer(g_vulkan_context->GetDevice(), &buffer_info, nullptr, &temp_buffer); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: "); - return false; - } - - VkMemoryRequirements memory_requirements; - vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), temp_buffer, &memory_requirements); - uint32_t memory_type_index = g_vulkan_context->GetMemoryType(memory_requirements.memoryTypeBits, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); - VkMemoryAllocateInfo memory_allocate_info = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, nullptr, - memory_requirements.size, memory_type_index}; - VkDeviceMemory temp_buffer_memory; - res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, - &temp_buffer_memory); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: "); - vkDestroyBuffer(g_vulkan_context->GetDevice(), temp_buffer, nullptr); - return false; - } - - // Bind buffer to memory - res = vkBindBufferMemory(g_vulkan_context->GetDevice(), temp_buffer, temp_buffer_memory, 0); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkBindBufferMemory failed: "); - vkDestroyBuffer(g_vulkan_context->GetDevice(), temp_buffer, nullptr); - vkFreeMemory(g_vulkan_context->GetDevice(), temp_buffer_memory, nullptr); - return false; - } - - // Copy into buffer - void* mapped_ptr; - res = vkMapMemory(g_vulkan_context->GetDevice(), temp_buffer_memory, 0, buffer_info.size, 0, - &mapped_ptr); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkMapMemory failed: "); - vkDestroyBuffer(g_vulkan_context->GetDevice(), temp_buffer, nullptr); - vkFreeMemory(g_vulkan_context->GetDevice(), temp_buffer_memory, nullptr); - return false; - } - - // Copy texture data into staging buffer - memcpy(mapped_ptr, texture_data.data(), texture_data.size() * sizeof(u32)); - vkUnmapMemory(g_vulkan_context->GetDevice(), temp_buffer_memory); - - // Copy from staging buffer to the final texture - VkBufferImageCopy region = {0, - CHARACTER_WIDTH * CHARACTER_COUNT, - CHARACTER_HEIGHT, - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, - {0, 0, 0}, - {CHARACTER_WIDTH * CHARACTER_COUNT, CHARACTER_HEIGHT, 1}}; - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - vkCmdCopyBufferToImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), temp_buffer, - m_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - - // Free temp buffers after command buffer executes - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - g_command_buffer_mgr->DeferBufferDestruction(temp_buffer); - g_command_buffer_mgr->DeferDeviceMemoryDestruction(temp_buffer_memory); - return true; -} - -bool RasterFont::CreateShaders() -{ - m_vertex_shader = Util::CompileAndCreateVertexShader(VERTEX_SHADER_SOURCE); - m_fragment_shader = Util::CompileAndCreateFragmentShader(FRAGMENT_SHADER_SOURCE); - return m_vertex_shader != VK_NULL_HANDLE && m_fragment_shader != VK_NULL_HANDLE; -} - -void RasterFont::PrintMultiLineText(VkRenderPass render_pass, const std::string& text, - float start_x, float start_y, u32 bbWidth, u32 bbHeight, - u32 color) -{ - // skip empty strings - if (text.empty()) - return; - - UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), - render_pass, m_vertex_shader, VK_NULL_HANDLE, m_fragment_shader, - PrimitiveType::Triangles); - - UtilityShaderVertex* vertices = draw.ReserveVertices(text.length() * 6); - size_t num_vertices = 0; - if (!vertices) - return; - - float delta_x = float(2 * CHARACTER_WIDTH) / float(bbWidth); - float delta_y = float(2 * CHARACTER_HEIGHT) / float(bbHeight); - float border_x = 2.0f / float(bbWidth); - float border_y = 4.0f / float(bbHeight); - - float x = float(start_x); - float y = float(start_y); - - for (const char& c : text) - { - if (c == '\n') - { - x = float(start_x); - y -= delta_y + border_y; - continue; - } - - // do not print spaces, they can be skipped easily - if (c == ' ') - { - x += delta_x + border_x; - continue; - } - - if (c < CHARACTER_OFFSET || c >= CHARACTER_COUNT + CHARACTER_OFFSET) - continue; - - vertices[num_vertices].SetPosition(x, y); - vertices[num_vertices].SetTextureCoordinates(static_cast(c - CHARACTER_OFFSET), 0.0f); - num_vertices++; - - vertices[num_vertices].SetPosition(x + delta_x, y); - vertices[num_vertices].SetTextureCoordinates(static_cast(c - CHARACTER_OFFSET + 1), - 0.0f); - num_vertices++; - - vertices[num_vertices].SetPosition(x + delta_x, y + delta_y); - vertices[num_vertices].SetTextureCoordinates(static_cast(c - CHARACTER_OFFSET + 1), - 1.0f); - num_vertices++; - - vertices[num_vertices].SetPosition(x, y); - vertices[num_vertices].SetTextureCoordinates(static_cast(c - CHARACTER_OFFSET), 0.0f); - num_vertices++; - - vertices[num_vertices].SetPosition(x + delta_x, y + delta_y); - vertices[num_vertices].SetTextureCoordinates(static_cast(c - CHARACTER_OFFSET + 1), - 1.0f); - num_vertices++; - - vertices[num_vertices].SetPosition(x, y + delta_y); - vertices[num_vertices].SetTextureCoordinates(static_cast(c - CHARACTER_OFFSET), 1.0f); - num_vertices++; - - x += delta_x + border_x; - } - - // skip all whitespace strings - if (num_vertices == 0) - return; - - draw.CommitVertices(num_vertices); - - struct PCBlock - { - float char_size[2]; - float offset[2]; - float color[4]; - } pc_block = {}; - - pc_block.char_size[0] = 1.0f / static_cast(CHARACTER_COUNT); - pc_block.char_size[1] = 1.0f; - - // shadows - pc_block.offset[0] = 2.0f / bbWidth; - pc_block.offset[1] = -2.0f / bbHeight; - pc_block.color[3] = (color >> 24) / 255.0f; - - draw.SetPushConstants(&pc_block, sizeof(pc_block)); - draw.SetPSSampler(0, m_texture->GetView(), g_object_cache->GetLinearSampler()); - - // Setup alpha blending - BlendingState blend_state = RenderState::GetNoBlendingBlendState(); - blend_state.blendenable = true; - blend_state.srcfactor = BlendMode::SRCALPHA; - blend_state.dstfactor = BlendMode::INVSRCALPHA; - draw.SetBlendState(blend_state); - - draw.Draw(); - - // non-shadowed part - pc_block.offset[0] = 0.0f; - pc_block.offset[1] = 0.0f; - pc_block.color[0] = ((color >> 16) & 0xFF) / 255.0f; - pc_block.color[1] = ((color >> 8) & 0xFF) / 255.0f; - pc_block.color[2] = (color & 0xFF) / 255.0f; - draw.SetPushConstants(&pc_block, sizeof(pc_block)); - draw.Draw(); -} - -} // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/RasterFont.h b/Source/Core/VideoBackends/Vulkan/RasterFont.h deleted file mode 100644 index f12cf0f16c..0000000000 --- a/Source/Core/VideoBackends/Vulkan/RasterFont.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include -#include - -#include "Common/CommonTypes.h" - -#include "VideoBackends/Vulkan/Constants.h" - -namespace Vulkan -{ -class Texture2D; - -class RasterFont -{ -public: - RasterFont(); - ~RasterFont(); - - const Texture2D* GetTexture() const; - - bool Initialize(); - - void PrintMultiLineText(VkRenderPass render_pass, const std::string& text, float start_x, - float start_y, u32 bbWidth, u32 bbHeight, u32 color); - -private: - bool CreateTexture(); - bool CreateShaders(); - - std::unique_ptr m_texture; - - VkShaderModule m_vertex_shader = VK_NULL_HANDLE; - VkShaderModule m_fragment_shader = VK_NULL_HANDLE; -}; - -} // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 718bdec8d7..dffe3fdd3c 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -21,7 +21,6 @@ #include "VideoBackends/Vulkan/FramebufferManager.h" #include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoBackends/Vulkan/PostProcessing.h" -#include "VideoBackends/Vulkan/RasterFont.h" #include "VideoBackends/Vulkan/Renderer.h" #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/StreamBuffer.h" @@ -47,9 +46,9 @@ namespace Vulkan { -Renderer::Renderer(std::unique_ptr swap_chain) +Renderer::Renderer(std::unique_ptr swap_chain, float backbuffer_scale) : ::Renderer(swap_chain ? static_cast(swap_chain->GetWidth()) : 1, - swap_chain ? static_cast(swap_chain->GetHeight()) : 0, + swap_chain ? static_cast(swap_chain->GetHeight()) : 0, backbuffer_scale, swap_chain ? swap_chain->GetTextureFormat() : AbstractTextureFormat::Undefined), m_swap_chain(std::move(swap_chain)) { @@ -89,13 +88,6 @@ bool Renderer::Initialize() return false; } - m_raster_font = std::make_unique(); - if (!m_raster_font->Initialize()) - { - PanicAlert("Failed to initialize raster font."); - return false; - } - // Swap chain render pass. if (m_swap_chain) { @@ -130,8 +122,7 @@ bool Renderer::Initialize() // Initialize post processing. m_post_processor = std::make_unique(); - if (!static_cast(m_post_processor.get()) - ->Initialize(m_raster_font->GetTexture())) + if (!static_cast(m_post_processor.get())->Initialize()) { PanicAlert("failed to initialize post processor."); return false; @@ -232,44 +223,11 @@ Renderer::CreateFramebuffer(const AbstractTexture* color_attachment, static_cast(depth_attachment)); } -std::tuple Renderer::UpdateUtilityUniformBuffer(const void* uniforms, - u32 uniforms_size) -{ - StreamBuffer* ubo_buf = g_object_cache->GetUtilityShaderUniformBuffer(); - if (!ubo_buf->ReserveMemory(uniforms_size, g_vulkan_context->GetUniformBufferAlignment())) - { - Util::ExecuteCurrentCommandsAndRestoreState(false, true); - if (!ubo_buf->ReserveMemory(uniforms_size, g_vulkan_context->GetUniformBufferAlignment())) - { - PanicAlert("Failed to reserve uniform buffer space for utility draw."); - return {}; - } - } - - VkBuffer ubo = ubo_buf->GetBuffer(); - u32 ubo_offset = static_cast(ubo_buf->GetCurrentOffset()); - std::memcpy(ubo_buf->GetCurrentHostPointer(), uniforms, uniforms_size); - ubo_buf->CommitMemory(uniforms_size); - - return std::tie(ubo, ubo_offset); -} - void Renderer::SetPipeline(const AbstractPipeline* pipeline) { StateTracker::GetInstance()->SetPipeline(static_cast(pipeline)); } -void Renderer::RenderText(const std::string& text, int left, int top, u32 color) -{ - u32 backbuffer_width = m_swap_chain->GetWidth(); - u32 backbuffer_height = m_swap_chain->GetHeight(); - - m_raster_font->PrintMultiLineText(m_swap_chain_render_pass, text, - left * 2.0f / static_cast(backbuffer_width) - 1, - 1 - top * 2.0f / static_cast(backbuffer_height), - backbuffer_width, backbuffer_height, color); -} - u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) { if (type == EFBAccessType::PeekColor) @@ -577,76 +535,24 @@ void Renderer::ReinterpretPixelData(unsigned int convtype) BindEFBToStateTracker(); } -void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks) +void Renderer::Flush() { - // Pending/batched EFB pokes should be included in the final image. - FramebufferManager::GetInstance()->FlushEFBPokes(); + Util::ExecuteCurrentCommandsAndRestoreState(true, false); +} - auto* xfb_texture = static_cast(texture); - - // End the current render pass. +void Renderer::BindBackbuffer(const ClearColor& clear_color) +{ StateTracker::GetInstance()->EndRenderPass(); - StateTracker::GetInstance()->OnEndFrame(); // Handle host window resizes. CheckForSurfaceChange(); CheckForSurfaceResize(); - // There are a few variables which can alter the final window draw rectangle, and some of them - // are determined by guest state. Currently, the only way to catch these is to update every frame. - UpdateDrawRectangle(); - // Ensure the worker thread is not still submitting a previous command buffer. // In other words, the last frame has been submitted (otherwise the next call would // be a race, as the image may not have been consumed yet). g_command_buffer_mgr->PrepareToSubmitCommandBuffer(); - // Draw to the screen if we have a swap chain. - if (m_swap_chain) - { - DrawScreen(xfb_texture, xfb_region); - - // Submit the current command buffer, signaling rendering finished semaphore when it's done - // Because this final command buffer is rendering to the swap chain, we need to wait for - // the available semaphore to be signaled before executing the buffer. This final submission - // can happen off-thread in the background while we're preparing the next frame. - g_command_buffer_mgr->SubmitCommandBuffer( - true, m_image_available_semaphore, m_rendering_finished_semaphore, - m_swap_chain->GetSwapChain(), m_swap_chain->GetCurrentImageIndex()); - } - else - { - // No swap chain, just execute command buffer. - g_command_buffer_mgr->SubmitCommandBuffer(true); - } - - // NOTE: It is important that no rendering calls are made to the EFB between submitting the - // (now-previous) frame and after the below config checks are completed. If the target size - // changes, as the resize methods to not defer the destruction of the framebuffer, the current - // command buffer will contain references to a now non-existent framebuffer. - - // Prep for the next frame (get command buffer ready) before doing anything else. - BeginFrame(); - - // Restore the EFB color texture to color attachment ready for rendering the next frame. - FramebufferManager::GetInstance()->GetEFBColorTexture()->TransitionToLayout( - g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - RestoreAPIState(); - - // Determine what (if anything) has changed in the config. - CheckForConfigChanges(); - - // Clean up stale textures. - TextureCache::GetInstance()->Cleanup(frameCount); -} - -void Renderer::Flush() -{ - Util::ExecuteCurrentCommandsAndRestoreState(true, false); -} - -void Renderer::DrawScreen(VKTexture* xfb_texture, const EFBRectangle& xfb_region) -{ VkResult res; if (!g_command_buffer_mgr->CheckLastPresentFail()) { @@ -696,49 +602,65 @@ void Renderer::DrawScreen(VKTexture* xfb_texture, const EFBRectangle& xfb_region // Begin render pass for rendering to the swap chain. VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; StateTracker::GetInstance()->BeginClearRenderPass(region, &clear_value, 1); +} - // Draw - BlitScreen(m_swap_chain_render_pass, GetTargetRectangle(), xfb_region, - xfb_texture->GetRawTexIdentifier()); - - // Draw OSD - Util::SetViewportAndScissor(g_command_buffer_mgr->GetCurrentCommandBuffer(), 0, 0, - backbuffer->GetWidth(), backbuffer->GetHeight()); - DrawDebugText(); - OSD::DoCallbacks(OSD::CallbackType::OnFrame); - OSD::DrawMessages(); - +void Renderer::PresentBackbuffer() +{ // End drawing to backbuffer StateTracker::GetInstance()->EndRenderPass(); + StateTracker::GetInstance()->OnEndFrame(); // Transition the backbuffer to PRESENT_SRC to ensure all commands drawing // to it have finished before present. + Texture2D* backbuffer = m_swap_chain->GetCurrentTexture(); backbuffer->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + + // Submit the current command buffer, signaling rendering finished semaphore when it's done + // Because this final command buffer is rendering to the swap chain, we need to wait for + // the available semaphore to be signaled before executing the buffer. This final submission + // can happen off-thread in the background while we're preparing the next frame. + g_command_buffer_mgr->SubmitCommandBuffer( + true, m_image_available_semaphore, m_rendering_finished_semaphore, + m_swap_chain->GetSwapChain(), m_swap_chain->GetCurrentImageIndex()); + + BeginFrame(); } -void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_rect, - const TargetRectangle& src_rect, const Texture2D* src_tex) +void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) { + const TargetRectangle target_rc = GetTargetRectangle(); + VulkanPostProcessing* post_processor = static_cast(m_post_processor.get()); if (g_ActiveConfig.stereo_mode == StereoMode::SBS || g_ActiveConfig.stereo_mode == StereoMode::TAB) { TargetRectangle left_rect; TargetRectangle right_rect; - std::tie(left_rect, right_rect) = ConvertStereoRectangle(dst_rect); + std::tie(left_rect, right_rect) = ConvertStereoRectangle(target_rc); - post_processor->BlitFromTexture(left_rect, src_rect, src_tex, 0, render_pass); - post_processor->BlitFromTexture(right_rect, src_rect, src_tex, 1, render_pass); + post_processor->BlitFromTexture(left_rect, rc, + static_cast(texture)->GetRawTexIdentifier(), + 0, m_swap_chain_render_pass); + post_processor->BlitFromTexture(right_rect, rc, + static_cast(texture)->GetRawTexIdentifier(), + 1, m_swap_chain_render_pass); } else if (g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer) { - post_processor->BlitFromTexture(dst_rect, src_rect, src_tex, -1, render_pass); + post_processor->BlitFromTexture(target_rc, rc, + static_cast(texture)->GetRawTexIdentifier(), + -1, m_swap_chain_render_pass); } else { - post_processor->BlitFromTexture(dst_rect, src_rect, src_tex, 0, render_pass); + post_processor->BlitFromTexture(target_rc, rc, + static_cast(texture)->GetRawTexIdentifier(), + 0, m_swap_chain_render_pass); } + + // The post-processor uses the old-style Vulkan draws, which mess with the tracked state. + StateTracker::GetInstance()->SetPendingRebind(); } void Renderer::CheckForSurfaceChange() @@ -787,36 +709,20 @@ void Renderer::CheckForSurfaceResize() OnSwapChainResized(); } -void Renderer::CheckForConfigChanges() +void Renderer::OnConfigChanged(u32 bits) { - // Save the video config so we can compare against to determine which settings have changed. - const u32 old_multisamples = g_ActiveConfig.iMultisamples; - const int old_anisotropy = g_ActiveConfig.iMaxAnisotropy; - const bool old_force_filtering = g_ActiveConfig.bForceFiltering; - - // Copy g_Config to g_ActiveConfig. - // NOTE: This can potentially race with the UI thread, however if it does, the changes will be - // delayed until the next time CheckForConfigChanges is called. - UpdateActiveConfig(); - - // Determine which (if any) settings have changed. - const bool multisamples_changed = old_multisamples != g_ActiveConfig.iMultisamples; - const bool anisotropy_changed = old_anisotropy != g_ActiveConfig.iMaxAnisotropy; - const bool force_texture_filtering_changed = - old_force_filtering != g_ActiveConfig.bForceFiltering; - // Update texture cache settings with any changed options. TextureCache::GetInstance()->OnConfigChanged(g_ActiveConfig); // Handle settings that can cause the EFB framebuffer to change. - if (CalculateTargetSize() || multisamples_changed) + if (bits & CONFIG_CHANGE_BIT_TARGET_SIZE) RecreateEFBFramebuffer(); // MSAA samples changed, we need to recreate the EFB render pass. // If the stereoscopy mode changed, we need to recreate the buffers as well. // SSAA changed on/off, we have to recompile shaders. // Changing stereoscopy from off<->on also requires shaders to be recompiled. - if (CheckForHostConfigChanges()) + if (bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES)) { RecreateEFBFramebuffer(); RecompileShaders(); @@ -826,22 +732,21 @@ void Renderer::CheckForConfigChanges() } // For vsync, we need to change the present mode, which means recreating the swap chain. - if (m_swap_chain && g_ActiveConfig.IsVSync() != m_swap_chain->IsVSyncEnabled()) + if (m_swap_chain && bits & CONFIG_CHANGE_BIT_VSYNC) { g_command_buffer_mgr->WaitForGPUIdle(); m_swap_chain->SetVSync(g_ActiveConfig.IsVSync()); } // For quad-buffered stereo we need to change the layer count, so recreate the swap chain. - if (m_swap_chain && - (g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer) != m_swap_chain->IsStereoEnabled()) + if (m_swap_chain && bits & CONFIG_CHANGE_BIT_STEREO_MODE) { g_command_buffer_mgr->WaitForGPUIdle(); m_swap_chain->RecreateSwapChain(); } // Wipe sampler cache if force texture filtering or anisotropy changes. - if (anisotropy_changed || force_texture_filtering_changed) + if (bits & (CONFIG_CHANGE_BIT_ANISOTROPY | CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING)) ResetSamplerStates(); // Check for a changed post-processing shader and recompile if needed. diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index d5e89b17e2..e6b1c910b5 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -7,7 +7,6 @@ #include #include #include -#include #include "Common/CommonTypes.h" #include "VideoBackends/Vulkan/Constants.h" @@ -22,7 +21,6 @@ class FramebufferManager; class SwapChain; class StagingTexture2D; class Texture2D; -class RasterFont; class VKFramebuffer; class VKPipeline; class VKTexture; @@ -30,7 +28,7 @@ class VKTexture; class Renderer : public ::Renderer { public: - Renderer(std::unique_ptr swap_chain); + Renderer(std::unique_ptr swap_chain, float backbuffer_scale); ~Renderer() override; static Renderer* GetInstance(); @@ -55,15 +53,15 @@ public: SwapChain* GetSwapChain() const { return m_swap_chain.get(); } BoundingBox* GetBoundingBox() const { return m_bounding_box.get(); } - void RenderText(const std::string& pstr, int left, int top, u32 color) override; u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override; u16 BBoxRead(int index) override; void BBoxWrite(int index, u16 value) override; TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; - void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override; void Flush() override; + void RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) override; + void OnConfigChanged(u32 bits) override; void ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) override; @@ -90,6 +88,8 @@ public: float far_depth) override; void Draw(u32 base_vertex, u32 num_vertices) override; void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override; + void BindBackbuffer(const ClearColor& clear_color = {}) override; + void PresentBackbuffer() override; private: bool CreateSemaphores(); @@ -99,7 +99,6 @@ private: void CheckForSurfaceChange(); void CheckForSurfaceResize(); - void CheckForConfigChanges(); void ResetSamplerStates(); @@ -112,15 +111,6 @@ private: bool CompileShaders(); void DestroyShaders(); - // Draw the frame, as well as the OSD to the swap chain. - void DrawScreen(VKTexture* xfb_texture, const EFBRectangle& xfb_region); - - // Copies/scales an image to the currently-bound framebuffer. - void BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_rect, - const TargetRectangle& src_rect, const Texture2D* src_tex); - - std::tuple UpdateUtilityUniformBuffer(const void* uniforms, u32 uniforms_size); - VkSemaphore m_image_available_semaphore = VK_NULL_HANDLE; VkSemaphore m_rendering_finished_semaphore = VK_NULL_HANDLE; VkRenderPass m_swap_chain_render_pass = VK_NULL_HANDLE; @@ -128,7 +118,6 @@ private: std::unique_ptr m_swap_chain; std::unique_ptr m_bounding_box; - std::unique_ptr m_raster_font; // Keep a copy of sampler states to avoid cache lookups every draw std::array m_sampler_states = {}; diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp index 471a8b12f5..089b385ffe 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp @@ -767,7 +767,9 @@ bool StateTracker::UpdateGXDescriptorSet() if (num_writes > 0) vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), num_writes, writes.data(), 0, nullptr); - m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS; + m_num_active_descriptor_sets = g_vulkan_context->SupportsBoundingBox() ? + NUM_GX_DRAW_DESCRIPTOR_SETS_SSBO : + NUM_GX_DRAW_DESCRIPTOR_SETS; m_num_dynamic_offsets = NUM_UBO_DESCRIPTOR_SET_BINDINGS; return true; } diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.h b/Source/Core/VideoBackends/Vulkan/StateTracker.h index a3d7a365ae..f4cbc8e180 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.h +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.h @@ -103,7 +103,8 @@ private: // Number of descriptor sets for game draws. enum { - NUM_GX_DRAW_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER + 1, + NUM_GX_DRAW_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS + 1, + NUM_GX_DRAW_DESCRIPTOR_SETS_SSBO = DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER + 1, NUM_UTILITY_DRAW_DESCRIPTOR_SETS = 2 }; @@ -192,4 +193,4 @@ private: std::vector m_scheduled_command_buffer_kicks; bool m_allow_background_execution = true; }; -} +} // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp index e9b92ba1e1..3f26b28a7c 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp @@ -45,11 +45,6 @@ VkShaderModule TextureCache::GetCopyShader() const return m_copy_shader; } -StreamBuffer* TextureCache::GetTextureUploadBuffer() const -{ - return m_texture_upload_buffer.get(); -} - TextureCache* TextureCache::GetInstance() { return static_cast(g_texture_cache.get()); @@ -57,15 +52,6 @@ TextureCache* TextureCache::GetInstance() bool TextureCache::Initialize() { - m_texture_upload_buffer = - StreamBuffer::Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, INITIAL_TEXTURE_UPLOAD_BUFFER_SIZE, - MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE); - if (!m_texture_upload_buffer) - { - PanicAlert("Failed to create texture upload buffer"); - return false; - } - m_texture_converter = std::make_unique(); if (!m_texture_converter->Initialize()) { diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.h b/Source/Core/VideoBackends/Vulkan/TextureCache.h index fd6b57981a..35b3b6c360 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.h +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.h @@ -49,7 +49,6 @@ public: TLUTFormat palette_format) override; VkShaderModule GetCopyShader() const; - StreamBuffer* GetTextureUploadBuffer() const; private: void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect, @@ -57,8 +56,6 @@ private: float gamma, bool clamp_top, bool clamp_bottom, const CopyFilterCoefficientArray& filter_coefficients) override; - std::unique_ptr m_texture_upload_buffer; - std::unique_ptr m_texture_converter; VkShaderModule m_copy_shader = VK_NULL_HANDLE; diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index 304c5cc908..929307edb3 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -290,7 +290,7 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* if (upload_size <= STAGING_TEXTURE_UPLOAD_THRESHOLD && upload_size <= MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE) { - StreamBuffer* stream_buffer = TextureCache::GetInstance()->GetTextureUploadBuffer(); + StreamBuffer* stream_buffer = g_object_cache->GetTextureUploadBuffer(); if (!stream_buffer->ReserveMemory(upload_size, upload_alignment)) { // Execute the command buffer first. diff --git a/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj b/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj index 5f1311ec9a..5b465ff733 100644 --- a/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj +++ b/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj @@ -44,7 +44,6 @@ - @@ -71,7 +70,6 @@ - diff --git a/Source/Core/VideoBackends/Vulkan/main.cpp b/Source/Core/VideoBackends/Vulkan/main.cpp index 63d61c9d01..53c04c4e37 100644 --- a/Source/Core/VideoBackends/Vulkan/main.cpp +++ b/Source/Core/VideoBackends/Vulkan/main.cpp @@ -225,7 +225,7 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) // Create main wrapper instances. g_framebuffer_manager = std::make_unique(); - g_renderer = std::make_unique(std::move(swap_chain)); + g_renderer = std::make_unique(std::move(swap_chain), wsi.render_surface_scale); g_vertex_manager = std::make_unique(); g_texture_cache = std::make_unique(); ::g_shader_cache = std::make_unique(); @@ -303,6 +303,18 @@ void VideoBackend::PrepareWindow(const WindowSystemInfo& wsi) // [view setLayer:layer] reinterpret_cast(objc_msgSend)(view, sel_getUid("setLayer:"), layer); + + // NSScreen* screen = [NSScreen mainScreen] + id screen = reinterpret_cast(objc_msgSend)(objc_getClass("NSScreen"), + sel_getUid("mainScreen")); + + // CGFloat factor = [screen backingScaleFactor] + double factor = + reinterpret_cast(objc_msgSend)(screen, sel_getUid("backingScaleFactor")); + + // layer.contentsScale = factor + reinterpret_cast(objc_msgSend)(layer, sel_getUid("setContentsScale:"), + factor); #endif } } // namespace Vulkan diff --git a/Source/Core/VideoCommon/AsyncShaderCompiler.cpp b/Source/Core/VideoCommon/AsyncShaderCompiler.cpp index 755f105942..942f293f1b 100644 --- a/Source/Core/VideoCommon/AsyncShaderCompiler.cpp +++ b/Source/Core/VideoCommon/AsyncShaderCompiler.cpp @@ -77,7 +77,7 @@ void AsyncShaderCompiler::WaitUntilCompletion( // Wait a second before opening a progress dialog. // This way, if the operation completes quickly, we don't annoy the user. - constexpr u32 CHECK_INTERVAL_MS = 50; + constexpr u32 CHECK_INTERVAL_MS = 1000 / 30; constexpr auto CHECK_INTERVAL = std::chrono::milliseconds(CHECK_INTERVAL_MS); for (u32 i = 0; i < (1000 / CHECK_INTERVAL_MS); i++) { diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 5cf64ec74f..b29492be50 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -65,6 +65,7 @@ PUBLIC PRIVATE png xxhash + imgui ) if(_M_X86) diff --git a/Source/Core/VideoCommon/OnScreenDisplay.cpp b/Source/Core/VideoCommon/OnScreenDisplay.cpp index 62997cd150..d6f9b38a3e 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.cpp +++ b/Source/Core/VideoCommon/OnScreenDisplay.cpp @@ -5,22 +5,79 @@ #include #include #include +#include #include +#include "imgui.h" + #include "Common/CommonTypes.h" #include "Common/Timer.h" #include "Core/ConfigManager.h" #include "VideoCommon/OnScreenDisplay.h" -#include "VideoCommon/RenderBase.h" namespace OSD { -static std::multimap s_callbacks; +constexpr float LEFT_MARGIN = 10.0f; // Pixels to the left of OSD messages. +constexpr float TOP_MARGIN = 10.0f; // Pixels above the first OSD message. +constexpr float WINDOW_PADDING = 4.0f; // Pixels between subsequent OSD messages. + +struct Message +{ + Message() {} + Message(const std::string& text_, u32 timestamp_, u32 color_) + : text(text_), timestamp(timestamp_), color(color_) + { + } + std::string text; + u32 timestamp; + u32 color; +}; static std::multimap s_messages; static std::mutex s_messages_mutex; +static ImVec4 RGBAToImVec4(const u32 rgba) +{ + return ImVec4(static_cast((rgba >> 16) & 0xFF) / 255.0f, + static_cast((rgba >> 8) & 0xFF) / 255.0f, + static_cast((rgba >> 0) & 0xFF) / 255.0f, + static_cast((rgba >> 24) & 0xFF) / 255.0f); +} + +static float DrawMessage(int index, const Message& msg, const ImVec2& position, int time_left) +{ + // We have to provide a window name, and these shouldn't be duplicated. + // So instead, we generate a name based on the number of messages drawn. + const std::string window_name = StringFromFormat("osd_%d", index); + + // The size must be reset, otherwise the length of old messages could influence new ones. + ImGui::SetNextWindowPos(position); + ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f)); + + // Gradually fade old messages away. + const float alpha = std::min(1.0f, std::max(0.0f, time_left / 1024.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha); + + float window_height = 0.0f; + if (ImGui::Begin(window_name.c_str(), nullptr, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav | + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing)) + { + // Use %s in case message contains %. + ImGui::TextColored(RGBAToImVec4(msg.color), "%s", msg.text.c_str()); + window_height = + ImGui::GetWindowSize().y + (WINDOW_PADDING * ImGui::GetIO().DisplayFramebufferScale.y); + } + + ImGui::End(); + ImGui::PopStyleVar(); + + return window_height; +} + void AddTypedMessage(MessageType type, const std::string& message, u32 ms, u32 rgba) { std::lock_guard lock(s_messages_mutex); @@ -35,14 +92,6 @@ void AddMessage(const std::string& message, u32 ms, u32 rgba) Message(message, Common::Timer::GetTimeMs() + ms, rgba)); } -void DrawMessage(const Message& msg, int top, int left, int time_left) -{ - float alpha = std::min(1.0f, std::max(0.0f, time_left / 1024.0f)); - u32 color = (msg.m_rgba & 0xFFFFFF) | ((u32)((msg.m_rgba >> 24) * alpha) << 24); - - g_renderer->RenderText(msg.m_str, left, top, color); -} - void DrawMessages() { if (!SConfig::GetInstance().bOnScreenDisplayMessages) @@ -51,21 +100,22 @@ void DrawMessages() { std::lock_guard lock(s_messages_mutex); - u32 now = Common::Timer::GetTimeMs(); - int left = 20, top = 35; + const u32 now = Common::Timer::GetTimeMs(); + float current_x = LEFT_MARGIN * ImGui::GetIO().DisplayFramebufferScale.x; + float current_y = TOP_MARGIN * ImGui::GetIO().DisplayFramebufferScale.y; + int index = 0; auto it = s_messages.begin(); while (it != s_messages.end()) { const Message& msg = it->second; - int time_left = (int)(msg.m_timestamp - now); - DrawMessage(msg, top, left, time_left); + const int time_left = static_cast(msg.timestamp - now); + current_y += DrawMessage(index++, msg, ImVec2(current_x, current_y), time_left); if (time_left <= 0) it = s_messages.erase(it); else ++it; - top += 15; } } } @@ -75,24 +125,4 @@ void ClearMessages() std::lock_guard lock(s_messages_mutex); s_messages.clear(); } - -// On-Screen Display Callbacks -void AddCallback(CallbackType type, Callback cb) -{ - s_callbacks.emplace(type, cb); } - -void DoCallbacks(CallbackType type) -{ - auto it_bounds = s_callbacks.equal_range(type); - for (auto it = it_bounds.first; it != it_bounds.second; ++it) - { - it->second(); - } - - // Wipe all callbacks on shutdown - if (type == CallbackType::Shutdown) - s_callbacks.clear(); -} - -} // namespace diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index a05f4058ef..51358aff19 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -11,15 +11,6 @@ namespace OSD { -struct Message -{ - Message() {} - Message(const std::string& s, u32 ts, u32 rgba) : m_str(s), m_timestamp(ts), m_rgba(rgba) {} - std::string m_str; - u32 m_timestamp; - u32 m_rgba; -}; - enum class MessageType { NetPlayPing, @@ -49,20 +40,7 @@ constexpr u32 VERY_LONG = 10000; void AddMessage(const std::string& message, u32 ms = Duration::SHORT, u32 rgba = Color::YELLOW); void AddTypedMessage(MessageType type, const std::string& message, u32 ms = Duration::SHORT, u32 rgba = Color::YELLOW); -void DrawMessage(const Message& msg, int top, int left, int time_left); // draw one message void DrawMessages(); // draw the current messages on the screen. Only call once // per frame. void ClearMessages(); - -// On-screen callbacks -enum class CallbackType -{ - Initialization, - OnFrame, - Shutdown -}; -using Callback = std::function; - -void AddCallback(CallbackType type, Callback cb); -void DoCallbacks(CallbackType type); } // namespace OSD diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 3c8c5d5a82..aef425a726 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -21,6 +21,8 @@ #include #include +#include "imgui.h" + #include "Common/Assert.h" #include "Common/CommonTypes.h" #include "Common/Config/Config.h" @@ -63,6 +65,7 @@ #include "VideoCommon/Statistics.h" #include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/TextureDecoder.h" +#include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VideoConfig.h" @@ -78,26 +81,23 @@ static float AspectToWidescreen(float aspect) return aspect * ((16.0f / 9.0f) / (4.0f / 3.0f)); } -Renderer::Renderer(int backbuffer_width, int backbuffer_height, +Renderer::Renderer(int backbuffer_width, int backbuffer_height, float backbuffer_scale, AbstractTextureFormat backbuffer_format) : m_backbuffer_width(backbuffer_width), m_backbuffer_height(backbuffer_height), - m_backbuffer_format(backbuffer_format) + m_backbuffer_scale(backbuffer_scale), m_backbuffer_format(backbuffer_format) { UpdateActiveConfig(); UpdateDrawRectangle(); CalculateTargetSize(); m_aspect_wide = SConfig::GetInstance().bWii && Config::Get(Config::SYSCONF_WIDESCREEN); - - m_last_host_config_bits = ShaderHostConfig::GetCurrent().bits; - m_last_efb_multisamples = g_ActiveConfig.iMultisamples; } Renderer::~Renderer() = default; bool Renderer::Initialize() { - return true; + return InitializeImGui(); } void Renderer::Shutdown() @@ -105,6 +105,7 @@ void Renderer::Shutdown() // First stop any framedumping, which might need to dump the last xfb frame. This process // can require additional graphics sub-systems so it needs to be done first ShutdownFrameDumping(); + ShutdownImGui(); } void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStride, u32 fbHeight, @@ -235,162 +236,119 @@ void Renderer::SaveScreenshot(const std::string& filename, bool wait_for_complet } } -bool Renderer::CheckForHostConfigChanges() +void Renderer::CheckForConfigChanges() { + const ShaderHostConfig old_shader_host_config = ShaderHostConfig::GetCurrent(); + const StereoMode old_stereo = g_ActiveConfig.stereo_mode; + const u32 old_multisamples = g_ActiveConfig.iMultisamples; + const int old_anisotropy = g_ActiveConfig.iMaxAnisotropy; + const bool old_force_filtering = g_ActiveConfig.bForceFiltering; + const bool old_vsync = g_ActiveConfig.IsVSync(); + const bool old_bbox = g_ActiveConfig.bBBoxEnable; + + UpdateActiveConfig(); + + // Update texture cache settings with any changed options. + g_texture_cache->OnConfigChanged(g_ActiveConfig); + + // Determine which (if any) settings have changed. ShaderHostConfig new_host_config = ShaderHostConfig::GetCurrent(); - if (new_host_config.bits == m_last_host_config_bits && - m_last_efb_multisamples == g_ActiveConfig.iMultisamples) + u32 changed_bits = 0; + if (old_shader_host_config.bits != new_host_config.bits) + changed_bits |= CONFIG_CHANGE_BIT_HOST_CONFIG; + if (old_stereo != g_ActiveConfig.stereo_mode) + changed_bits |= CONFIG_CHANGE_BIT_STEREO_MODE; + if (old_multisamples != g_ActiveConfig.iMultisamples) + changed_bits |= CONFIG_CHANGE_BIT_MULTISAMPLES; + if (old_anisotropy != g_ActiveConfig.iMaxAnisotropy) + changed_bits |= CONFIG_CHANGE_BIT_ANISOTROPY; + if (old_force_filtering != g_ActiveConfig.bForceFiltering) + changed_bits |= CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING; + if (old_vsync != g_ActiveConfig.IsVSync()) + changed_bits |= CONFIG_CHANGE_BIT_VSYNC; + if (old_bbox != g_ActiveConfig.bBBoxEnable) + changed_bits |= CONFIG_CHANGE_BIT_BBOX; + if (CalculateTargetSize()) + changed_bits |= CONFIG_CHANGE_BIT_TARGET_SIZE; + + // No changes? + if (changed_bits == 0) + return; + + // Notify the backend of the changes, if any. + OnConfigChanged(changed_bits); + + // Reload shaders if host config has changed. + if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES)) { - return false; + OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL); + SetPipeline(nullptr); + g_vertex_manager->InvalidatePipelineObject(); + g_shader_cache->SetHostConfig(new_host_config, g_ActiveConfig.iMultisamples); } - - m_last_host_config_bits = new_host_config.bits; - m_last_efb_multisamples = g_ActiveConfig.iMultisamples; - - // Reload shaders. - OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL); - SetPipeline(nullptr); - g_vertex_manager->InvalidatePipelineObject(); - g_shader_cache->SetHostConfig(new_host_config, g_ActiveConfig.iMultisamples); - return true; } // Create On-Screen-Messages void Renderer::DrawDebugText() { - std::string final_yellow, final_cyan; + const auto& config = SConfig::GetInstance(); - if (g_ActiveConfig.bShowFPS || SConfig::GetInstance().m_ShowFrameCount) + if (g_ActiveConfig.bShowFPS) { - if (g_ActiveConfig.bShowFPS) - final_cyan += StringFromFormat("FPS: %.2f", m_fps_counter.GetFPS()); + // Position in the top-right corner of the screen. + ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x - (10.0f * m_backbuffer_scale), + 10.0f * m_backbuffer_scale), + ImGuiCond_Always, ImVec2(1.0f, 0.0f)); + ImGui::SetNextWindowSize(ImVec2(100.0f * m_backbuffer_scale, 30.0f * m_backbuffer_scale)); - if (g_ActiveConfig.bShowFPS && SConfig::GetInstance().m_ShowFrameCount) - final_cyan += " - "; - if (SConfig::GetInstance().m_ShowFrameCount) + if (ImGui::Begin("FPS", nullptr, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav | + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing)) { - final_cyan += StringFromFormat("Frame: %" PRIu64, Movie::GetCurrentFrame()); + ImGui::TextColored(ImVec4(0.0f, 1.0f, 1.0f, 1.0f), "FPS: %.2f", m_fps_counter.GetFPS()); + } + ImGui::End(); + } + + const bool show_movie_window = + config.m_ShowFrameCount | config.m_ShowLag | config.m_ShowInputDisplay | config.m_ShowRTC; + if (show_movie_window) + { + // Position under the FPS display. + ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x - (10.0f * m_backbuffer_scale), + 50.0f * m_backbuffer_scale), + ImGuiCond_FirstUseEver, ImVec2(1.0f, 0.0f)); + ImGui::SetNextWindowSizeConstraints( + ImVec2(150.0f * m_backbuffer_scale, 20.0f * m_backbuffer_scale), + ImGui::GetIO().DisplaySize); + if (ImGui::Begin("Movie", nullptr, ImGuiWindowFlags_NoFocusOnAppearing)) + { + if (config.m_ShowFrameCount) + { + ImGui::Text("Frame: %" PRIu64, Movie::GetCurrentFrame()); + } if (Movie::IsPlayingInput()) - final_cyan += StringFromFormat("\nInput: %" PRIu64 " / %" PRIu64, - Movie::GetCurrentInputCount(), Movie::GetTotalInputCount()); + { + ImGui::Text("Input: %" PRIu64 " / %" PRIu64, Movie::GetCurrentInputCount(), + Movie::GetTotalInputCount()); + } + if (SConfig::GetInstance().m_ShowLag) + ImGui::Text("Lag: %" PRIu64 "\n", Movie::GetCurrentLagCount()); + if (SConfig::GetInstance().m_ShowInputDisplay) + ImGui::TextUnformatted(Movie::GetInputDisplay().c_str()); + if (SConfig::GetInstance().m_ShowRTC) + ImGui::TextUnformatted(Movie::GetRTCDisplay().c_str()); } - - final_cyan += "\n"; - final_yellow += "\n"; + ImGui::End(); } - if (SConfig::GetInstance().m_ShowLag) - { - final_cyan += StringFromFormat("Lag: %" PRIu64 "\n", Movie::GetCurrentLagCount()); - final_yellow += "\n"; - } - - if (SConfig::GetInstance().m_ShowInputDisplay) - { - final_cyan += Movie::GetInputDisplay(); - final_yellow += "\n"; - } - - if (SConfig::GetInstance().m_ShowRTC) - { - final_cyan += Movie::GetRTCDisplay(); - final_yellow += "\n"; - } - - // OSD Menu messages - if (m_osd_message > 0) - { - m_osd_time = Common::Timer::GetTimeMs() + 3000; - m_osd_message = -m_osd_message; - } - - if (static_cast(m_osd_time) > Common::Timer::GetTimeMs()) - { - std::string res_text; - switch (g_ActiveConfig.iEFBScale) - { - case EFB_SCALE_AUTO_INTEGRAL: - res_text = "Auto (integral)"; - break; - case 1: - res_text = "Native"; - break; - default: - res_text = StringFromFormat("%dx", g_ActiveConfig.iEFBScale); - break; - } - const char* ar_text = ""; - switch (g_ActiveConfig.aspect_mode) - { - case AspectMode::Stretch: - ar_text = "Stretch"; - break; - case AspectMode::Analog: - ar_text = "Force 4:3"; - break; - case AspectMode::AnalogWide: - ar_text = "Force 16:9"; - break; - case AspectMode::Auto: - default: - ar_text = "Auto"; - break; - } - const std::string audio_text = SConfig::GetInstance().m_IsMuted ? - "Muted" : - std::to_string(SConfig::GetInstance().m_Volume) + "%"; - - const char* const efbcopy_text = g_ActiveConfig.bSkipEFBCopyToRam ? "to Texture" : "to RAM"; - const char* const xfbcopy_text = g_ActiveConfig.bSkipXFBCopyToRam ? "to Texture" : "to RAM"; - - // The rows - const std::string lines[] = { - std::string("Internal Resolution: ") + res_text, - std::string("Aspect Ratio: ") + ar_text + (g_ActiveConfig.bCrop ? " (crop)" : ""), - std::string("Copy EFB: ") + efbcopy_text, - std::string("Fog: ") + (g_ActiveConfig.bDisableFog ? "Disabled" : "Enabled"), - SConfig::GetInstance().m_EmulationSpeed <= 0 ? - "Speed Limit: Unlimited" : - StringFromFormat("Speed Limit: %li%%", - std::lround(SConfig::GetInstance().m_EmulationSpeed * 100.f)), - std::string("Copy XFB: ") + xfbcopy_text + - (g_ActiveConfig.bImmediateXFB ? " (Immediate)" : ""), - "Volume: " + audio_text, - }; - - enum - { - lines_count = sizeof(lines) / sizeof(*lines) - }; - - // The latest changed setting in yellow - for (int i = 0; i != lines_count; ++i) - { - if (m_osd_message == -i - 1) - final_yellow += lines[i]; - final_yellow += '\n'; - } - - // The other settings in cyan - for (int i = 0; i != lines_count; ++i) - { - if (m_osd_message != -i - 1) - final_cyan += lines[i]; - final_cyan += '\n'; - } - } - - final_cyan += Common::Profiler::ToString(); - if (g_ActiveConfig.bOverlayStats) - final_cyan += Statistics::ToString(); + Statistics::Display(); if (g_ActiveConfig.bOverlayProjStats) - final_cyan += Statistics::ToStringProj(); - - // and then the text - RenderText(final_cyan, 20, 20, 0xFF00FFFF); - RenderText(final_yellow, 20, 20, 0xFFFFFF00); + Statistics::DisplayProj(); } float Renderer::CalculateDrawAspectRatio() const @@ -638,6 +596,289 @@ void Renderer::RecordVideoMemory() texMem); } +static std::string GenerateImGuiVertexShader() +{ + const APIType api_type = g_ActiveConfig.backend_info.api_type; + std::stringstream ss; + + // Uniform buffer contains the viewport size, and we transform in the vertex shader. + if (api_type == APIType::D3D) + ss << "cbuffer PSBlock : register(b0) {\n"; + else if (api_type == APIType::OpenGL) + ss << "UBO_BINDING(std140, 1) uniform PSBlock {\n"; + else if (api_type == APIType::Vulkan) + ss << "UBO_BINDING(std140, 1) uniform PSBlock {\n"; + ss << "float2 u_rcp_viewport_size_mul2;\n"; + ss << "};\n"; + + if (api_type == APIType::D3D) + { + ss << "void main(in float2 rawpos : POSITION,\n" + << " in float2 rawtex0 : TEXCOORD,\n" + << " in float4 rawcolor0 : COLOR,\n" + << " out float2 frag_uv : TEXCOORD,\n" + << " out float4 frag_color : COLOR,\n" + << " out float4 out_pos : SV_Position)\n"; + } + else + { + ss << "ATTRIBUTE_LOCATION(" << SHADER_POSITION_ATTRIB << ") in float2 rawpos;\n" + << "ATTRIBUTE_LOCATION(" << SHADER_TEXTURE0_ATTRIB << ") in float2 rawtex0;\n" + << "ATTRIBUTE_LOCATION(" << SHADER_COLOR0_ATTRIB << ") in float4 rawcolor0;\n" + << "VARYING_LOCATION(0) out float2 frag_uv;\n" + << "VARYING_LOCATION(1) out float4 frag_color;\n" + << "void main()\n"; + } + + ss << "{\n" + << " frag_uv = rawtex0;\n" + << " frag_color = rawcolor0;\n"; + + ss << " " << (api_type == APIType::D3D ? "out_pos" : "gl_Position") + << "= float4(rawpos.x * u_rcp_viewport_size_mul2.x - 1.0, 1.0 - rawpos.y * " + "u_rcp_viewport_size_mul2.y, 0.0, 1.0);\n"; + + // Clip-space is flipped in Vulkan + if (api_type == APIType::Vulkan) + ss << " gl_Position.y = -gl_Position.y;\n"; + + ss << "}\n"; + return ss.str(); +} + +static std::string GenerateImGuiPixelShader() +{ + const APIType api_type = g_ActiveConfig.backend_info.api_type; + + std::stringstream ss; + if (api_type == APIType::D3D) + { + ss << "Texture2DArray tex0 : register(t0);\n" + << "SamplerState samp0 : register(s0);\n" + << "void main(in float2 frag_uv : TEXCOORD,\n" + << " in float4 frag_color : COLOR,\n" + << " out float4 ocol0 : SV_Target)\n"; + } + else + { + ss << "SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n" + << "VARYING_LOCATION(0) in float2 frag_uv; \n" + << "VARYING_LOCATION(1) in float4 frag_color;\n" + << "FRAGMENT_OUTPUT_LOCATION(0) out float4 ocol0;\n" + << "void main()\n"; + } + + ss << "{\n"; + + if (api_type == APIType::D3D) + ss << " ocol0 = tex0.Sample(samp0, float3(frag_uv, 0.0)) * frag_color;\n"; + else + ss << " ocol0 = texture(samp0, float3(frag_uv, 0.0)) * frag_color;\n"; + + ss << "}\n"; + + return ss.str(); +} + +bool Renderer::InitializeImGui() +{ + if (!ImGui::CreateContext()) + { + PanicAlert("Creating ImGui context failed"); + return false; + } + + // Don't create an ini file. TODO: Do we want this in the future? + ImGui::GetIO().IniFilename = nullptr; + ImGui::GetIO().DisplayFramebufferScale.x = m_backbuffer_scale; + ImGui::GetIO().DisplayFramebufferScale.y = m_backbuffer_scale; + ImGui::GetIO().FontGlobalScale = m_backbuffer_scale; + ImGui::GetStyle().ScaleAllSizes(m_backbuffer_scale); + + PortableVertexDeclaration vdecl = {}; + vdecl.position = {VAR_FLOAT, 2, offsetof(ImDrawVert, pos), true, false}; + vdecl.texcoords[0] = {VAR_FLOAT, 2, offsetof(ImDrawVert, uv), true, false}; + vdecl.colors[0] = {VAR_UNSIGNED_BYTE, 4, offsetof(ImDrawVert, col), true, false}; + vdecl.stride = sizeof(ImDrawVert); + m_imgui_vertex_format = g_vertex_manager->CreateNativeVertexFormat(vdecl); + if (!m_imgui_vertex_format) + { + PanicAlert("Failed to create imgui vertex format"); + return false; + } + + const std::string vertex_shader_source = GenerateImGuiVertexShader(); + const std::string pixel_shader_source = GenerateImGuiPixelShader(); + std::unique_ptr vertex_shader = CreateShaderFromSource( + ShaderStage::Vertex, vertex_shader_source.c_str(), vertex_shader_source.size()); + std::unique_ptr pixel_shader = CreateShaderFromSource( + ShaderStage::Pixel, pixel_shader_source.c_str(), pixel_shader_source.size()); + if (!vertex_shader || !pixel_shader) + { + PanicAlert("Failed to compile imgui shaders"); + return false; + } + + AbstractPipelineConfig pconfig = {}; + pconfig.vertex_format = m_imgui_vertex_format.get(); + pconfig.vertex_shader = vertex_shader.get(); + pconfig.pixel_shader = pixel_shader.get(); + pconfig.rasterization_state.hex = RenderState::GetNoCullRasterizationState().hex; + pconfig.rasterization_state.primitive = PrimitiveType::Triangles; + pconfig.depth_state.hex = RenderState::GetNoDepthTestingDepthStencilState().hex; + pconfig.blending_state.hex = RenderState::GetNoBlendingBlendState().hex; + pconfig.blending_state.blendenable = true; + pconfig.blending_state.srcfactor = BlendMode::SRCALPHA; + pconfig.blending_state.dstfactor = BlendMode::INVSRCALPHA; + pconfig.blending_state.srcfactoralpha = BlendMode::ZERO; + pconfig.blending_state.dstfactoralpha = BlendMode::ONE; + pconfig.framebuffer_state.color_texture_format = m_backbuffer_format; + pconfig.framebuffer_state.depth_texture_format = AbstractTextureFormat::Undefined; + pconfig.framebuffer_state.samples = 1; + pconfig.framebuffer_state.per_sample_shading = false; + pconfig.usage = AbstractPipelineUsage::Utility; + m_imgui_pipeline = g_renderer->CreatePipeline(pconfig); + if (!m_imgui_pipeline) + { + PanicAlert("Failed to create imgui pipeline"); + return false; + } + + // Font texture(s). + { + ImGuiIO& io = ImGui::GetIO(); + u8* font_tex_pixels; + int font_tex_width, font_tex_height; + io.Fonts->GetTexDataAsRGBA32(&font_tex_pixels, &font_tex_width, &font_tex_height); + + TextureConfig font_tex_config(font_tex_width, font_tex_height, 1, 1, 1, + AbstractTextureFormat::RGBA8, false); + std::unique_ptr font_tex = CreateTexture(font_tex_config); + if (!font_tex) + { + PanicAlert("Failed to create imgui texture"); + return false; + } + font_tex->Load(0, font_tex_width, font_tex_height, font_tex_width, font_tex_pixels, + sizeof(u32) * font_tex_width * font_tex_height); + + io.Fonts->TexID = font_tex.get(); + + m_imgui_textures.push_back(std::move(font_tex)); + } + + m_imgui_last_frame_time = Common::Timer::GetTimeUs(); + BeginImGuiFrame(); + return true; +} + +void Renderer::ShutdownImGui() +{ + ImGui::EndFrame(); + ImGui::DestroyContext(); + m_imgui_pipeline.reset(); + m_imgui_vertex_format.reset(); + m_imgui_textures.clear(); +} + +void Renderer::BeginImGuiFrame() +{ + std::unique_lock imgui_lock(m_imgui_mutex); + + const u64 current_time_us = Common::Timer::GetTimeUs(); + const u64 time_diff_us = current_time_us - m_imgui_last_frame_time; + const float time_diff_secs = static_cast(time_diff_us / 1000000.0); + m_imgui_last_frame_time = current_time_us; + + // Update I/O with window dimensions. + ImGuiIO& io = ImGui::GetIO(); + io.DisplaySize = + ImVec2(static_cast(m_backbuffer_width), static_cast(m_backbuffer_height)); + io.DeltaTime = time_diff_secs; + + ImGui::NewFrame(); +} + +void Renderer::RenderImGui() +{ + ImGui::Render(); + + ImDrawData* draw_data = ImGui::GetDrawData(); + if (!draw_data) + return; + + SetViewport(0.0f, 0.0f, static_cast(m_backbuffer_width), + static_cast(m_backbuffer_height), 0.0f, 1.0f); + + // Uniform buffer for draws. + struct ImGuiUbo + { + float u_rcp_viewport_size_mul2[2]; + float padding[2]; + }; + ImGuiUbo ubo = {{1.0f / m_backbuffer_width * 2.0f, 1.0f / m_backbuffer_height * 2.0f}}; + + // Set up common state for drawing. + SetPipeline(m_imgui_pipeline.get()); + SetSamplerState(0, RenderState::GetPointSamplerState()); + g_vertex_manager->UploadUtilityUniforms(&ubo, sizeof(ubo)); + + for (int i = 0; i < draw_data->CmdListsCount; i++) + { + const ImDrawList* cmdlist = draw_data->CmdLists[i]; + if (cmdlist->VtxBuffer.empty() || cmdlist->IdxBuffer.empty()) + return; + + u32 base_vertex, base_index; + g_vertex_manager->UploadUtilityVertices(cmdlist->VtxBuffer.Data, sizeof(ImDrawVert), + cmdlist->VtxBuffer.Size, cmdlist->IdxBuffer.Data, + cmdlist->IdxBuffer.Size, &base_vertex, &base_index); + + for (const ImDrawCmd& cmd : cmdlist->CmdBuffer) + { + if (cmd.UserCallback) + { + cmd.UserCallback(cmdlist, &cmd); + continue; + } + + SetScissorRect(MathUtil::Rectangle( + static_cast(cmd.ClipRect.x), static_cast(cmd.ClipRect.y), + static_cast(cmd.ClipRect.z), static_cast(cmd.ClipRect.w))); + SetTexture(0, reinterpret_cast(cmd.TextureId)); + DrawIndexed(base_index, cmd.ElemCount, base_vertex); + base_index += cmd.ElemCount; + } + } +} + +std::unique_lock Renderer::GetImGuiLock() +{ + return std::unique_lock(m_imgui_mutex); +} + +void Renderer::BeginUIFrame() +{ + ResetAPIState(); + BindBackbuffer({0.0f, 0.0f, 0.0f, 1.0f}); +} + +void Renderer::EndUIFrame() +{ + { + auto lock = GetImGuiLock(); + RenderImGui(); + } + + { + std::lock_guard guard(m_swap_mutex); + PresentBackbuffer(); + } + + BeginImGuiFrame(); + RestoreAPIState(); +} + void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc, u64 ticks) { @@ -705,10 +946,27 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const // with the loader, and it has not been unmapped yet. Force a pipeline flush to avoid this. g_vertex_manager->Flush(); - // TODO: merge more generic parts into VideoCommon + // Render the XFB to the screen. + ResetAPIState(); + BindBackbuffer({0.0f, 0.0f, 0.0f, 1.0f}); + UpdateDrawRectangle(); + RenderXFBToScreen(xfb_entry->texture.get(), xfb_rect); + + // Hold the imgui lock while we're presenting. + // It's only to prevent races on inputs anyway, at this point. + { + auto lock = GetImGuiLock(); + + DrawDebugText(); + OSD::DrawMessages(); + + RenderImGui(); + } + + // Present to the window system. { std::lock_guard guard(m_swap_mutex); - g_renderer->SwapImpl(xfb_entry->texture.get(), xfb_rect, ticks); + PresentBackbuffer(); } // Update the window size based on the frame that was just rendered. @@ -730,10 +988,9 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const GFX_DEBUGGER_PAUSE_AT(NEXT_FRAME, true); // Begin new frame - // Set default viewport and scissor, for the clear to work correctly - // New frame stats.ResetFrame(); g_shader_cache->RetrieveAsyncShaders(); + BeginImGuiFrame(); // We invalidate the pipeline object at the start of the frame. // This is for the rare case where only a single pipeline configuration is used, @@ -745,6 +1002,15 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const // rate and not waiting for vblank. Otherwise, we'd end up with a huge list of pending copies. g_texture_cache->FlushEFBCopies(); + // Remove stale EFB/XFB copies. + g_texture_cache->Cleanup(frameCount); + + // Handle any config changes, this gets propogated to the backend. + CheckForConfigChanges(); + g_Config.iSaveTargetId = 0; + + RestoreAPIState(); + Core::Callback_VideoCopiedToXFB(true); } else @@ -1078,8 +1344,3 @@ std::unique_ptr Renderer::CreateAsyncShaderCom { return std::make_unique(); } - -void Renderer::ShowOSDMessage(OSDMessage message) -{ - m_osd_message = static_cast(message); -} diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 4e9c60efae..45a82c1968 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -40,6 +40,7 @@ class AbstractPipeline; class AbstractShader; class AbstractTexture; class AbstractStagingTexture; +class NativeVertexFormat; class PostProcessingShaderImplementation; struct TextureConfig; struct ComputePipelineConfig; @@ -56,24 +57,14 @@ struct EfbPokeData extern int frameCount; -enum class OSDMessage : s32 -{ - IRChanged = 1, - ARToggled = 2, - EFBCopyToggled = 3, - FogToggled = 4, - SpeedChanged = 5, - XFBChanged = 6, - VolumeChanged = 7, -}; - // Renderer really isn't a very good name for this class - it's more like "Misc". // The long term goal is to get rid of this class and replace it with others that make // more sense. class Renderer { public: - Renderer(int backbuffer_width, int backbuffer_height, AbstractTextureFormat backbuffer_format); + Renderer(int backbuffer_width, int backbuffer_height, float backbuffer_scale, + AbstractTextureFormat backbuffer_format); virtual ~Renderer(); using ClearColor = std::array; @@ -118,6 +109,14 @@ public: virtual void Draw(u32 base_vertex, u32 num_vertices) {} virtual void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) {} + // Binds the backbuffer for rendering. The buffer will be cleared immediately after binding. + // This is where any window size changes are detected, therefore m_backbuffer_width and/or + // m_backbuffer_height may change after this function returns. + virtual void BindBackbuffer(const ClearColor& clear_color = {}) {} + + // Presents the backbuffer to the window system, or "swaps buffers". + virtual void PresentBackbuffer() {} + // Shader modules/objects. virtual std::unique_ptr CreateShaderFromSource(ShaderStage stage, const char* source, size_t length) = 0; @@ -135,6 +134,7 @@ public: // Display resolution int GetBackbufferWidth() const { return m_backbuffer_width; } int GetBackbufferHeight() const { return m_backbuffer_height; } + float GetBackbufferScale() const { return m_backbuffer_scale; } void SetWindowSize(int width, int height); // EFB coordinate conversion functions @@ -166,7 +166,8 @@ public: void SaveScreenshot(const std::string& filename, bool wait_for_completion); void DrawDebugText(); - virtual void RenderText(const std::string& text, int left, int top, u32 color) = 0; + // ImGui initialization depends on being able to create textures and pipelines, so do it last. + bool InitializeImGui(); virtual void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) = 0; @@ -180,11 +181,18 @@ public: virtual u16 BBoxRead(int index) = 0; virtual void BBoxWrite(int index, u16 value) = 0; + virtual void Flush() {} + // Finish up the current frame, print some stats void Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc, u64 ticks); - virtual void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) = 0; - virtual void Flush() {} + + // Draws the specified XFB buffer to the screen, performing any post-processing. + // Assumes that the backbuffer has already been bound and cleared. + virtual void RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) {} + + // Called when the configuration changes, and backend structures need to be updated. + virtual void OnConfigChanged(u32 bits) {} PEControl::PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; } void StorePixelFormat(PEControl::PixelFormat new_format) { m_prev_efb_format = new_format; } @@ -197,17 +205,49 @@ public: virtual std::unique_ptr CreateAsyncShaderCompiler(); - void ShowOSDMessage(OSDMessage message); + // Returns a lock for the ImGui mutex, enabling data structures to be modified from outside. + // Use with care, only non-drawing functions should be called from outside the video thread, + // as the drawing is tied to a "frame". + std::unique_lock GetImGuiLock(); + + // Begins/presents a "UI frame". UI frames do not draw any of the console XFB, but this could + // change in the future. + void BeginUIFrame(); + void EndUIFrame(); protected: + // Bitmask containing information about which configuration has changed for the backend. + enum ConfigChangeBits : u32 + { + CONFIG_CHANGE_BIT_HOST_CONFIG = (1 << 0), + CONFIG_CHANGE_BIT_MULTISAMPLES = (1 << 1), + CONFIG_CHANGE_BIT_STEREO_MODE = (1 << 2), + CONFIG_CHANGE_BIT_TARGET_SIZE = (1 << 3), + CONFIG_CHANGE_BIT_ANISOTROPY = (1 << 4), + CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING = (1 << 5), + CONFIG_CHANGE_BIT_VSYNC = (1 << 6), + CONFIG_CHANGE_BIT_BBOX = (1 << 7) + }; + std::tuple CalculateTargetScale(int x, int y) const; bool CalculateTargetSize(); - bool CheckForHostConfigChanges(); + void CheckForConfigChanges(); void CheckFifoRecording(); void RecordVideoMemory(); + // Sets up ImGui state for the next frame. + // This function itself acquires the ImGui lock, so it should not be held. + void BeginImGuiFrame(); + + // Destroys all ImGui GPU resources, must do before shutdown. + void ShutdownImGui(); + + // Renders ImGui windows to the currently-bound framebuffer. + // Should be called with the ImGui lock held. + void RenderImGui(); + // TODO: Remove the width/height parameters once we make the EFB an abstract framebuffer. const AbstractFramebuffer* m_current_framebuffer = nullptr; u32 m_current_framebuffer_width = 1; @@ -226,6 +266,7 @@ protected: // Backbuffer (window) size and render area int m_backbuffer_width = 0; int m_backbuffer_height = 0; + float m_backbuffer_scale = 1.0f; AbstractTextureFormat m_backbuffer_format = AbstractTextureFormat::Undefined; TargetRectangle m_target_rectangle = {}; @@ -238,8 +279,12 @@ protected: Common::Flag m_surface_resized; std::mutex m_swap_mutex; - u32 m_last_host_config_bits = 0; - u32 m_last_efb_multisamples = 1; + // ImGui resources. + std::unique_ptr m_imgui_vertex_format; + std::vector> m_imgui_textures; + std::unique_ptr m_imgui_pipeline; + std::mutex m_imgui_mutex; + u64 m_imgui_last_frame_time; private: void RunFrameDumps(); @@ -284,9 +329,6 @@ private: u32 m_last_xfb_width = MAX_XFB_WIDTH; u32 m_last_xfb_height = MAX_XFB_HEIGHT; - s32 m_osd_message = 0; - s32 m_osd_time = 0; - // NOTE: The methods below are called on the framedumping thread. bool StartFrameDumpToAVI(const FrameDumpConfig& config); void DumpFrameToAVI(const FrameDumpConfig& config); diff --git a/Source/Core/VideoCommon/RenderState.cpp b/Source/Core/VideoCommon/RenderState.cpp index 5cad8c2584..b61b031b04 100644 --- a/Source/Core/VideoCommon/RenderState.cpp +++ b/Source/Core/VideoCommon/RenderState.cpp @@ -199,6 +199,13 @@ SamplerState& SamplerState::operator=(const SamplerState& rhs) namespace RenderState { +RasterizationState GetInvalidRasterizationState() +{ + RasterizationState state; + state.hex = UINT32_C(0xFFFFFFFF); + return state; +} + RasterizationState GetNoCullRasterizationState() { RasterizationState state = {}; @@ -206,6 +213,13 @@ RasterizationState GetNoCullRasterizationState() return state; } +DepthState GetInvalidDepthState() +{ + DepthState state; + state.hex = UINT32_C(0xFFFFFFFF); + return state; +} + DepthState GetNoDepthTestingDepthStencilState() { DepthState state = {}; @@ -215,6 +229,13 @@ DepthState GetNoDepthTestingDepthStencilState() return state; } +BlendingState GetInvalidBlendingState() +{ + BlendingState state; + state.hex = UINT32_C(0xFFFFFFFF); + return state; +} + BlendingState GetNoBlendingBlendState() { BlendingState state = {}; @@ -230,6 +251,13 @@ BlendingState GetNoBlendingBlendState() return state; } +SamplerState GetInvalidSamplerState() +{ + SamplerState state; + state.hex = UINT64_C(0xFFFFFFFFFFFFFFFF); + return state; +} + SamplerState GetPointSamplerState() { SamplerState state = {}; diff --git a/Source/Core/VideoCommon/RenderState.h b/Source/Core/VideoCommon/RenderState.h index d72c2d3984..5cd0ba7470 100644 --- a/Source/Core/VideoCommon/RenderState.h +++ b/Source/Core/VideoCommon/RenderState.h @@ -113,9 +113,13 @@ union SamplerState namespace RenderState { +RasterizationState GetInvalidRasterizationState(); RasterizationState GetNoCullRasterizationState(); +DepthState GetInvalidDepthState(); DepthState GetNoDepthTestingDepthStencilState(); +BlendingState GetInvalidBlendingState(); BlendingState GetNoBlendingBlendState(); +SamplerState GetInvalidSamplerState(); SamplerState GetPointSamplerState(); SamplerState GetLinearSamplerState(); } diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp index 299b0d1bca..3b0c110f96 100644 --- a/Source/Core/VideoCommon/ShaderCache.cpp +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -8,7 +8,6 @@ #include "Common/FileUtil.h" #include "Common/MsgHandler.h" #include "Core/ConfigManager.h" -#include "Core/Host.h" #include "VideoCommon/FramebufferManagerBase.h" #include "VideoCommon/RenderBase.h" @@ -16,6 +15,8 @@ #include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VertexManagerBase.h" +#include "imgui.h" + std::unique_ptr g_shader_cache; namespace VideoCommon @@ -153,12 +154,29 @@ void ShaderCache::WaitForAsyncCompiler() while (m_async_shader_compiler->HasPendingWork() || m_async_shader_compiler->HasCompletedWork()) { m_async_shader_compiler->WaitUntilCompletion([](size_t completed, size_t total) { - Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(), - static_cast(completed), static_cast(total)); + g_renderer->BeginUIFrame(); + + const float scale = ImGui::GetIO().DisplayFramebufferScale.x; + + ImGui::SetNextWindowSize(ImVec2(400.0f * scale, 50.0f * scale), ImGuiCond_Always); + ImGui::SetNextWindowPosCenter(ImGuiCond_Always); + if (ImGui::Begin(GetStringT("Compiling Shaders").c_str(), nullptr, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav | + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing)) + { + ImGui::Text("Compiling shaders: %zu/%zu", completed, total); + ImGui::ProgressBar(static_cast(completed) / + static_cast(std::max(total, static_cast(1))), + ImVec2(-1.0f, 0.0f), ""); + } + ImGui::End(); + + g_renderer->EndUIFrame(); }); m_async_shader_compiler->RetrieveWorkItems(); } - Host_UpdateProgressDialog("", -1, -1); } template diff --git a/Source/Core/VideoCommon/Statistics.cpp b/Source/Core/VideoCommon/Statistics.cpp index 0a4a9236b0..af122bfcdb 100644 --- a/Source/Core/VideoCommon/Statistics.cpp +++ b/Source/Core/VideoCommon/Statistics.cpp @@ -6,6 +6,8 @@ #include #include +#include "imgui.h" + #include "Common/StringUtil.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexLoaderManager.h" @@ -26,91 +28,98 @@ void Statistics::SwapDL() std::swap(stats.thisFrame.numBPLoadsInDL, stats.thisFrame.numBPLoads); } -std::string Statistics::ToString() +void Statistics::Display() { - std::string str; + const float scale = ImGui::GetIO().DisplayFramebufferScale.x; + ImGui::SetNextWindowPos(ImVec2(10.0f * scale, 10.0f * scale), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSizeConstraints(ImVec2(275.0f * scale, 400.0f * scale), + ImGui::GetIO().DisplaySize); + if (!ImGui::Begin("Statistics", nullptr, ImGuiWindowFlags_NoNavInputs)) + { + ImGui::End(); + return; + } + + ImGui::Columns(2, "Statistics", true); + +#define DRAW_STAT(name, format, ...) \ + ImGui::Text(name); \ + ImGui::NextColumn(); \ + ImGui::Text(format, __VA_ARGS__); \ + ImGui::NextColumn(); if (g_ActiveConfig.backend_info.api_type == APIType::Nothing) { - str += StringFromFormat("Objects: %i\n", stats.thisFrame.numDrawnObjects); - str += StringFromFormat("Vertices Loaded: %i\n", stats.thisFrame.numVerticesLoaded); - str += StringFromFormat("Triangles Input: %i\n", stats.thisFrame.numTrianglesIn); - str += StringFromFormat("Triangles Rejected: %i\n", stats.thisFrame.numTrianglesRejected); - str += StringFromFormat("Triangles Culled: %i\n", stats.thisFrame.numTrianglesCulled); - str += StringFromFormat("Triangles Clipped: %i\n", stats.thisFrame.numTrianglesClipped); - str += StringFromFormat("Triangles Drawn: %i\n", stats.thisFrame.numTrianglesDrawn); - str += StringFromFormat("Rasterized Pix: %i\n", stats.thisFrame.rasterizedPixels); - str += StringFromFormat("TEV Pix In: %i\n", stats.thisFrame.tevPixelsIn); - str += StringFromFormat("TEV Pix Out: %i\n", stats.thisFrame.tevPixelsOut); + DRAW_STAT("Objects", "%d", stats.thisFrame.numDrawnObjects); + DRAW_STAT("Vertices Loaded", "%d", stats.thisFrame.numVerticesLoaded); + DRAW_STAT("Triangles Input", "%d", stats.thisFrame.numTrianglesIn); + DRAW_STAT("Triangles Rejected", "%d", stats.thisFrame.numTrianglesRejected); + DRAW_STAT("Triangles Culled", "%d", stats.thisFrame.numTrianglesCulled); + DRAW_STAT("Triangles Clipped", "%d", stats.thisFrame.numTrianglesClipped); + DRAW_STAT("Triangles Drawn", "%d", stats.thisFrame.numTrianglesDrawn); + DRAW_STAT("Rasterized Pix", "%d", stats.thisFrame.rasterizedPixels); + DRAW_STAT("TEV Pix In", "%d", stats.thisFrame.tevPixelsIn); + DRAW_STAT("TEV Pix Out", "%d", stats.thisFrame.tevPixelsOut); } - str += StringFromFormat("Textures created: %i\n", stats.numTexturesCreated); - str += StringFromFormat("Textures uploaded: %i\n", stats.numTexturesUploaded); - str += StringFromFormat("Textures alive: %i\n", stats.numTexturesAlive); - str += StringFromFormat("pshaders created: %i\n", stats.numPixelShadersCreated); - str += StringFromFormat("pshaders alive: %i\n", stats.numPixelShadersAlive); - str += StringFromFormat("vshaders created: %i\n", stats.numVertexShadersCreated); - str += StringFromFormat("vshaders alive: %i\n", stats.numVertexShadersAlive); - str += StringFromFormat("shaders changes: %i\n", stats.thisFrame.numShaderChanges); - str += StringFromFormat("dlists called: %i\n", stats.thisFrame.numDListsCalled); - str += StringFromFormat("Primitive joins: %i\n", stats.thisFrame.numPrimitiveJoins); - str += StringFromFormat("Draw calls: %i\n", stats.thisFrame.numDrawCalls); - str += StringFromFormat("Primitives: %i\n", stats.thisFrame.numPrims); - str += StringFromFormat("Primitives (DL): %i\n", stats.thisFrame.numDLPrims); - str += StringFromFormat("XF loads: %i\n", stats.thisFrame.numXFLoads); - str += StringFromFormat("XF loads (DL): %i\n", stats.thisFrame.numXFLoadsInDL); - str += StringFromFormat("CP loads: %i\n", stats.thisFrame.numCPLoads); - str += StringFromFormat("CP loads (DL): %i\n", stats.thisFrame.numCPLoadsInDL); - str += StringFromFormat("BP loads: %i\n", stats.thisFrame.numBPLoads); - str += StringFromFormat("BP loads (DL): %i\n", stats.thisFrame.numBPLoadsInDL); - str += StringFromFormat("Vertex streamed: %i kB\n", stats.thisFrame.bytesVertexStreamed / 1024); - str += StringFromFormat("Index streamed: %i kB\n", stats.thisFrame.bytesIndexStreamed / 1024); - str += StringFromFormat("Uniform streamed: %i kB\n", stats.thisFrame.bytesUniformStreamed / 1024); - str += StringFromFormat("Vertex Loaders: %i\n", stats.numVertexLoaders); + DRAW_STAT("Textures created", "%d", stats.numTexturesCreated); + DRAW_STAT("Textures uploaded", "%d", stats.numTexturesUploaded); + DRAW_STAT("Textures alive", "%d", stats.numTexturesAlive); + DRAW_STAT("pshaders created", "%d", stats.numPixelShadersCreated); + DRAW_STAT("pshaders alive", "%d", stats.numPixelShadersAlive); + DRAW_STAT("vshaders created", "%d", stats.numVertexShadersCreated); + DRAW_STAT("vshaders alive", "%d", stats.numVertexShadersAlive); + DRAW_STAT("shaders changes", "%d", stats.thisFrame.numShaderChanges); + DRAW_STAT("dlists called", "%d", stats.thisFrame.numDListsCalled); + DRAW_STAT("Primitive joins", "%d", stats.thisFrame.numPrimitiveJoins); + DRAW_STAT("Draw calls", "%d", stats.thisFrame.numDrawCalls); + DRAW_STAT("Primitives", "%d", stats.thisFrame.numPrims); + DRAW_STAT("Primitives (DL)", "%d", stats.thisFrame.numDLPrims); + DRAW_STAT("XF loads", "%d", stats.thisFrame.numXFLoads); + DRAW_STAT("XF loads (DL)", "%d", stats.thisFrame.numXFLoadsInDL); + DRAW_STAT("CP loads", "%d", stats.thisFrame.numCPLoads); + DRAW_STAT("CP loads (DL)", "%d", stats.thisFrame.numCPLoadsInDL); + DRAW_STAT("BP loads", "%d", stats.thisFrame.numBPLoads); + DRAW_STAT("BP loads (DL)", "%d", stats.thisFrame.numBPLoadsInDL); + DRAW_STAT("Vertex streamed", "%i kB", stats.thisFrame.bytesVertexStreamed / 1024); + DRAW_STAT("Index streamed", "%i kB", stats.thisFrame.bytesIndexStreamed / 1024); + DRAW_STAT("Uniform streamed", "%i kB", stats.thisFrame.bytesUniformStreamed / 1024); + DRAW_STAT("Vertex Loaders", "%d", stats.numVertexLoaders); - std::string vertex_list = VertexLoaderManager::VertexLoadersToString(); +#undef DRAW_STAT - // TODO : at some point text1 just becomes too huge and overflows, we can't even read the added - // stuff - // since it gets added at the far bottom of the screen anyway (actually outside the rendering - // window) - // we should really reset the list instead of using substr - if (vertex_list.size() + str.size() > 8170) - vertex_list = vertex_list.substr(0, 8170 - str.size()); + ImGui::Columns(1); - str += vertex_list; - - return str; + ImGui::End(); } // Is this really needed? -std::string Statistics::ToStringProj() +void Statistics::DisplayProj() { - std::string projections; + if (!ImGui::Begin("Projection Statistics", nullptr, ImGuiWindowFlags_NoNavInputs)) + { + ImGui::End(); + return; + } - projections += "Projection #: X for Raw 6=0 (X for Raw 6!=0)\n\n"; - projections += StringFromFormat("Projection 0: %f (%f) Raw 0: %f\n", stats.gproj_0, - stats.g2proj_0, stats.proj_0); - projections += StringFromFormat("Projection 1: %f (%f)\n", stats.gproj_1, stats.g2proj_1); - projections += StringFromFormat("Projection 2: %f (%f) Raw 1: %f\n", stats.gproj_2, - stats.g2proj_2, stats.proj_1); - projections += StringFromFormat("Projection 3: %f (%f)\n\n", stats.gproj_3, stats.g2proj_3); - projections += StringFromFormat("Projection 4: %f (%f)\n", stats.gproj_4, stats.g2proj_4); - projections += StringFromFormat("Projection 5: %f (%f) Raw 2: %f\n", stats.gproj_5, - stats.g2proj_5, stats.proj_2); - projections += StringFromFormat("Projection 6: %f (%f) Raw 3: %f\n", stats.gproj_6, - stats.g2proj_6, stats.proj_3); - projections += StringFromFormat("Projection 7: %f (%f)\n\n", stats.gproj_7, stats.g2proj_7); - projections += StringFromFormat("Projection 8: %f (%f)\n", stats.gproj_8, stats.g2proj_8); - projections += StringFromFormat("Projection 9: %f (%f)\n", stats.gproj_9, stats.g2proj_9); - projections += StringFromFormat("Projection 10: %f (%f) Raw 4: %f\n\n", stats.gproj_10, - stats.g2proj_10, stats.proj_4); - projections += StringFromFormat("Projection 11: %f (%f) Raw 5: %f\n\n", stats.gproj_11, - stats.g2proj_11, stats.proj_5); - projections += StringFromFormat("Projection 12: %f (%f)\n", stats.gproj_12, stats.g2proj_12); - projections += StringFromFormat("Projection 13: %f (%f)\n", stats.gproj_13, stats.g2proj_13); - projections += StringFromFormat("Projection 14: %f (%f)\n", stats.gproj_14, stats.g2proj_14); - projections += StringFromFormat("Projection 15: %f (%f)\n", stats.gproj_15, stats.g2proj_15); + ImGui::Text("Projection #: X for Raw 6=0 (X for Raw 6!=0)"); + ImGui::NewLine(); + ImGui::Text("Projection 0: %f (%f) Raw 0: %f", stats.gproj_0, stats.g2proj_0, stats.proj_0); + ImGui::Text("Projection 1: %f (%f)", stats.gproj_1, stats.g2proj_1); + ImGui::Text("Projection 2: %f (%f) Raw 1: %f", stats.gproj_2, stats.g2proj_2, stats.proj_1); + ImGui::Text("Projection 3: %f (%f)", stats.gproj_3, stats.g2proj_3); + ImGui::Text("Projection 4: %f (%f)", stats.gproj_4, stats.g2proj_4); + ImGui::Text("Projection 5: %f (%f) Raw 2: %f", stats.gproj_5, stats.g2proj_5, stats.proj_2); + ImGui::Text("Projection 6: %f (%f) Raw 3: %f", stats.gproj_6, stats.g2proj_6, stats.proj_3); + ImGui::Text("Projection 7: %f (%f)", stats.gproj_7, stats.g2proj_7); + ImGui::Text("Projection 8: %f (%f)", stats.gproj_8, stats.g2proj_8); + ImGui::Text("Projection 9: %f (%f)", stats.gproj_9, stats.g2proj_9); + ImGui::Text("Projection 10: %f (%f) Raw 4: %f", stats.gproj_10, stats.g2proj_10, stats.proj_4); + ImGui::Text("Projection 11: %f (%f) Raw 5: %f", stats.gproj_11, stats.g2proj_11, stats.proj_5); + ImGui::Text("Projection 12: %f (%f)", stats.gproj_12, stats.g2proj_12); + ImGui::Text("Projection 13: %f (%f)", stats.gproj_13, stats.g2proj_13); + ImGui::Text("Projection 14: %f (%f)", stats.gproj_14, stats.g2proj_14); + ImGui::Text("Projection 15: %f (%f)", stats.gproj_15, stats.g2proj_15); - return projections; + ImGui::End(); } diff --git a/Source/Core/VideoCommon/Statistics.h b/Source/Core/VideoCommon/Statistics.h index 74ee317b82..c743bf7f08 100644 --- a/Source/Core/VideoCommon/Statistics.h +++ b/Source/Core/VideoCommon/Statistics.h @@ -65,9 +65,8 @@ struct Statistics ThisFrame thisFrame; void ResetFrame(); static void SwapDL(); - - static std::string ToString(); - static std::string ToStringProj(); + static void Display(); + static void DisplayProj(); }; extern Statistics stats; diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index fd6bbdfa2b..aa8b3d110f 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -278,9 +278,6 @@ void VideoBackendBase::InitializeShared() memset(&g_preprocess_cp_state, 0, sizeof(g_preprocess_cp_state)); memset(texMem, 0, TMEM_SIZE); - // Do our OSD callbacks - OSD::DoCallbacks(OSD::CallbackType::Initialization); - // do not initialize again for the config window m_initialized = true; @@ -303,9 +300,6 @@ void VideoBackendBase::InitializeShared() void VideoBackendBase::ShutdownShared() { - // Do our OSD callbacks - OSD::DoCallbacks(OSD::CallbackType::Shutdown); - m_initialized = false; VertexLoaderManager::Clear(); diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index 00ceae25c7..e2a8d60b1b 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -183,8 +183,11 @@ {2e6c348c-c75c-4d94-8d1e-9c1fcbf3efe4} + + {4c3b2264-ea73-4a7b-9cfe-65b0fd635ebb} + - + \ No newline at end of file