@@ -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);
}
@@ -36,6 +36,8 @@ class RenderWidget final : public QWidget
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;

Large diffs are not rendered by default.

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

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,
TargetRectangle left_rc, right_rc;
std::tie(left_rc, right_rc) = ConvertStereoRectangle(target_rc);

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,17 +808,18 @@ 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() :
PixelShaderCache::GetColorCopyProgram(false);
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);
}
@@ -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 @@ class Renderer : public ::Renderer
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 @@ class Renderer : public ::Renderer

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 @@ class Renderer : public ::Renderer
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 @@ class Renderer : public ::Renderer
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;
};
}
@@ -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()
@@ -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
@@ -29,15 +29,12 @@ class Renderer : public ::Renderer
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
{
@@ -9,7 +9,6 @@ add_library(videoogl
PerfQuery.cpp
PostProcessing.cpp
ProgramShaderCache.cpp
RasterFont.cpp
Render.cpp
SamplerCache.cpp
StreamBuffer.cpp
@@ -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" />
@@ -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>
@@ -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"

This file was deleted.

This file was deleted.

@@ -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,119 +1413,36 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
glDisable(GL_DEBUG_OUTPUT);
}

auto* xfb_texture = static_cast<OGLTexture*>(texture);

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())
{
// 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);
// Swap the back and front buffers, presenting the image.
m_main_gl_context->Swap();
}

if (fb_needs_update)
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))
{
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);
}

g_framebuffer_manager.reset();
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);
}

// 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 (bits & CONFIG_CHANGE_BIT_VSYNC && !DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC))
m_main_gl_context->SwapInterval(g_ActiveConfig.IsVSync());

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

@@ -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 @@ class Renderer : public ::Renderer
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 @@ class Renderer : public ::Renderer

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 @@ class Renderer : public ::Renderer
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 @@ class Renderer : public ::Renderer
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
@@ -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();
@@ -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();
}
@@ -24,11 +24,8 @@ class SWOGLWindow
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 @@ class SWOGLWindow

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;
@@ -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)
@@ -32,15 +32,14 @@ class SWRenderer : public Renderer
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;
void BBoxWrite(int index, u16 value) override;

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;
@@ -5,7 +5,6 @@ add_library(videovulkan
ObjectCache.cpp
PerfQuery.cpp
PostProcessing.cpp
RasterFont.cpp
Renderer.cpp
ShaderCache.cpp
ShaderCompiler.cpp
@@ -54,6 +54,15 @@ bool ObjectCache::Initialize()
if (!CreateStaticSamplers())
return false;

m_texture_upload_buffer =
StreamBuffer::Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, INITIAL_TEXTURE_UPLOAD_BUFFER_SIZE,
MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE);
if (!m_texture_upload_buffer)
{
PanicAlert("Failed to create texture upload buffer");
return false;
}

m_utility_shader_vertex_buffer =
StreamBuffer::Create(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 1024 * 1024, 4 * 1024 * 1024);
m_utility_shader_uniform_buffer =
@@ -58,6 +58,7 @@ class ObjectCache
{
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 @@ class ObjectCache
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;
@@ -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;
@@ -21,7 +21,7 @@ class VulkanPostProcessing : public PostProcessingShaderImplementation
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 @@ class VulkanPostProcessing : public PostProcessingShaderImplementation
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;
};

This file was deleted.

This file was deleted.

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

auto* xfb_texture = static_cast<VKTexture*>(texture);
Util::ExecuteCurrentCommandsAndRestoreState(true, false);
}

// 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.
@@ -7,7 +7,6 @@
#include <array>
#include <cstddef>
#include <memory>
#include <tuple>

#include "Common/CommonTypes.h"
#include "VideoBackends/Vulkan/Constants.h"
@@ -22,15 +21,14 @@ class FramebufferManager;
class SwapChain;
class StagingTexture2D;
class Texture2D;
class RasterFont;
class VKFramebuffer;
class VKPipeline;
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 @@ class Renderer : public ::Renderer

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 @@ class Renderer : public ::Renderer
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 @@ class Renderer : public ::Renderer

void CheckForSurfaceChange();
void CheckForSurfaceResize();
void CheckForConfigChanges();

void ResetSamplerStates();

@@ -112,23 +111,13 @@ class Renderer : public ::Renderer
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;
VkRenderPass m_swap_chain_clear_render_pass = VK_NULL_HANDLE;

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 = {};
@@ -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;
}
@@ -103,7 +103,8 @@ class StateTracker
// 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 @@ class StateTracker
std::vector<u32> m_scheduled_command_buffer_kicks;
bool m_allow_background_execution = true;
};
}
} // namespace Vulkan
@@ -45,27 +45,13 @@ 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());
}

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())
{
@@ -49,16 +49,13 @@ class TextureCache : public TextureCacheBase
TLUTFormat palette_format) override;

VkShaderModule GetCopyShader() const;
StreamBuffer* GetTextureUploadBuffer() const;

private:
void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
bool scale_by_half, EFBCopyFormat dst_format, bool is_intensity,
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;
@@ -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.
@@ -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" />
@@ -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
@@ -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++)
{
@@ -65,6 +65,7 @@ PUBLIC
PRIVATE
png
xxhash
imgui
)

if(_M_X86)
@@ -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
@@ -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