Merge pull request #7478 from stenzek/imgui

Replace raster font with dear imgui
This commit is contained in:
spycrab 2019-01-25 15:08:40 +01:00 committed by GitHub
commit 6962d5bc52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 1145 additions and 2064 deletions

View File

@ -21,7 +21,16 @@ import java.lang.ref.WeakReference;
*/
public final class NativeLibrary
{
public static WeakReference<EmulationActivity> sEmulationActivity = new WeakReference<>(null);
private static WeakReference<EmulationActivity> 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

View File

@ -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<String, UsbDevice> 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,

View File

@ -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<String, UsbDevice> devices = manager.getDeviceList();

View File

@ -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<std::string>& 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, "<init>", "()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<std::string>& paths, bool first_open,
std::optional<std::string> 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<std::string>& paths, bool first_open,
std::unique_ptr<BootParameters> 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<std::string>& paths, bool first_open,
}
Core::Shutdown();
ButtonManager::Shutdown();
UICommon::Shutdown();
guard.unlock();
@ -652,14 +697,14 @@ static void Run(const std::vector<std::string>& 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,

View File

@ -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;
};

View File

@ -143,6 +143,7 @@ PRIVATE
core
Qt5::Widgets
uicommon
imgui
)
if(WIN32)

View File

@ -463,6 +463,9 @@
<ProjectReference Include="$(CoreDir)VideoBackends\Vulkan\Vulkan.vcxproj">
<Project>{29f29a19-f141-45ad-9679-5a2923b49da3}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\Externals\imgui\imgui.vcxproj">
<Project>{4c3b2264-ea73-4a7b-9cfe-65b0fd635ebb}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@ -5,6 +5,7 @@
#include "DolphinQt/HotkeyScheduler.h"
#include <algorithm>
#include <cmath>
#include <thread>
#include <QCoreApplication>
@ -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<int>(Config::Get(Config::GFX_ASPECT_RATIO)) + 1) & 3;
Config::SetCurrent(Config::GFX_ASPECT_RATIO, static_cast<AspectMode>(aspect_ratio));
switch (static_cast<AspectMode>(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

View File

@ -164,6 +164,7 @@ static WindowSystemInfo GetWindowSystemInfo(QWindow* window)
else
wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
#endif
wsi.render_surface_scale = window ? static_cast<float>(window->devicePixelRatio()) : 1.0f;
return wsi;
}

View File

@ -17,6 +17,8 @@
#include <QScreen>
#include <QTimer>
#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<const QKeyEvent*>(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<const QMouseEvent*>(event)->x();
ImGui::GetIO().MousePos.y = static_cast<const QMouseEvent*>(event)->y();
}
break;
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
{
auto lock = g_renderer->GetImGuiLock();
const u32 button_mask = static_cast<u32>(static_cast<const QMouseEvent*>(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);
}

View File

@ -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;

View File

@ -98,390 +98,7 @@ private:
std::list<bool*> 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

View File

@ -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);
}
}

View File

@ -125,6 +125,7 @@ void FramebufferManager::BindEFBRenderTarget(bool bind_depth)
FramebufferManager::FramebufferManager(int target_width, int target_height)
{
static constexpr std::array<float, 4> clear_color = {0.0f, 0.0f, 0.0f, 1.0f};
m_target_width = static_cast<unsigned int>(std::max(target_width, 1));
m_target_height = static_cast<unsigned int>(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);

View File

@ -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<FramebufferManager>(m_target_width, m_target_height);
SetupDeviceObjects();
// Clear EFB textures
constexpr std::array<float, 4> 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<const DXTexture*>(depth_attachment));
}
void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
{
D3D::DrawTextScaled(static_cast<float>(left + 1), static_cast<float>(top + 1), 20.f, 0.0f,
color & 0xFF000000, text);
D3D::DrawTextScaled(static_cast<float>(left), static_cast<float>(top), 20.f, 0.0f, color, text);
}
std::unique_ptr<AbstractShader> 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<float, 4> 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<DXTexture*>(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<float>(m_backbuffer_width),
static_cast<float>(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<FramebufferManager>(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<float>(left_rc.left), static_cast<float>(left_rc.top),
static_cast<float>(left_rc.GetWidth()), static_cast<float>(right_rc.GetHeight()),
0.0f, 1.0f);
D3D::drawShadedTexQuad(static_cast<const DXTexture*>(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<float>(right_rc.left), static_cast<float>(right_rc.top),
static_cast<float>(right_rc.GetWidth()), static_cast<float>(right_rc.GetHeight()),
0.0f, 1.0f);
D3D::drawShadedTexQuad(static_cast<const DXTexture*>(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<float>(target_rc.left), static_cast<float>(target_rc.top),
static_cast<float>(target_rc.GetWidth()), static_cast<float>(target_rc.GetHeight()));
const CD3D11_VIEWPORT right_vp(
static_cast<float>(target_rc.left + m_backbuffer_width), static_cast<float>(target_rc.top),
static_cast<float>(target_rc.GetWidth()), static_cast<float>(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<const DXTexture*>(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<const DXTexture*>(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<float>(target_rc.left), static_cast<float>(target_rc.top),
static_cast<float>(target_rc.GetWidth()), static_cast<float>(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<const DXTexture*>(texture)->GetRawTexIdentifier()->GetSRV(),
&source_rc, texture->GetWidth(), texture->GetHeight(), pixelShader,
VertexShaderCache::GetSimpleVertexShader(),
VertexShaderCache::GetSimpleInputLayout(), geomShader);
}

View File

@ -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<ID3D11BlendState*, 4> 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;
};
}

View File

@ -147,7 +147,8 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi)
}
// internal interfaces
g_renderer = std::make_unique<Renderer>(backbuffer_width, backbuffer_height);
g_renderer =
std::make_unique<Renderer>(backbuffer_width, backbuffer_height, wsi.render_surface_scale);
g_shader_cache = std::make_unique<VideoCommon::ShaderCache>();
g_texture_cache = std::make_unique<TextureCache>();
g_vertex_manager = std::make_unique<VertexManager>();
@ -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()

View File

@ -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<const NullTexture*>(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

View File

@ -29,15 +29,12 @@ public:
size_t length) override;
std::unique_ptr<AbstractPipeline> 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
{

View File

@ -9,7 +9,6 @@ add_library(videoogl
PerfQuery.cpp
PostProcessing.cpp
ProgramShaderCache.cpp
RasterFont.cpp
Render.cpp
SamplerCache.cpp
StreamBuffer.cpp

View File

@ -46,7 +46,6 @@
<ClCompile Include="PerfQuery.cpp" />
<ClCompile Include="PostProcessing.cpp" />
<ClCompile Include="ProgramShaderCache.cpp" />
<ClCompile Include="RasterFont.cpp" />
<ClCompile Include="Render.cpp" />
<ClCompile Include="SamplerCache.cpp" />
<ClCompile Include="StreamBuffer.cpp" />
@ -64,7 +63,6 @@
<ClInclude Include="PerfQuery.h" />
<ClInclude Include="PostProcessing.h" />
<ClInclude Include="ProgramShaderCache.h" />
<ClInclude Include="RasterFont.h" />
<ClInclude Include="Render.h" />
<ClInclude Include="SamplerCache.h" />
<ClInclude Include="StreamBuffer.h" />

View File

@ -7,9 +7,6 @@
<Filter Include="GLUtil">
<UniqueIdentifier>{5bfec41c-1031-4925-8f98-38c7b49b1924}</UniqueIdentifier>
</Filter>
<Filter Include="Logging">
<UniqueIdentifier>{00dadfd8-a906-4b0c-b415-d42a69cf3ca7}</UniqueIdentifier>
</Filter>
<Filter Include="Render">
<UniqueIdentifier>{696df73b-378e-4399-8f21-999b65d78dcd}</UniqueIdentifier>
</Filter>
@ -24,9 +21,6 @@
<ClCompile Include="TextureConverter.cpp">
<Filter>GLUtil</Filter>
</ClCompile>
<ClCompile Include="RasterFont.cpp">
<Filter>Logging</Filter>
</ClCompile>
<ClCompile Include="BoundingBox.cpp">
<Filter>Render</Filter>
</ClCompile>
@ -70,9 +64,6 @@
<ClInclude Include="TextureConverter.h">
<Filter>GLUtil</Filter>
</ClInclude>
<ClInclude Include="RasterFont.h">
<Filter>Logging</Filter>
</ClInclude>
<ClInclude Include="BoundingBox.h">
<Filter>Render</Filter>
</ClInclude>

View File

@ -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"

View File

@ -1,291 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <vector>
#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<u32> 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<VertexManager*>(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<GLfloat> 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<VertexManager*>(g_vertex_manager.get())->GetVertexBufferHandle());
ProgramShaderCache::InvalidateVertexFormat();
}
}

View File

@ -1,30 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <string>
#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;
};
}

View File

@ -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<RasterFont> 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<GLContext> main_gl_context)
Renderer::Renderer(std::unique_ptr<GLContext> main_gl_context, float backbuffer_scale)
: ::Renderer(static_cast<int>(std::max(main_gl_context->GetBackBufferWidth(), 1u)),
static_cast<int>(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<GLContext> 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<GLContext> 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<OpenGLPostProcessing>();
s_raster_font = std::make_unique<RasterFont>();
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<const OGLTexture*>(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<float>(screen_width) - 1.0f,
1.0f - top * 2.0f / static_cast<float>(screen_height), 0,
screen_width, screen_height, color);
}
std::unique_ptr<AbstractShader> 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<OpenGLPostProcessing*>(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<const OGLTexture*>(texture)->GetRawTexIdentifier(),
texture->GetWidth(), texture->GetHeight(), 0);
post_processor->BlitFromTexture(source_rc, right_rc,
static_cast<const OGLTexture*>(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<const OGLTexture*>(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<const OGLTexture*>(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<const OGLTexture*>(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<OGLTexture*>(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<FramebufferManager*>(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<FramebufferManager>(
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<GLenum>(GL_TEXTURE0 + i));
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
m_bound_textures[i] = nullptr;
}
}

View File

@ -83,7 +83,7 @@ extern VideoConfig g_ogl_config;
class Renderer : public ::Renderer
{
public:
Renderer(std::unique_ptr<GLContext> main_gl_context);
Renderer(std::unique_ptr<GLContext> 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<GLContext> m_main_gl_context;
std::array<const AbstractTexture*, 8> 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

View File

@ -172,7 +172,7 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi)
if (!InitializeGLExtensions(main_gl_context.get()) || !FillBackendInfo())
return false;
g_renderer = std::make_unique<Renderer>(std::move(main_gl_context));
g_renderer = std::make_unique<Renderer>(std::move(main_gl_context), wsi.render_surface_scale);
g_vertex_manager = std::make_unique<VertexManager>();
g_perf_query = GetPerfQuery();
ProgramShaderCache::Init();

View File

@ -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<SW::SWTexture*>(image);
const SW::SWTexture* sw_image = static_cast<const SW::SWTexture*>(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();
}

View File

@ -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<SWOGLWindow> 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<TextData> m_text;
u32 m_image_program = 0;
u32 m_image_texture = 0;
u32 m_image_vao = 0;

View File

@ -25,7 +25,7 @@
#include "VideoCommon/VideoConfig.h"
SWRenderer::SWRenderer(std::unique_ptr<SWOGLWindow> window)
: ::Renderer(static_cast<int>(MAX_XFB_WIDTH), static_cast<int>(MAX_XFB_HEIGHT),
: ::Renderer(static_cast<int>(MAX_XFB_WIDTH), static_cast<int>(MAX_XFB_HEIGHT), 1.0f,
AbstractTextureFormat::RGBA8),
m_window(std::move(window))
{
@ -55,11 +55,6 @@ SWRenderer::CreateFramebuffer(const AbstractTexture* color_attachment,
static_cast<const SW::SWTexture*>(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<AbstractPipeline> 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)

View File

@ -32,7 +32,6 @@ public:
size_t length) override;
std::unique_ptr<AbstractPipeline> 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;

View File

@ -5,7 +5,6 @@ add_library(videovulkan
ObjectCache.cpp
PerfQuery.cpp
PostProcessing.cpp
RasterFont.cpp
Renderer.cpp
ShaderCache.cpp
ShaderCompiler.cpp

View File

@ -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 =

View File

@ -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<VertexFormat> m_utility_shader_vertex_format;
std::unique_ptr<StreamBuffer> m_utility_shader_vertex_buffer;
std::unique_ptr<StreamBuffer> m_utility_shader_uniform_buffer;
std::unique_ptr<StreamBuffer> m_texture_upload_buffer;
VkSampler m_point_sampler = VK_NULL_HANDLE;
VkSampler m_linear_sampler = VK_NULL_HANDLE;

View File

@ -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;

View File

@ -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;
};

View File

@ -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 <cstring>
#include <vector>
#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<u32> 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<VkDeviceSize>(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, &region);
// 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<float>(c - CHARACTER_OFFSET), 0.0f);
num_vertices++;
vertices[num_vertices].SetPosition(x + delta_x, y);
vertices[num_vertices].SetTextureCoordinates(static_cast<float>(c - CHARACTER_OFFSET + 1),
0.0f);
num_vertices++;
vertices[num_vertices].SetPosition(x + delta_x, y + delta_y);
vertices[num_vertices].SetTextureCoordinates(static_cast<float>(c - CHARACTER_OFFSET + 1),
1.0f);
num_vertices++;
vertices[num_vertices].SetPosition(x, y);
vertices[num_vertices].SetTextureCoordinates(static_cast<float>(c - CHARACTER_OFFSET), 0.0f);
num_vertices++;
vertices[num_vertices].SetPosition(x + delta_x, y + delta_y);
vertices[num_vertices].SetTextureCoordinates(static_cast<float>(c - CHARACTER_OFFSET + 1),
1.0f);
num_vertices++;
vertices[num_vertices].SetPosition(x, y + delta_y);
vertices[num_vertices].SetTextureCoordinates(static_cast<float>(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<float>(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

View File

@ -1,41 +0,0 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#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<Texture2D> m_texture;
VkShaderModule m_vertex_shader = VK_NULL_HANDLE;
VkShaderModule m_fragment_shader = VK_NULL_HANDLE;
};
} // namespace Vulkan

View File

@ -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<SwapChain> swap_chain)
Renderer::Renderer(std::unique_ptr<SwapChain> swap_chain, float backbuffer_scale)
: ::Renderer(swap_chain ? static_cast<int>(swap_chain->GetWidth()) : 1,
swap_chain ? static_cast<int>(swap_chain->GetHeight()) : 0,
swap_chain ? static_cast<int>(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<RasterFont>();
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<VulkanPostProcessing>();
if (!static_cast<VulkanPostProcessing*>(m_post_processor.get())
->Initialize(m_raster_font->GetTexture()))
if (!static_cast<VulkanPostProcessing*>(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<const VKTexture*>(depth_attachment));
}
std::tuple<VkBuffer, u32> 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<u32>(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<const VKPipeline*>(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<float>(backbuffer_width) - 1,
1 - top * 2.0f / static_cast<float>(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<VKTexture*>(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<VulkanPostProcessing*>(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<const VKTexture*>(texture)->GetRawTexIdentifier(),
0, m_swap_chain_render_pass);
post_processor->BlitFromTexture(right_rect, rc,
static_cast<const VKTexture*>(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<const VKTexture*>(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<const VKTexture*>(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.

View File

@ -7,7 +7,6 @@
#include <array>
#include <cstddef>
#include <memory>
#include <tuple>
#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<SwapChain> swap_chain);
Renderer(std::unique_ptr<SwapChain> 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<VkBuffer, u32> 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<SwapChain> m_swap_chain;
std::unique_ptr<BoundingBox> m_bounding_box;
std::unique_ptr<RasterFont> m_raster_font;
// Keep a copy of sampler states to avoid cache lookups every draw
std::array<SamplerState, NUM_PIXEL_SHADER_SAMPLERS> m_sampler_states = {};

View File

@ -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;
}

View File

@ -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<u32> m_scheduled_command_buffer_kicks;
bool m_allow_background_execution = true;
};
}
} // namespace Vulkan

View File

@ -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<TextureCache*>(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<TextureConverter>();
if (!m_texture_converter->Initialize())
{

View File

@ -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<StreamBuffer> m_texture_upload_buffer;
std::unique_ptr<TextureConverter> m_texture_converter;
VkShaderModule m_copy_shader = VK_NULL_HANDLE;

View File

@ -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.

View File

@ -44,7 +44,6 @@
<ClCompile Include="ShaderCache.cpp" />
<ClCompile Include="TextureConverter.cpp" />
<ClCompile Include="PerfQuery.cpp" />
<ClCompile Include="RasterFont.cpp" />
<ClCompile Include="StagingBuffer.cpp" />
<ClCompile Include="Util.cpp" />
<ClCompile Include="VertexFormat.cpp" />
@ -71,7 +70,6 @@
<ClInclude Include="PostProcessing.h" />
<ClInclude Include="ShaderCache.h" />
<ClInclude Include="TextureConverter.h" />
<ClInclude Include="RasterFont.h" />
<ClInclude Include="StagingBuffer.h" />
<ClInclude Include="Util.h" />
<ClInclude Include="VertexFormat.h" />

View File

@ -225,7 +225,7 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi)
// Create main wrapper instances.
g_framebuffer_manager = std::make_unique<FramebufferManager>();
g_renderer = std::make_unique<Renderer>(std::move(swap_chain));
g_renderer = std::make_unique<Renderer>(std::move(swap_chain), wsi.render_surface_scale);
g_vertex_manager = std::make_unique<VertexManager>();
g_texture_cache = std::make_unique<TextureCache>();
::g_shader_cache = std::make_unique<VideoCommon::ShaderCache>();
@ -303,6 +303,18 @@ void VideoBackend::PrepareWindow(const WindowSystemInfo& wsi)
// [view setLayer:layer]
reinterpret_cast<void (*)(id, SEL, id)>(objc_msgSend)(view, sel_getUid("setLayer:"), layer);
// NSScreen* screen = [NSScreen mainScreen]
id screen = reinterpret_cast<id (*)(Class, SEL)>(objc_msgSend)(objc_getClass("NSScreen"),
sel_getUid("mainScreen"));
// CGFloat factor = [screen backingScaleFactor]
double factor =
reinterpret_cast<double (*)(id, SEL)>(objc_msgSend)(screen, sel_getUid("backingScaleFactor"));
// layer.contentsScale = factor
reinterpret_cast<void (*)(id, SEL, double)>(objc_msgSend)(layer, sel_getUid("setContentsScale:"),
factor);
#endif
}
} // namespace Vulkan

View File

@ -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++)
{

View File

@ -65,6 +65,7 @@ PUBLIC
PRIVATE
png
xxhash
imgui
)
if(_M_X86)

View File

@ -5,22 +5,79 @@
#include <algorithm>
#include <list>
#include <map>
#include <mutex>
#include <string>
#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<CallbackType, Callback> 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<MessageType, Message> s_messages;
static std::mutex s_messages_mutex;
static ImVec4 RGBAToImVec4(const u32 rgba)
{
return ImVec4(static_cast<float>((rgba >> 16) & 0xFF) / 255.0f,
static_cast<float>((rgba >> 8) & 0xFF) / 255.0f,
static_cast<float>((rgba >> 0) & 0xFF) / 255.0f,
static_cast<float>((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<std::mutex> 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<std::mutex> 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<int>(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<std::mutex> 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

View File

@ -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()>;
void AddCallback(CallbackType type, Callback cb);
void DoCallbacks(CallbackType type);
} // namespace OSD

View File

@ -21,6 +21,8 @@
#include <string>
#include <tuple>
#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<u32>(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<AbstractShader> vertex_shader = CreateShaderFromSource(
ShaderStage::Vertex, vertex_shader_source.c_str(), vertex_shader_source.size());
std::unique_ptr<AbstractShader> 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<AbstractTexture> 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<std::mutex> 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<float>(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<float>(m_backbuffer_width), static_cast<float>(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<float>(m_backbuffer_width),
static_cast<float>(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<int>(
static_cast<int>(cmd.ClipRect.x), static_cast<int>(cmd.ClipRect.y),
static_cast<int>(cmd.ClipRect.z), static_cast<int>(cmd.ClipRect.w)));
SetTexture(0, reinterpret_cast<const AbstractTexture*>(cmd.TextureId));
DrawIndexed(base_index, cmd.ElemCount, base_vertex);
base_index += cmd.ElemCount;
}
}
}
std::unique_lock<std::mutex> Renderer::GetImGuiLock()
{
return std::unique_lock<std::mutex>(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<std::mutex> 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<std::mutex> 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<VideoCommon::AsyncShaderCompiler> Renderer::CreateAsyncShaderCom
{
return std::make_unique<VideoCommon::AsyncShaderCompiler>();
}
void Renderer::ShowOSDMessage(OSDMessage message)
{
m_osd_message = static_cast<s32>(message);
}

View File

@ -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<float, 4>;
@ -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<AbstractShader>
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<VideoCommon::AsyncShaderCompiler> 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<std::mutex> 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<int, int> 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<NativeVertexFormat> m_imgui_vertex_format;
std::vector<std::unique_ptr<AbstractTexture>> m_imgui_textures;
std::unique_ptr<AbstractPipeline> 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);

View File

@ -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 = {};

View File

@ -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();
}

View File

@ -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<VideoCommon::ShaderCache> 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<int>(completed), static_cast<int>(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<float>(completed) /
static_cast<float>(std::max(total, static_cast<size_t>(1))),
ImVec2(-1.0f, 0.0f), "");
}
ImGui::End();
g_renderer->EndUIFrame();
});
m_async_shader_compiler->RetrieveWorkItems();
}
Host_UpdateProgressDialog("", -1, -1);
}
template <ShaderStage stage, typename K, typename T>

View File

@ -6,6 +6,8 @@
#include <string>
#include <utility>
#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();
}

View File

@ -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;

View File

@ -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();

View File

@ -183,8 +183,11 @@
<ProjectReference Include="$(CoreDir)Common\Common.vcxproj">
<Project>{2e6c348c-c75c-4d94-8d1e-9c1fcbf3efe4}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\Externals\imgui\imgui.vcxproj">
<Project>{4c3b2264-ea73-4a7b-9cfe-65b0fd635ebb}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>