Merge pull request #5832 from stenzek/ubershader-prereq
Ubershader prerequisites
This commit is contained in:
commit
5bad2ee4a4
|
@ -148,6 +148,10 @@ void Host_YieldToUI()
|
|||
{
|
||||
}
|
||||
|
||||
void Host_UpdateProgressDialog(const char* caption, int position, int total)
|
||||
{
|
||||
}
|
||||
|
||||
static bool MsgAlert(const char* caption, const char* text, bool yes_no, int /*Style*/)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_ERROR, DOLPHIN_TAG, "%s:%s", caption, text);
|
||||
|
|
|
@ -141,6 +141,8 @@ public:
|
|||
|
||||
constexpr T Value() const { return Value(std::is_signed<T>()); }
|
||||
constexpr operator T() const { return Value(); }
|
||||
constexpr std::size_t StartBit() const { return position; }
|
||||
constexpr std::size_t NumBits() const { return bits; }
|
||||
private:
|
||||
// StorageType is T for non-enum types and the underlying type of T if
|
||||
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#import <AppKit/AppKit.h>
|
||||
#else
|
||||
struct NSOpenGLContext;
|
||||
struct NSOpenGLPixelFormat;
|
||||
struct NSView;
|
||||
#endif
|
||||
|
||||
|
@ -18,13 +19,16 @@ class cInterfaceAGL : public cInterfaceBase
|
|||
public:
|
||||
void Swap() override;
|
||||
bool Create(void* window_handle, bool stereo, bool core) override;
|
||||
bool Create(cInterfaceBase* main_context) override;
|
||||
bool MakeCurrent() override;
|
||||
bool ClearCurrent() override;
|
||||
void Shutdown() override;
|
||||
void Update() override;
|
||||
void SwapInterval(int interval) override;
|
||||
std::unique_ptr<cInterfaceBase> CreateSharedContext() override;
|
||||
|
||||
private:
|
||||
NSView* m_view;
|
||||
NSOpenGLContext* m_context;
|
||||
NSView* m_view = nullptr;
|
||||
NSOpenGLContext* m_context = nullptr;
|
||||
NSOpenGLPixelFormat* m_pixel_format = nullptr;
|
||||
};
|
||||
|
|
|
@ -60,15 +60,14 @@ bool cInterfaceAGL::Create(void* window_handle, bool stereo, bool core)
|
|||
NSOpenGLPFAAccelerated,
|
||||
stereo ? NSOpenGLPFAStereo : static_cast<NSOpenGLPixelFormatAttribute>(0),
|
||||
0};
|
||||
NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
|
||||
if (fmt == nil)
|
||||
m_pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
|
||||
if (m_pixel_format == nil)
|
||||
{
|
||||
ERROR_LOG(VIDEO, "failed to create pixel format");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_context = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:nil];
|
||||
[fmt release];
|
||||
m_context = [[NSOpenGLContext alloc] initWithFormat:m_pixel_format shareContext:nil];
|
||||
if (m_context == nil)
|
||||
{
|
||||
ERROR_LOG(VIDEO, "failed to create context");
|
||||
|
@ -82,6 +81,30 @@ bool cInterfaceAGL::Create(void* window_handle, bool stereo, bool core)
|
|||
return AttachContextToView(m_context, m_view, &s_backbuffer_width, &s_backbuffer_height);
|
||||
}
|
||||
|
||||
bool cInterfaceAGL::Create(cInterfaceBase* main_context)
|
||||
{
|
||||
cInterfaceAGL* agl_context = static_cast<cInterfaceAGL*>(main_context);
|
||||
NSOpenGLPixelFormat* pixel_format = agl_context->m_pixel_format;
|
||||
NSOpenGLContext* share_context = agl_context->m_context;
|
||||
|
||||
m_context = [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:share_context];
|
||||
if (m_context == nil)
|
||||
{
|
||||
ERROR_LOG(VIDEO, "failed to create shared context");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<cInterfaceBase> cInterfaceAGL::CreateSharedContext()
|
||||
{
|
||||
std::unique_ptr<cInterfaceBase> context = std::make_unique<cInterfaceAGL>();
|
||||
if (!context->Create(this))
|
||||
return nullptr;
|
||||
return context;
|
||||
}
|
||||
|
||||
bool cInterfaceAGL::MakeCurrent()
|
||||
{
|
||||
[m_context makeCurrentContext];
|
||||
|
@ -100,6 +123,8 @@ void cInterfaceAGL::Shutdown()
|
|||
[m_context clearDrawable];
|
||||
[m_context release];
|
||||
m_context = nil;
|
||||
[m_pixel_format release];
|
||||
m_pixel_format = nil;
|
||||
}
|
||||
|
||||
void cInterfaceAGL::Update()
|
||||
|
|
|
@ -212,14 +212,18 @@ bool cInterfaceEGL::Create(void* window_handle, bool stereo, bool core)
|
|||
|
||||
egl_ctx = eglCreateContext(egl_dpy, m_config, EGL_NO_CONTEXT, &core_attribs[0]);
|
||||
if (egl_ctx)
|
||||
{
|
||||
m_attribs = std::move(core_attribs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!egl_ctx)
|
||||
{
|
||||
m_core = false;
|
||||
egl_ctx = eglCreateContext(egl_dpy, m_config, EGL_NO_CONTEXT, &ctx_attribs[0]);
|
||||
m_attribs = std::move(ctx_attribs);
|
||||
}
|
||||
|
||||
if (!egl_ctx)
|
||||
|
@ -255,31 +259,13 @@ bool cInterfaceEGL::Create(cInterfaceBase* main_context)
|
|||
m_is_shared = true;
|
||||
m_has_handle = false;
|
||||
|
||||
EGLint ctx_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
|
||||
|
||||
switch (egl_context->GetMode())
|
||||
{
|
||||
case GLInterfaceMode::MODE_OPENGL:
|
||||
ctx_attribs[0] = EGL_NONE;
|
||||
break;
|
||||
case GLInterfaceMode::MODE_OPENGLES2:
|
||||
ctx_attribs[1] = 2;
|
||||
break;
|
||||
case GLInterfaceMode::MODE_OPENGLES3:
|
||||
ctx_attribs[1] = 3;
|
||||
break;
|
||||
default:
|
||||
INFO_LOG(VIDEO, "Unknown opengl mode set");
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (egl_context->GetMode() == GLInterfaceMode::MODE_OPENGL)
|
||||
eglBindAPI(EGL_OPENGL_API);
|
||||
else
|
||||
eglBindAPI(EGL_OPENGL_ES_API);
|
||||
|
||||
egl_ctx = eglCreateContext(egl_dpy, m_config, egl_context->egl_ctx, ctx_attribs);
|
||||
egl_ctx =
|
||||
eglCreateContext(egl_dpy, m_config, egl_context->egl_ctx, egl_context->m_attribs.data());
|
||||
if (!egl_ctx)
|
||||
{
|
||||
INFO_LOG(VIDEO, "Error: eglCreateContext failed 0x%04x", eglGetError());
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/GL/GLInterfaceBase.h"
|
||||
|
||||
|
@ -17,6 +18,7 @@ private:
|
|||
bool m_has_handle;
|
||||
EGLNativeWindowType m_host_window;
|
||||
bool m_supports_surfaceless = false;
|
||||
std::vector<int> m_attribs;
|
||||
|
||||
bool CreateWindowSurface();
|
||||
void DestroyWindowSurface();
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
|
||||
#include "Common/GL/GLInterface/GLX.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
|
@ -17,6 +18,9 @@ typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int interval);
|
|||
static PFNGLXCREATECONTEXTATTRIBSPROC glXCreateContextAttribs = nullptr;
|
||||
static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = nullptr;
|
||||
|
||||
static PFNGLXCREATEGLXPBUFFERSGIXPROC glXCreateGLXPbufferSGIX = nullptr;
|
||||
static PFNGLXDESTROYGLXPBUFFERSGIXPROC glXDestroyGLXPbufferSGIX = nullptr;
|
||||
|
||||
static bool s_glxError;
|
||||
static int ctxErrorHandler(Display* dpy, XErrorEvent* ev)
|
||||
{
|
||||
|
@ -26,7 +30,7 @@ static int ctxErrorHandler(Display* dpy, XErrorEvent* ev)
|
|||
|
||||
void cInterfaceGLX::SwapInterval(int Interval)
|
||||
{
|
||||
if (glXSwapIntervalSGI)
|
||||
if (glXSwapIntervalSGI && m_has_handle)
|
||||
glXSwapIntervalSGI(Interval);
|
||||
else
|
||||
ERROR_LOG(VIDEO, "No support for SwapInterval (framerate clamped to monitor refresh rate).");
|
||||
|
@ -45,6 +49,9 @@ void cInterfaceGLX::Swap()
|
|||
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
|
||||
bool cInterfaceGLX::Create(void* window_handle, bool stereo, bool core)
|
||||
{
|
||||
m_has_handle = !!window_handle;
|
||||
m_host_window = (Window)window_handle;
|
||||
|
||||
dpy = XOpenDisplay(nullptr);
|
||||
int screen = DefaultScreen(dpy);
|
||||
|
||||
|
@ -100,51 +107,43 @@ bool cInterfaceGLX::Create(void* window_handle, bool stereo, bool core)
|
|||
fbconfig = *fbc;
|
||||
XFree(fbc);
|
||||
|
||||
// Get an appropriate visual
|
||||
XVisualInfo* vi = glXGetVisualFromFBConfig(dpy, fbconfig);
|
||||
|
||||
s_glxError = false;
|
||||
XErrorHandler oldHandler = XSetErrorHandler(&ctxErrorHandler);
|
||||
|
||||
// Create a GLX context.
|
||||
// We try to get a 4.0 core profile, else we try 3.3, else try it with anything we get.
|
||||
int context_attribs[] = {GLX_CONTEXT_MAJOR_VERSION_ARB,
|
||||
4,
|
||||
GLX_CONTEXT_MINOR_VERSION_ARB,
|
||||
0,
|
||||
GLX_CONTEXT_PROFILE_MASK_ARB,
|
||||
GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
GLX_CONTEXT_FLAGS_ARB,
|
||||
GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
|
||||
None};
|
||||
std::array<int, 9> context_attribs = {
|
||||
{GLX_CONTEXT_MAJOR_VERSION_ARB, 4, GLX_CONTEXT_MINOR_VERSION_ARB, 0,
|
||||
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, GLX_CONTEXT_FLAGS_ARB,
|
||||
GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, None}};
|
||||
ctx = nullptr;
|
||||
if (core)
|
||||
{
|
||||
ctx = glXCreateContextAttribs(dpy, fbconfig, 0, True, context_attribs);
|
||||
ctx = glXCreateContextAttribs(dpy, fbconfig, 0, True, &context_attribs[0]);
|
||||
XSync(dpy, False);
|
||||
m_attribs.insert(m_attribs.end(), context_attribs.begin(), context_attribs.end());
|
||||
}
|
||||
if (core && (!ctx || s_glxError))
|
||||
{
|
||||
int context_attribs_33[] = {GLX_CONTEXT_MAJOR_VERSION_ARB,
|
||||
3,
|
||||
GLX_CONTEXT_MINOR_VERSION_ARB,
|
||||
3,
|
||||
GLX_CONTEXT_PROFILE_MASK_ARB,
|
||||
GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
GLX_CONTEXT_FLAGS_ARB,
|
||||
GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
|
||||
None};
|
||||
std::array<int, 9> context_attribs_33 = {
|
||||
{GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 3,
|
||||
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, GLX_CONTEXT_FLAGS_ARB,
|
||||
GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, None}};
|
||||
s_glxError = false;
|
||||
ctx = glXCreateContextAttribs(dpy, fbconfig, 0, True, context_attribs_33);
|
||||
ctx = glXCreateContextAttribs(dpy, fbconfig, 0, True, &context_attribs_33[0]);
|
||||
XSync(dpy, False);
|
||||
m_attribs.clear();
|
||||
m_attribs.insert(m_attribs.end(), context_attribs_33.begin(), context_attribs_33.end());
|
||||
}
|
||||
if (!ctx || s_glxError)
|
||||
{
|
||||
int context_attribs_legacy[] = {GLX_CONTEXT_MAJOR_VERSION_ARB, 1, GLX_CONTEXT_MINOR_VERSION_ARB,
|
||||
0, None};
|
||||
std::array<int, 5> context_attribs_legacy = {
|
||||
{GLX_CONTEXT_MAJOR_VERSION_ARB, 1, GLX_CONTEXT_MINOR_VERSION_ARB, 0, None}};
|
||||
s_glxError = false;
|
||||
ctx = glXCreateContextAttribs(dpy, fbconfig, 0, True, context_attribs_legacy);
|
||||
ctx = glXCreateContextAttribs(dpy, fbconfig, 0, True, &context_attribs_legacy[0]);
|
||||
XSync(dpy, False);
|
||||
m_attribs.clear();
|
||||
m_attribs.insert(m_attribs.end(), context_attribs_legacy.begin(), context_attribs_legacy.end());
|
||||
}
|
||||
if (!ctx || s_glxError)
|
||||
{
|
||||
|
@ -153,12 +152,78 @@ bool cInterfaceGLX::Create(void* window_handle, bool stereo, bool core)
|
|||
}
|
||||
XSetErrorHandler(oldHandler);
|
||||
|
||||
std::string tmp;
|
||||
std::istringstream buffer(glXQueryExtensionsString(dpy, screen));
|
||||
while (buffer >> tmp)
|
||||
{
|
||||
if (tmp == "GLX_SGIX_pbuffer")
|
||||
m_supports_pbuffer = true;
|
||||
}
|
||||
|
||||
if (m_supports_pbuffer)
|
||||
{
|
||||
// Get the function pointers we require
|
||||
glXCreateGLXPbufferSGIX =
|
||||
(PFNGLXCREATEGLXPBUFFERSGIXPROC)GetFuncAddress("glXCreateGLXPbufferSGIX");
|
||||
glXDestroyGLXPbufferSGIX =
|
||||
(PFNGLXDESTROYGLXPBUFFERSGIXPROC)GetFuncAddress("glXDestroyGLXPbufferSGIX");
|
||||
}
|
||||
|
||||
if (!CreateWindowSurface())
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Error: CreateWindowSurface failed\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cInterfaceGLX::Create(cInterfaceBase* main_context)
|
||||
{
|
||||
cInterfaceGLX* glx_context = static_cast<cInterfaceGLX*>(main_context);
|
||||
|
||||
m_has_handle = false;
|
||||
dpy = glx_context->dpy;
|
||||
fbconfig = glx_context->fbconfig;
|
||||
s_glxError = false;
|
||||
XErrorHandler oldHandler = XSetErrorHandler(&ctxErrorHandler);
|
||||
|
||||
ctx = glXCreateContextAttribs(dpy, fbconfig, glx_context->ctx, True, &glx_context->m_attribs[0]);
|
||||
XSync(dpy, False);
|
||||
|
||||
if (!ctx || s_glxError)
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Unable to create GL context.");
|
||||
return false;
|
||||
}
|
||||
XSetErrorHandler(oldHandler);
|
||||
|
||||
if (!CreateWindowSurface())
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Error: CreateWindowSurface failed\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<cInterfaceBase> cInterfaceGLX::CreateSharedContext()
|
||||
{
|
||||
std::unique_ptr<cInterfaceBase> context = std::make_unique<cInterfaceGLX>();
|
||||
if (!context->Create(this))
|
||||
return nullptr;
|
||||
return context;
|
||||
}
|
||||
|
||||
bool cInterfaceGLX::CreateWindowSurface()
|
||||
{
|
||||
if (m_has_handle)
|
||||
{
|
||||
// Get an appropriate visual
|
||||
XVisualInfo* vi = glXGetVisualFromFBConfig(dpy, fbconfig);
|
||||
|
||||
XWindow.Initialize(dpy);
|
||||
|
||||
Window parent = (Window)window_handle;
|
||||
|
||||
XWindowAttributes attribs;
|
||||
if (!XGetWindowAttributes(dpy, parent, &attribs))
|
||||
if (!XGetWindowAttributes(dpy, m_host_window, &attribs))
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Window attribute retrieval failed");
|
||||
return false;
|
||||
|
@ -167,16 +232,36 @@ bool cInterfaceGLX::Create(void* window_handle, bool stereo, bool core)
|
|||
s_backbuffer_width = attribs.width;
|
||||
s_backbuffer_height = attribs.height;
|
||||
|
||||
win = XWindow.CreateXWindow(parent, vi);
|
||||
win = XWindow.CreateXWindow(m_host_window, vi);
|
||||
XFree(vi);
|
||||
}
|
||||
else
|
||||
{
|
||||
win = m_pbuffer = glXCreateGLXPbufferSGIX(dpy, fbconfig, 1, 1, nullptr);
|
||||
if (!m_pbuffer)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cInterfaceGLX::DestroyWindowSurface()
|
||||
{
|
||||
if (!m_pbuffer)
|
||||
{
|
||||
XWindow.DestroyXWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
glXDestroyGLXPbufferSGIX(dpy, m_pbuffer);
|
||||
m_pbuffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool cInterfaceGLX::MakeCurrent()
|
||||
{
|
||||
bool success = glXMakeCurrent(dpy, win, ctx);
|
||||
if (success)
|
||||
if (success && !glXSwapIntervalSGI)
|
||||
{
|
||||
// load this function based on the current bound context
|
||||
glXSwapIntervalSGI =
|
||||
|
@ -193,11 +278,18 @@ bool cInterfaceGLX::ClearCurrent()
|
|||
// Close backend
|
||||
void cInterfaceGLX::Shutdown()
|
||||
{
|
||||
XWindow.DestroyXWindow();
|
||||
DestroyWindowSurface();
|
||||
if (ctx)
|
||||
{
|
||||
glXDestroyContext(dpy, ctx);
|
||||
|
||||
// Don't close the display connection if we are a shared context.
|
||||
// Saves doing reference counting on this object, and the main context will always
|
||||
// be shut down last anyway.
|
||||
if (m_has_handle)
|
||||
{
|
||||
XCloseDisplay(dpy);
|
||||
ctx = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glx.h>
|
||||
#include <GL/glxext.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/GL/GLInterface/X11_Util.h"
|
||||
#include "Common/GL/GLInterfaceBase.h"
|
||||
|
@ -13,11 +16,19 @@
|
|||
class cInterfaceGLX : public cInterfaceBase
|
||||
{
|
||||
private:
|
||||
Window m_host_window;
|
||||
cX11Window XWindow;
|
||||
Display* dpy;
|
||||
Window win;
|
||||
GLXContext ctx;
|
||||
GLXFBConfig fbconfig;
|
||||
bool m_has_handle;
|
||||
bool m_supports_pbuffer = false;
|
||||
GLXPbufferSGIX m_pbuffer = 0;
|
||||
std::vector<int> m_attribs;
|
||||
|
||||
bool CreateWindowSurface();
|
||||
void DestroyWindowSurface();
|
||||
|
||||
public:
|
||||
friend class cX11Window;
|
||||
|
@ -25,7 +36,9 @@ public:
|
|||
void Swap() override;
|
||||
void* GetFuncAddress(const std::string& name) override;
|
||||
bool Create(void* window_handle, bool stereo, bool core) override;
|
||||
bool Create(cInterfaceBase* main_context) override;
|
||||
bool MakeCurrent() override;
|
||||
bool ClearCurrent() override;
|
||||
void Shutdown() override;
|
||||
std::unique_ptr<cInterfaceBase> CreateSharedContext() override;
|
||||
};
|
||||
|
|
|
@ -35,6 +35,7 @@ void Host_UpdateMainFrame();
|
|||
void Host_UpdateTitle(const std::string& title);
|
||||
void Host_ShowVideoConfig(void* parent, const std::string& backend_name);
|
||||
void Host_YieldToUI();
|
||||
void Host_UpdateProgressDialog(const char* caption, int position, int total);
|
||||
|
||||
// TODO (neobrain): Remove this from host!
|
||||
void* Host_GetRenderHandle();
|
||||
|
|
|
@ -136,6 +136,10 @@ void Host_YieldToUI()
|
|||
{
|
||||
}
|
||||
|
||||
void Host_UpdateProgressDialog(const char* caption, int position, int total)
|
||||
{
|
||||
}
|
||||
|
||||
#if HAVE_X11
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/keysym.h>
|
||||
|
|
|
@ -84,6 +84,9 @@ void Host_YieldToUI()
|
|||
{
|
||||
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
}
|
||||
void Host_UpdateProgressDialog(const char* caption, int position, int total)
|
||||
{
|
||||
}
|
||||
|
||||
// We ignore these, and their purpose should be questioned individually.
|
||||
// In particular, RequestRenderWindowSize, RequestFullscreen, and
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <wx/menu.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/progdlg.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/statusbr.h>
|
||||
#include <wx/textctrl.h>
|
||||
|
@ -817,6 +818,37 @@ void CFrame::OnHostMessage(wxCommandEvent& event)
|
|||
case IDM_STOPPED:
|
||||
OnStopped();
|
||||
break;
|
||||
|
||||
case IDM_UPDATE_PROGRESS_DIALOG:
|
||||
{
|
||||
int current = event.GetInt();
|
||||
int total = static_cast<int>(event.GetExtraLong());
|
||||
if (total < 0 || current >= total)
|
||||
{
|
||||
if (m_progress_dialog)
|
||||
{
|
||||
delete m_progress_dialog;
|
||||
m_progress_dialog = nullptr;
|
||||
}
|
||||
}
|
||||
else if (total > 0 && current < total)
|
||||
{
|
||||
if (!m_progress_dialog)
|
||||
{
|
||||
m_progress_dialog = new wxProgressDialog(
|
||||
_("Operation in progress..."), event.GetString(), total, m_render_frame,
|
||||
wxPD_APP_MODAL | wxPD_ELAPSED_TIME | wxPD_SMOOTH | wxPD_REMAINING_TIME);
|
||||
m_progress_dialog->Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_progress_dialog->GetRange() != total)
|
||||
m_progress_dialog->SetRange(total);
|
||||
m_progress_dialog->Update(current, event.GetString());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ class wxAuiNotebook;
|
|||
class wxAuiNotebookEvent;
|
||||
class wxListEvent;
|
||||
class wxMenuItem;
|
||||
class wxProgressDialog;
|
||||
|
||||
class CRenderFrame : public wxFrame
|
||||
{
|
||||
|
@ -154,6 +155,7 @@ private:
|
|||
FifoPlayerDlg* m_fifo_player_dialog = nullptr;
|
||||
std::array<TASInputDlg*, 8> m_tas_input_dialogs{};
|
||||
wxCheatsWindow* m_cheats_window = nullptr;
|
||||
wxProgressDialog* m_progress_dialog = nullptr;
|
||||
bool m_use_debugger = false;
|
||||
bool m_batch_mode = false;
|
||||
bool m_editing_perspectives = false;
|
||||
|
|
|
@ -307,6 +307,7 @@ enum
|
|||
IDM_WINDOW_SIZE_REQUEST,
|
||||
IDM_STOPPED,
|
||||
IDM_HOST_MESSAGE,
|
||||
IDM_UPDATE_PROGRESS_DIALOG,
|
||||
|
||||
IDM_MPANEL,
|
||||
ID_STATUSBAR,
|
||||
|
|
|
@ -488,3 +488,12 @@ void Host_YieldToUI()
|
|||
{
|
||||
wxGetApp().GetMainLoop()->YieldFor(wxEVT_CATEGORY_UI);
|
||||
}
|
||||
|
||||
void Host_UpdateProgressDialog(const char* caption, int position, int total)
|
||||
{
|
||||
wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATE_PROGRESS_DIALOG);
|
||||
event.SetString(caption);
|
||||
event.SetInt(position);
|
||||
event.SetExtraLong(total);
|
||||
main_frame->GetEventHandler()->AddPendingEvent(event);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ set(SRCS
|
|||
PostProcessing.cpp
|
||||
RasterFont.cpp
|
||||
Renderer.cpp
|
||||
ShaderCache.cpp
|
||||
ShaderCompiler.cpp
|
||||
StateTracker.cpp
|
||||
StagingBuffer.cpp
|
||||
|
|
|
@ -435,8 +435,8 @@ void FramebufferManager::ReinterpretPixelData(int convtype)
|
|||
|
||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
|
||||
m_efb_load_render_pass, g_object_cache->GetScreenQuadVertexShader(),
|
||||
g_object_cache->GetScreenQuadGeometryShader(), pixel_shader);
|
||||
m_efb_load_render_pass, g_shader_cache->GetScreenQuadVertexShader(),
|
||||
g_shader_cache->GetScreenQuadGeometryShader(), pixel_shader);
|
||||
|
||||
RasterizationState rs_state = Util::GetNoCullRasterizationState();
|
||||
rs_state.samples = m_efb_samples;
|
||||
|
@ -510,8 +510,8 @@ Texture2D* FramebufferManager::ResolveEFBDepthTexture(const VkRect2D& region)
|
|||
// Draw using resolve shader to write the minimum depth of all samples to the resolve texture.
|
||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
|
||||
m_depth_resolve_render_pass, g_object_cache->GetScreenQuadVertexShader(),
|
||||
g_object_cache->GetScreenQuadGeometryShader(), m_ps_depth_resolve);
|
||||
m_depth_resolve_render_pass, g_shader_cache->GetScreenQuadVertexShader(),
|
||||
g_shader_cache->GetScreenQuadGeometryShader(), m_ps_depth_resolve);
|
||||
draw.BeginRenderPass(m_depth_resolve_framebuffer, region);
|
||||
draw.SetPSSampler(0, m_efb_depth_texture->GetView(), g_object_cache->GetPointSampler());
|
||||
draw.SetViewportAndScissor(region.offset.x, region.offset.y, region.extent.width,
|
||||
|
@ -641,7 +641,7 @@ bool FramebufferManager::CompileConversionShaders()
|
|||
}
|
||||
)";
|
||||
|
||||
std::string header = g_object_cache->GetUtilityShaderHeader();
|
||||
std::string header = g_shader_cache->GetUtilityShaderHeader();
|
||||
DestroyConversionShaders();
|
||||
|
||||
m_ps_rgb8_to_rgba6 = Util::CompileAndCreateFragmentShader(header + RGB8_TO_RGBA6_SHADER_SOURCE);
|
||||
|
@ -698,7 +698,7 @@ bool FramebufferManager::PopulateColorReadbackTexture()
|
|||
|
||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
|
||||
m_copy_color_render_pass, g_object_cache->GetScreenQuadVertexShader(),
|
||||
m_copy_color_render_pass, g_shader_cache->GetScreenQuadVertexShader(),
|
||||
VK_NULL_HANDLE, m_copy_color_shader);
|
||||
|
||||
VkRect2D rect = {{0, 0}, {EFB_WIDTH, EFB_HEIGHT}};
|
||||
|
@ -781,7 +781,7 @@ bool FramebufferManager::PopulateDepthReadbackTexture()
|
|||
|
||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
|
||||
m_copy_depth_render_pass, g_object_cache->GetScreenQuadVertexShader(),
|
||||
m_copy_depth_render_pass, g_shader_cache->GetScreenQuadVertexShader(),
|
||||
VK_NULL_HANDLE, m_copy_depth_shader);
|
||||
|
||||
VkRect2D rect = {{0, 0}, {EFB_WIDTH, EFB_HEIGHT}};
|
||||
|
@ -956,10 +956,10 @@ bool FramebufferManager::CompileReadbackShaders()
|
|||
}
|
||||
)";
|
||||
|
||||
source = g_object_cache->GetUtilityShaderHeader() + COPY_COLOR_SHADER_SOURCE;
|
||||
source = g_shader_cache->GetUtilityShaderHeader() + COPY_COLOR_SHADER_SOURCE;
|
||||
m_copy_color_shader = Util::CompileAndCreateFragmentShader(source);
|
||||
|
||||
source = g_object_cache->GetUtilityShaderHeader() + COPY_DEPTH_SHADER_SOURCE;
|
||||
source = g_shader_cache->GetUtilityShaderHeader() + COPY_DEPTH_SHADER_SOURCE;
|
||||
m_copy_depth_shader = Util::CompileAndCreateFragmentShader(source);
|
||||
|
||||
return m_copy_color_shader != VK_NULL_HANDLE && m_copy_depth_shader != VK_NULL_HANDLE;
|
||||
|
@ -1176,7 +1176,7 @@ void FramebufferManager::DrawPokeVertices(const EFBPokeVertex* vertices, size_t
|
|||
pipeline_info.depth_stencil_state.compare_op = VK_COMPARE_OP_ALWAYS;
|
||||
}
|
||||
|
||||
VkPipeline pipeline = g_object_cache->GetPipeline(pipeline_info);
|
||||
VkPipeline pipeline = g_shader_cache->GetPipeline(pipeline_info);
|
||||
if (pipeline == VK_NULL_HANDLE)
|
||||
{
|
||||
PanicAlert("Failed to get pipeline for EFB poke draw");
|
||||
|
@ -1309,7 +1309,7 @@ bool FramebufferManager::CompilePokeShaders()
|
|||
}
|
||||
)";
|
||||
|
||||
std::string source = g_object_cache->GetUtilityShaderHeader();
|
||||
std::string source = g_shader_cache->GetUtilityShaderHeader();
|
||||
if (m_poke_primitive_topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST)
|
||||
source += "#define USE_POINT_SIZE 1\n";
|
||||
source += POKE_VERTEX_SHADER_SOURCE;
|
||||
|
@ -1319,13 +1319,13 @@ bool FramebufferManager::CompilePokeShaders()
|
|||
|
||||
if (g_vulkan_context->SupportsGeometryShaders())
|
||||
{
|
||||
source = g_object_cache->GetUtilityShaderHeader() + POKE_GEOMETRY_SHADER_SOURCE;
|
||||
source = g_shader_cache->GetUtilityShaderHeader() + POKE_GEOMETRY_SHADER_SOURCE;
|
||||
m_poke_geometry_shader = Util::CompileAndCreateGeometryShader(source);
|
||||
if (m_poke_geometry_shader == VK_NULL_HANDLE)
|
||||
return false;
|
||||
}
|
||||
|
||||
source = g_object_cache->GetUtilityShaderHeader() + POKE_PIXEL_SHADER_SOURCE;
|
||||
source = g_shader_cache->GetUtilityShaderHeader() + POKE_PIXEL_SHADER_SOURCE;
|
||||
m_poke_fragment_shader = Util::CompileAndCreateFragmentShader(source);
|
||||
if (m_poke_fragment_shader == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -27,52 +27,6 @@ class CommandBufferManager;
|
|||
class VertexFormat;
|
||||
class StreamBuffer;
|
||||
|
||||
struct PipelineInfo
|
||||
{
|
||||
// These are packed in descending order of size, to avoid any padding so that the structure
|
||||
// can be copied/compared as a single block of memory. 64-bit pointer size is assumed.
|
||||
const VertexFormat* vertex_format;
|
||||
VkPipelineLayout pipeline_layout;
|
||||
VkShaderModule vs;
|
||||
VkShaderModule gs;
|
||||
VkShaderModule ps;
|
||||
VkRenderPass render_pass;
|
||||
BlendingState blend_state;
|
||||
RasterizationState rasterization_state;
|
||||
DepthStencilState depth_stencil_state;
|
||||
VkPrimitiveTopology primitive_topology;
|
||||
};
|
||||
|
||||
struct PipelineInfoHash
|
||||
{
|
||||
std::size_t operator()(const PipelineInfo& key) const;
|
||||
};
|
||||
|
||||
bool operator==(const PipelineInfo& lhs, const PipelineInfo& rhs);
|
||||
bool operator!=(const PipelineInfo& lhs, const PipelineInfo& rhs);
|
||||
bool operator<(const PipelineInfo& lhs, const PipelineInfo& rhs);
|
||||
bool operator>(const PipelineInfo& lhs, const PipelineInfo& rhs);
|
||||
bool operator==(const SamplerState& lhs, const SamplerState& rhs);
|
||||
bool operator!=(const SamplerState& lhs, const SamplerState& rhs);
|
||||
bool operator>(const SamplerState& lhs, const SamplerState& rhs);
|
||||
bool operator<(const SamplerState& lhs, const SamplerState& rhs);
|
||||
|
||||
struct ComputePipelineInfo
|
||||
{
|
||||
VkPipelineLayout pipeline_layout;
|
||||
VkShaderModule cs;
|
||||
};
|
||||
|
||||
struct ComputePipelineInfoHash
|
||||
{
|
||||
std::size_t operator()(const ComputePipelineInfo& key) const;
|
||||
};
|
||||
|
||||
bool operator==(const ComputePipelineInfo& lhs, const ComputePipelineInfo& rhs);
|
||||
bool operator!=(const ComputePipelineInfo& lhs, const ComputePipelineInfo& rhs);
|
||||
bool operator<(const ComputePipelineInfo& lhs, const ComputePipelineInfo& rhs);
|
||||
bool operator>(const ComputePipelineInfo& lhs, const ComputePipelineInfo& rhs);
|
||||
|
||||
class ObjectCache
|
||||
{
|
||||
public:
|
||||
|
@ -103,14 +57,6 @@ public:
|
|||
return m_utility_shader_uniform_buffer.get();
|
||||
}
|
||||
|
||||
// Get utility shader header based on current config.
|
||||
std::string GetUtilityShaderHeader() const;
|
||||
|
||||
// Accesses ShaderGen shader caches
|
||||
VkShaderModule GetVertexShaderForUid(const VertexShaderUid& uid);
|
||||
VkShaderModule GetGeometryShaderForUid(const GeometryShaderUid& uid);
|
||||
VkShaderModule GetPixelShaderForUid(const PixelShaderUid& uid);
|
||||
|
||||
// Static samplers
|
||||
VkSampler GetPointSampler() const { return m_point_sampler; }
|
||||
VkSampler GetLinearSampler() const { return m_linear_sampler; }
|
||||
|
@ -119,64 +65,17 @@ public:
|
|||
// Perform at startup, create descriptor layouts, compiles all static shaders.
|
||||
bool Initialize();
|
||||
|
||||
// Creates a pipeline for the specified description. The resulting pipeline, if successful
|
||||
// is not stored anywhere, this is left up to the caller.
|
||||
VkPipeline CreatePipeline(const PipelineInfo& info);
|
||||
|
||||
// Find a pipeline by the specified description, if not found, attempts to create it.
|
||||
VkPipeline GetPipeline(const PipelineInfo& info);
|
||||
|
||||
// Find a pipeline by the specified description, if not found, attempts to create it. If this
|
||||
// resulted in a pipeline being created, the second field of the return value will be false,
|
||||
// otherwise for a cache hit it will be true.
|
||||
std::pair<VkPipeline, bool> GetPipelineWithCacheResult(const PipelineInfo& info);
|
||||
|
||||
// Creates a compute pipeline, and does not track the handle.
|
||||
VkPipeline CreateComputePipeline(const ComputePipelineInfo& info);
|
||||
|
||||
// Find a pipeline by the specified description, if not found, attempts to create it
|
||||
VkPipeline GetComputePipeline(const ComputePipelineInfo& info);
|
||||
|
||||
// Clears our pipeline cache of all objects. This is necessary when recompiling shaders,
|
||||
// as drivers are free to return the same pointer again, which means that we may end up using
|
||||
// and old pipeline object if they are not cleared first. Some stutter may be experienced
|
||||
// while our cache is rebuilt on use, but the pipeline cache object should mitigate this.
|
||||
// NOTE: Ensure that none of these objects are in use before calling.
|
||||
void ClearPipelineCache();
|
||||
|
||||
// Saves the pipeline cache to disk. Call when shutting down.
|
||||
void SavePipelineCache();
|
||||
|
||||
// Clear sampler cache, use when anisotropy mode changes
|
||||
// WARNING: Ensure none of the objects from here are in use when calling
|
||||
void ClearSamplerCache();
|
||||
|
||||
// Recompile shared shaders, call when stereo mode changes.
|
||||
void RecompileSharedShaders();
|
||||
|
||||
// Reload pipeline cache. This will destroy all pipelines.
|
||||
void ReloadShaderAndPipelineCaches();
|
||||
|
||||
// Shared shader accessors
|
||||
VkShaderModule GetScreenQuadVertexShader() const { return m_screen_quad_vertex_shader; }
|
||||
VkShaderModule GetPassthroughVertexShader() const { return m_passthrough_vertex_shader; }
|
||||
VkShaderModule GetScreenQuadGeometryShader() const { return m_screen_quad_geometry_shader; }
|
||||
VkShaderModule GetPassthroughGeometryShader() const { return m_passthrough_geometry_shader; }
|
||||
private:
|
||||
bool CreatePipelineCache();
|
||||
bool LoadPipelineCache();
|
||||
bool ValidatePipelineCache(const u8* data, size_t data_length);
|
||||
void DestroyPipelineCache();
|
||||
void LoadShaderCaches();
|
||||
void DestroyShaderCaches();
|
||||
bool CreateDescriptorSetLayouts();
|
||||
void DestroyDescriptorSetLayouts();
|
||||
bool CreatePipelineLayouts();
|
||||
void DestroyPipelineLayouts();
|
||||
bool CreateUtilityShaderVertexFormat();
|
||||
bool CreateStaticSamplers();
|
||||
bool CompileSharedShaders();
|
||||
void DestroySharedShaders();
|
||||
void DestroySamplers();
|
||||
|
||||
std::array<VkDescriptorSetLayout, NUM_DESCRIPTOR_SET_LAYOUTS> m_descriptor_set_layouts = {};
|
||||
|
@ -186,32 +85,10 @@ private:
|
|||
std::unique_ptr<StreamBuffer> m_utility_shader_vertex_buffer;
|
||||
std::unique_ptr<StreamBuffer> m_utility_shader_uniform_buffer;
|
||||
|
||||
template <typename Uid>
|
||||
struct ShaderCache
|
||||
{
|
||||
std::map<Uid, VkShaderModule> shader_map;
|
||||
LinearDiskCache<Uid, u32> disk_cache;
|
||||
};
|
||||
ShaderCache<VertexShaderUid> m_vs_cache;
|
||||
ShaderCache<GeometryShaderUid> m_gs_cache;
|
||||
ShaderCache<PixelShaderUid> m_ps_cache;
|
||||
|
||||
std::unordered_map<PipelineInfo, VkPipeline, PipelineInfoHash> m_pipeline_objects;
|
||||
std::unordered_map<ComputePipelineInfo, VkPipeline, ComputePipelineInfoHash>
|
||||
m_compute_pipeline_objects;
|
||||
VkPipelineCache m_pipeline_cache = VK_NULL_HANDLE;
|
||||
std::string m_pipeline_cache_filename;
|
||||
|
||||
VkSampler m_point_sampler = VK_NULL_HANDLE;
|
||||
VkSampler m_linear_sampler = VK_NULL_HANDLE;
|
||||
|
||||
std::map<SamplerState, VkSampler> m_sampler_cache;
|
||||
|
||||
// Utility/shared shaders
|
||||
VkShaderModule m_screen_quad_vertex_shader = VK_NULL_HANDLE;
|
||||
VkShaderModule m_passthrough_vertex_shader = VK_NULL_HANDLE;
|
||||
VkShaderModule m_screen_quad_geometry_shader = VK_NULL_HANDLE;
|
||||
VkShaderModule m_passthrough_geometry_shader = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<ObjectCache> g_object_cache;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
||||
#include "VideoBackends/Vulkan/ObjectCache.h"
|
||||
#include "VideoBackends/Vulkan/ShaderCache.h"
|
||||
#include "VideoBackends/Vulkan/Texture2D.h"
|
||||
#include "VideoBackends/Vulkan/Util.h"
|
||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||
|
@ -43,12 +44,12 @@ void VulkanPostProcessing::BlitFromTexture(const TargetRectangle& dst, const Tar
|
|||
{
|
||||
// If the source layer is negative we simply copy all available layers.
|
||||
VkShaderModule geometry_shader =
|
||||
src_layer < 0 ? g_object_cache->GetPassthroughGeometryShader() : VK_NULL_HANDLE;
|
||||
src_layer < 0 ? g_shader_cache->GetPassthroughGeometryShader() : VK_NULL_HANDLE;
|
||||
VkShaderModule fragment_shader =
|
||||
m_fragment_shader != VK_NULL_HANDLE ? m_fragment_shader : m_default_fragment_shader;
|
||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), render_pass,
|
||||
g_object_cache->GetPassthroughVertexShader(), geometry_shader,
|
||||
g_shader_cache->GetPassthroughVertexShader(), geometry_shader,
|
||||
fragment_shader);
|
||||
|
||||
// Source is always bound.
|
||||
|
@ -240,7 +241,7 @@ bool VulkanPostProcessing::RecompileShader()
|
|||
if (m_fragment_shader != VK_NULL_HANDLE)
|
||||
{
|
||||
g_command_buffer_mgr->WaitForGPUIdle();
|
||||
g_object_cache->ClearPipelineCache();
|
||||
g_shader_cache->ClearPipelineCache();
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_fragment_shader, nullptr);
|
||||
m_fragment_shader = VK_NULL_HANDLE;
|
||||
}
|
||||
|
|
|
@ -462,8 +462,8 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha
|
|||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
|
||||
FramebufferManager::GetInstance()->GetEFBLoadRenderPass(),
|
||||
g_object_cache->GetPassthroughVertexShader(),
|
||||
g_object_cache->GetPassthroughGeometryShader(), m_clear_fragment_shader);
|
||||
g_shader_cache->GetPassthroughVertexShader(),
|
||||
g_shader_cache->GetPassthroughGeometryShader(), m_clear_fragment_shader);
|
||||
|
||||
draw.SetRasterizationState(rs_state);
|
||||
draw.SetDepthStencilState(depth_state);
|
||||
|
@ -1168,8 +1168,8 @@ void Renderer::CheckForConfigChanges()
|
|||
BindEFBToStateTracker();
|
||||
RecompileShaders();
|
||||
FramebufferManager::GetInstance()->RecompileShaders();
|
||||
g_object_cache->ReloadShaderAndPipelineCaches();
|
||||
g_object_cache->RecompileSharedShaders();
|
||||
g_shader_cache->ReloadShaderAndPipelineCaches();
|
||||
g_shader_cache->RecompileSharedShaders();
|
||||
StateTracker::GetInstance()->InvalidateShaderPointers();
|
||||
StateTracker::GetInstance()->ReloadPipelineUIDCache();
|
||||
}
|
||||
|
@ -1500,7 +1500,7 @@ bool Renderer::CompileShaders()
|
|||
|
||||
)";
|
||||
|
||||
std::string source = g_object_cache->GetUtilityShaderHeader() + CLEAR_FRAGMENT_SHADER_SOURCE;
|
||||
std::string source = g_shader_cache->GetUtilityShaderHeader() + CLEAR_FRAGMENT_SHADER_SOURCE;
|
||||
m_clear_fragment_shader = Util::CompileAndCreateFragmentShader(source);
|
||||
|
||||
return m_clear_fragment_shader != VK_NULL_HANDLE;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,172 @@
|
|||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/LinearDiskCache.h"
|
||||
|
||||
#include "VideoBackends/Vulkan/Constants.h"
|
||||
#include "VideoBackends/Vulkan/ObjectCache.h"
|
||||
|
||||
#include "VideoCommon/GeometryShaderGen.h"
|
||||
#include "VideoCommon/PixelShaderGen.h"
|
||||
#include "VideoCommon/RenderState.h"
|
||||
#include "VideoCommon/VertexShaderGen.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class CommandBufferManager;
|
||||
class VertexFormat;
|
||||
class StreamBuffer;
|
||||
|
||||
class CommandBufferManager;
|
||||
class VertexFormat;
|
||||
class StreamBuffer;
|
||||
|
||||
struct PipelineInfo
|
||||
{
|
||||
// These are packed in descending order of size, to avoid any padding so that the structure
|
||||
// can be copied/compared as a single block of memory. 64-bit pointer size is assumed.
|
||||
const VertexFormat* vertex_format;
|
||||
VkPipelineLayout pipeline_layout;
|
||||
VkShaderModule vs;
|
||||
VkShaderModule gs;
|
||||
VkShaderModule ps;
|
||||
VkRenderPass render_pass;
|
||||
BlendingState blend_state;
|
||||
RasterizationState rasterization_state;
|
||||
DepthStencilState depth_stencil_state;
|
||||
VkPrimitiveTopology primitive_topology;
|
||||
};
|
||||
|
||||
struct PipelineInfoHash
|
||||
{
|
||||
std::size_t operator()(const PipelineInfo& key) const;
|
||||
};
|
||||
|
||||
bool operator==(const PipelineInfo& lhs, const PipelineInfo& rhs);
|
||||
bool operator!=(const PipelineInfo& lhs, const PipelineInfo& rhs);
|
||||
bool operator<(const PipelineInfo& lhs, const PipelineInfo& rhs);
|
||||
bool operator>(const PipelineInfo& lhs, const PipelineInfo& rhs);
|
||||
bool operator==(const SamplerState& lhs, const SamplerState& rhs);
|
||||
bool operator!=(const SamplerState& lhs, const SamplerState& rhs);
|
||||
bool operator>(const SamplerState& lhs, const SamplerState& rhs);
|
||||
bool operator<(const SamplerState& lhs, const SamplerState& rhs);
|
||||
|
||||
struct ComputePipelineInfo
|
||||
{
|
||||
VkPipelineLayout pipeline_layout;
|
||||
VkShaderModule cs;
|
||||
};
|
||||
|
||||
struct ComputePipelineInfoHash
|
||||
{
|
||||
std::size_t operator()(const ComputePipelineInfo& key) const;
|
||||
};
|
||||
|
||||
bool operator==(const ComputePipelineInfo& lhs, const ComputePipelineInfo& rhs);
|
||||
bool operator!=(const ComputePipelineInfo& lhs, const ComputePipelineInfo& rhs);
|
||||
bool operator<(const ComputePipelineInfo& lhs, const ComputePipelineInfo& rhs);
|
||||
bool operator>(const ComputePipelineInfo& lhs, const ComputePipelineInfo& rhs);
|
||||
|
||||
class ShaderCache
|
||||
{
|
||||
public:
|
||||
ShaderCache();
|
||||
~ShaderCache();
|
||||
|
||||
// Get utility shader header based on current config.
|
||||
std::string GetUtilityShaderHeader() const;
|
||||
|
||||
// Accesses ShaderGen shader caches
|
||||
VkShaderModule GetVertexShaderForUid(const VertexShaderUid& uid);
|
||||
VkShaderModule GetGeometryShaderForUid(const GeometryShaderUid& uid);
|
||||
VkShaderModule GetPixelShaderForUid(const PixelShaderUid& uid);
|
||||
|
||||
// Perform at startup, create descriptor layouts, compiles all static shaders.
|
||||
bool Initialize();
|
||||
|
||||
// Creates a pipeline for the specified description. The resulting pipeline, if successful
|
||||
// is not stored anywhere, this is left up to the caller.
|
||||
VkPipeline CreatePipeline(const PipelineInfo& info);
|
||||
|
||||
// Find a pipeline by the specified description, if not found, attempts to create it.
|
||||
VkPipeline GetPipeline(const PipelineInfo& info);
|
||||
|
||||
// Find a pipeline by the specified description, if not found, attempts to create it. If this
|
||||
// resulted in a pipeline being created, the second field of the return value will be false,
|
||||
// otherwise for a cache hit it will be true.
|
||||
std::pair<VkPipeline, bool> GetPipelineWithCacheResult(const PipelineInfo& info);
|
||||
|
||||
// Creates a compute pipeline, and does not track the handle.
|
||||
VkPipeline CreateComputePipeline(const ComputePipelineInfo& info);
|
||||
|
||||
// Find a pipeline by the specified description, if not found, attempts to create it
|
||||
VkPipeline GetComputePipeline(const ComputePipelineInfo& info);
|
||||
|
||||
// Clears our pipeline cache of all objects. This is necessary when recompiling shaders,
|
||||
// as drivers are free to return the same pointer again, which means that we may end up using
|
||||
// and old pipeline object if they are not cleared first. Some stutter may be experienced
|
||||
// while our cache is rebuilt on use, but the pipeline cache object should mitigate this.
|
||||
// NOTE: Ensure that none of these objects are in use before calling.
|
||||
void ClearPipelineCache();
|
||||
|
||||
// Saves the pipeline cache to disk. Call when shutting down.
|
||||
void SavePipelineCache();
|
||||
|
||||
// Recompile shared shaders, call when stereo mode changes.
|
||||
void RecompileSharedShaders();
|
||||
|
||||
// Reload pipeline cache. This will destroy all pipelines.
|
||||
void ReloadShaderAndPipelineCaches();
|
||||
|
||||
// Shared shader accessors
|
||||
VkShaderModule GetScreenQuadVertexShader() const { return m_screen_quad_vertex_shader; }
|
||||
VkShaderModule GetPassthroughVertexShader() const { return m_passthrough_vertex_shader; }
|
||||
VkShaderModule GetScreenQuadGeometryShader() const { return m_screen_quad_geometry_shader; }
|
||||
VkShaderModule GetPassthroughGeometryShader() const { return m_passthrough_geometry_shader; }
|
||||
private:
|
||||
bool CreatePipelineCache();
|
||||
bool LoadPipelineCache();
|
||||
bool ValidatePipelineCache(const u8* data, size_t data_length);
|
||||
void DestroyPipelineCache();
|
||||
void LoadShaderCaches();
|
||||
void DestroyShaderCaches();
|
||||
bool CompileSharedShaders();
|
||||
void DestroySharedShaders();
|
||||
|
||||
template <typename Uid>
|
||||
struct ShaderModuleCache
|
||||
{
|
||||
std::map<Uid, VkShaderModule> shader_map;
|
||||
LinearDiskCache<Uid, u32> disk_cache;
|
||||
};
|
||||
ShaderModuleCache<VertexShaderUid> m_vs_cache;
|
||||
ShaderModuleCache<GeometryShaderUid> m_gs_cache;
|
||||
ShaderModuleCache<PixelShaderUid> m_ps_cache;
|
||||
|
||||
std::unordered_map<PipelineInfo, VkPipeline, PipelineInfoHash> m_pipeline_objects;
|
||||
std::unordered_map<ComputePipelineInfo, VkPipeline, ComputePipelineInfoHash>
|
||||
m_compute_pipeline_objects;
|
||||
VkPipelineCache m_pipeline_cache = VK_NULL_HANDLE;
|
||||
std::string m_pipeline_cache_filename;
|
||||
|
||||
// Utility/shared shaders
|
||||
VkShaderModule m_screen_quad_vertex_shader = VK_NULL_HANDLE;
|
||||
VkShaderModule m_passthrough_vertex_shader = VK_NULL_HANDLE;
|
||||
VkShaderModule m_screen_quad_geometry_shader = VK_NULL_HANDLE;
|
||||
VkShaderModule m_passthrough_geometry_shader = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<ShaderCache> g_shader_cache;
|
||||
|
||||
} // namespace Vulkan
|
|
@ -3,6 +3,7 @@
|
|||
// Refer to the license.txt file included.
|
||||
|
||||
#include "VideoBackends/Vulkan/ShaderCompiler.h"
|
||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
@ -37,6 +38,11 @@ static bool CompileShaderToSPV(SPIRVCodeVector* out_code, EShLanguage stage,
|
|||
const char* stage_filename, const char* source_code,
|
||||
size_t source_code_length, const char* header, size_t header_length);
|
||||
|
||||
// Copy GLSL source code to a SPIRVCodeVector, for use with VK_NV_glsl_shader.
|
||||
static void CopyGLSLToSPVVector(SPIRVCodeVector* out_code, const char* stage_filename,
|
||||
const char* source_code, size_t source_code_length,
|
||||
const char* header, size_t header_length);
|
||||
|
||||
// Regarding the UBO bind points, we subtract one from the binding index because
|
||||
// the OpenGL backend requires UBO #0 for non-block uniforms (at least on NV).
|
||||
// This allows us to share the same shaders but use bind point #0 in the Vulkan
|
||||
|
@ -219,6 +225,42 @@ bool CompileShaderToSPV(SPIRVCodeVector* out_code, EShLanguage stage, const char
|
|||
return true;
|
||||
}
|
||||
|
||||
void CopyGLSLToSPVVector(SPIRVCodeVector* out_code, const char* stage_filename,
|
||||
const char* source_code, size_t source_code_length, const char* header,
|
||||
size_t header_length)
|
||||
{
|
||||
std::string full_source_code;
|
||||
if (header_length > 0)
|
||||
{
|
||||
full_source_code.reserve(header_length + source_code_length);
|
||||
full_source_code.append(header, header_length);
|
||||
full_source_code.append(source_code, source_code_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
full_source_code.append(source_code, source_code_length);
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.iLog & CONF_SAVESHADERS)
|
||||
{
|
||||
static int counter = 0;
|
||||
std::string filename = StringFromFormat("%s%s_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(),
|
||||
stage_filename, counter++);
|
||||
|
||||
std::ofstream stream;
|
||||
File::OpenFStream(stream, filename, std::ios_base::out);
|
||||
if (stream.good())
|
||||
stream << full_source_code << std::endl;
|
||||
}
|
||||
|
||||
size_t padding = full_source_code.size() % 4;
|
||||
if (padding != 0)
|
||||
full_source_code.append(4 - padding, '\n');
|
||||
|
||||
out_code->resize(full_source_code.size() / 4);
|
||||
std::memcpy(out_code->data(), full_source_code.c_str(), full_source_code.size());
|
||||
}
|
||||
|
||||
bool InitializeGlslang()
|
||||
{
|
||||
static bool glslang_initialized = false;
|
||||
|
@ -338,29 +380,57 @@ const TBuiltInResource* GetCompilerResourceLimits()
|
|||
}
|
||||
|
||||
bool CompileVertexShader(SPIRVCodeVector* out_code, const char* source_code,
|
||||
size_t source_code_length, bool prepend_header)
|
||||
size_t source_code_length)
|
||||
{
|
||||
if (g_vulkan_context->SupportsNVGLSLExtension())
|
||||
{
|
||||
CopyGLSLToSPVVector(out_code, "vs", source_code, source_code_length, SHADER_HEADER,
|
||||
sizeof(SHADER_HEADER) - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return CompileShaderToSPV(out_code, EShLangVertex, "vs", source_code, source_code_length,
|
||||
SHADER_HEADER, sizeof(SHADER_HEADER) - 1);
|
||||
}
|
||||
|
||||
bool CompileGeometryShader(SPIRVCodeVector* out_code, const char* source_code,
|
||||
size_t source_code_length, bool prepend_header)
|
||||
size_t source_code_length)
|
||||
{
|
||||
if (g_vulkan_context->SupportsNVGLSLExtension())
|
||||
{
|
||||
CopyGLSLToSPVVector(out_code, "gs", source_code, source_code_length, SHADER_HEADER,
|
||||
sizeof(SHADER_HEADER) - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return CompileShaderToSPV(out_code, EShLangGeometry, "gs", source_code, source_code_length,
|
||||
SHADER_HEADER, sizeof(SHADER_HEADER) - 1);
|
||||
}
|
||||
|
||||
bool CompileFragmentShader(SPIRVCodeVector* out_code, const char* source_code,
|
||||
size_t source_code_length, bool prepend_header)
|
||||
size_t source_code_length)
|
||||
{
|
||||
if (g_vulkan_context->SupportsNVGLSLExtension())
|
||||
{
|
||||
CopyGLSLToSPVVector(out_code, "ps", source_code, source_code_length, SHADER_HEADER,
|
||||
sizeof(SHADER_HEADER) - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return CompileShaderToSPV(out_code, EShLangFragment, "ps", source_code, source_code_length,
|
||||
SHADER_HEADER, sizeof(SHADER_HEADER) - 1);
|
||||
}
|
||||
|
||||
bool CompileComputeShader(SPIRVCodeVector* out_code, const char* source_code,
|
||||
size_t source_code_length, bool prepend_header)
|
||||
size_t source_code_length)
|
||||
{
|
||||
if (g_vulkan_context->SupportsNVGLSLExtension())
|
||||
{
|
||||
CopyGLSLToSPVVector(out_code, "cs", source_code, source_code_length, COMPUTE_SHADER_HEADER,
|
||||
sizeof(COMPUTE_SHADER_HEADER) - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return CompileShaderToSPV(out_code, EShLangCompute, "cs", source_code, source_code_length,
|
||||
COMPUTE_SHADER_HEADER, sizeof(COMPUTE_SHADER_HEADER) - 1);
|
||||
}
|
||||
|
|
|
@ -19,19 +19,19 @@ using SPIRVCodeVector = std::vector<SPIRVCodeType>;
|
|||
|
||||
// Compile a vertex shader to SPIR-V.
|
||||
bool CompileVertexShader(SPIRVCodeVector* out_code, const char* source_code,
|
||||
size_t source_code_length, bool prepend_header = true);
|
||||
size_t source_code_length);
|
||||
|
||||
// Compile a geometry shader to SPIR-V.
|
||||
bool CompileGeometryShader(SPIRVCodeVector* out_code, const char* source_code,
|
||||
size_t source_code_length, bool prepend_header = true);
|
||||
size_t source_code_length);
|
||||
|
||||
// Compile a fragment shader to SPIR-V.
|
||||
bool CompileFragmentShader(SPIRVCodeVector* out_code, const char* source_code,
|
||||
size_t source_code_length, bool prepend_header = true);
|
||||
size_t source_code_length);
|
||||
|
||||
// Compile a compute shader to SPIR-V.
|
||||
bool CompileComputeShader(SPIRVCodeVector* out_code, const char* source_code,
|
||||
size_t source_code_length, bool prepend_header = true);
|
||||
size_t source_code_length);
|
||||
|
||||
} // namespace ShaderCompiler
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "VideoBackends/Vulkan/Constants.h"
|
||||
#include "VideoBackends/Vulkan/FramebufferManager.h"
|
||||
#include "VideoBackends/Vulkan/ObjectCache.h"
|
||||
#include "VideoBackends/Vulkan/ShaderCache.h"
|
||||
#include "VideoBackends/Vulkan/StreamBuffer.h"
|
||||
#include "VideoBackends/Vulkan/Util.h"
|
||||
#include "VideoBackends/Vulkan/VertexFormat.h"
|
||||
|
@ -181,7 +182,7 @@ bool StateTracker::PrecachePipelineUID(const SerializedPipelineUID& uid)
|
|||
pinfo.pipeline_layout = uid.ps_uid.GetUidData()->bounding_box ?
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX) :
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
|
||||
pinfo.vs = g_object_cache->GetVertexShaderForUid(uid.vs_uid);
|
||||
pinfo.vs = g_shader_cache->GetVertexShaderForUid(uid.vs_uid);
|
||||
if (pinfo.vs == VK_NULL_HANDLE)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Failed to get vertex shader from cached UID.");
|
||||
|
@ -189,14 +190,14 @@ bool StateTracker::PrecachePipelineUID(const SerializedPipelineUID& uid)
|
|||
}
|
||||
if (g_vulkan_context->SupportsGeometryShaders() && !uid.gs_uid.GetUidData()->IsPassthrough())
|
||||
{
|
||||
pinfo.gs = g_object_cache->GetGeometryShaderForUid(uid.gs_uid);
|
||||
pinfo.gs = g_shader_cache->GetGeometryShaderForUid(uid.gs_uid);
|
||||
if (pinfo.gs == VK_NULL_HANDLE)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Failed to get geometry shader from cached UID.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
pinfo.ps = g_object_cache->GetPixelShaderForUid(uid.ps_uid);
|
||||
pinfo.ps = g_shader_cache->GetPixelShaderForUid(uid.ps_uid);
|
||||
if (pinfo.ps == VK_NULL_HANDLE)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Failed to get pixel shader from cached UID.");
|
||||
|
@ -208,7 +209,7 @@ bool StateTracker::PrecachePipelineUID(const SerializedPipelineUID& uid)
|
|||
pinfo.blend_state.hex = uid.blend_state_bits;
|
||||
pinfo.primitive_topology = uid.primitive_topology;
|
||||
|
||||
VkPipeline pipeline = g_object_cache->GetPipeline(pinfo);
|
||||
VkPipeline pipeline = g_shader_cache->GetPipeline(pinfo);
|
||||
if (pipeline == VK_NULL_HANDLE)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Failed to get pipeline from cached UID.");
|
||||
|
@ -327,7 +328,7 @@ bool StateTracker::CheckForShaderChanges(u32 gx_primitive_type)
|
|||
|
||||
if (vs_uid != m_vs_uid)
|
||||
{
|
||||
m_pipeline_state.vs = g_object_cache->GetVertexShaderForUid(vs_uid);
|
||||
m_pipeline_state.vs = g_shader_cache->GetVertexShaderForUid(vs_uid);
|
||||
m_vs_uid = vs_uid;
|
||||
changed = true;
|
||||
}
|
||||
|
@ -340,7 +341,7 @@ bool StateTracker::CheckForShaderChanges(u32 gx_primitive_type)
|
|||
if (gs_uid.GetUidData()->IsPassthrough())
|
||||
m_pipeline_state.gs = VK_NULL_HANDLE;
|
||||
else
|
||||
m_pipeline_state.gs = g_object_cache->GetGeometryShaderForUid(gs_uid);
|
||||
m_pipeline_state.gs = g_shader_cache->GetGeometryShaderForUid(gs_uid);
|
||||
|
||||
m_gs_uid = gs_uid;
|
||||
changed = true;
|
||||
|
@ -349,7 +350,7 @@ bool StateTracker::CheckForShaderChanges(u32 gx_primitive_type)
|
|||
|
||||
if (ps_uid != m_ps_uid)
|
||||
{
|
||||
m_pipeline_state.ps = g_object_cache->GetPixelShaderForUid(ps_uid);
|
||||
m_pipeline_state.ps = g_shader_cache->GetPixelShaderForUid(ps_uid);
|
||||
m_ps_uid = ps_uid;
|
||||
changed = true;
|
||||
}
|
||||
|
@ -887,7 +888,7 @@ void StateTracker::EndClearRenderPass()
|
|||
|
||||
VkPipeline StateTracker::GetPipelineAndCacheUID(const PipelineInfo& info)
|
||||
{
|
||||
auto result = g_object_cache->GetPipelineWithCacheResult(info);
|
||||
auto result = g_shader_cache->GetPipelineWithCacheResult(info);
|
||||
|
||||
// Add to the UID cache if it is a new pipeline.
|
||||
if (!result.second && g_ActiveConfig.bShaderCache)
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include "Common/CommonTypes.h"
|
||||
#include "Common/LinearDiskCache.h"
|
||||
#include "VideoBackends/Vulkan/Constants.h"
|
||||
#include "VideoBackends/Vulkan/ObjectCache.h"
|
||||
#include "VideoBackends/Vulkan/ShaderCache.h"
|
||||
#include "VideoCommon/GeometryShaderGen.h"
|
||||
#include "VideoCommon/NativeVertexFormat.h"
|
||||
#include "VideoCommon/PixelShaderGen.h"
|
||||
|
|
|
@ -299,7 +299,7 @@ bool TextureCache::CompileShaders()
|
|||
}
|
||||
)";
|
||||
|
||||
std::string header = g_object_cache->GetUtilityShaderHeader();
|
||||
std::string header = g_shader_cache->GetUtilityShaderHeader();
|
||||
std::string source;
|
||||
|
||||
source = header + COPY_SHADER_SOURCE;
|
||||
|
@ -385,8 +385,8 @@ void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
|
|||
|
||||
UtilityShaderDraw draw(command_buffer,
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT),
|
||||
m_render_pass, g_object_cache->GetPassthroughVertexShader(),
|
||||
g_object_cache->GetPassthroughGeometryShader(),
|
||||
m_render_pass, g_shader_cache->GetPassthroughVertexShader(),
|
||||
g_shader_cache->GetPassthroughGeometryShader(),
|
||||
is_depth_copy ? m_efb_depth_to_tex_shader : m_efb_color_to_tex_shader);
|
||||
|
||||
draw.SetPushConstants(colmat, (is_depth_copy ? sizeof(float) * 20 : sizeof(float) * 28));
|
||||
|
|
|
@ -200,7 +200,7 @@ void TextureConverter::ConvertTexture(TextureCacheBase::TCacheEntry* dst_entry,
|
|||
// Bind and draw to the destination.
|
||||
UtilityShaderDraw draw(command_buffer,
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION),
|
||||
render_pass, g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE,
|
||||
render_pass, g_shader_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE,
|
||||
m_palette_conversion_shaders[palette_format]);
|
||||
|
||||
VkRect2D region = {{0, 0}, {dst_entry->GetWidth(), dst_entry->GetHeight()}};
|
||||
|
@ -240,7 +240,7 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p
|
|||
|
||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT),
|
||||
m_encoding_render_pass, g_object_cache->GetScreenQuadVertexShader(),
|
||||
m_encoding_render_pass, g_shader_cache->GetScreenQuadVertexShader(),
|
||||
VK_NULL_HANDLE, shader);
|
||||
|
||||
// Uniform - int4 of left,top,native_width,scale
|
||||
|
@ -299,7 +299,7 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u
|
|||
u32 output_width = dst_width / 2;
|
||||
UtilityShaderDraw draw(command_buffer,
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
|
||||
m_encoding_render_pass, g_object_cache->GetPassthroughVertexShader(),
|
||||
m_encoding_render_pass, g_shader_cache->GetPassthroughVertexShader(),
|
||||
VK_NULL_HANDLE, m_rgb_to_yuyv_shader);
|
||||
VkRect2D region = {{0, 0}, {output_width, dst_height}};
|
||||
draw.BeginRenderPass(m_encoding_render_framebuffer, region);
|
||||
|
@ -376,7 +376,7 @@ void TextureConverter::DecodeYUYVTextureFromMemory(VKTexture* dst_texture, const
|
|||
// Convert from the YUYV data now in the intermediate texture to RGBA in the destination.
|
||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION),
|
||||
m_encoding_render_pass, g_object_cache->GetScreenQuadVertexShader(),
|
||||
m_encoding_render_pass, g_shader_cache->GetScreenQuadVertexShader(),
|
||||
VK_NULL_HANDLE, m_yuyv_to_rgb_shader);
|
||||
VkRect2D region = {{0, 0}, {src_width, src_height}};
|
||||
draw.BeginRenderPass(dst_texture->GetFramebuffer(), region);
|
||||
|
@ -408,7 +408,7 @@ bool TextureConverter::SupportsTextureDecoding(TextureFormat format, TlutFormat
|
|||
std::string shader_source =
|
||||
TextureConversionShader::GenerateDecodingShader(format, palette_format, APIType::Vulkan);
|
||||
|
||||
pipeline.compute_shader = Util::CompileAndCreateComputeShader(shader_source, true);
|
||||
pipeline.compute_shader = Util::CompileAndCreateComputeShader(shader_source);
|
||||
if (pipeline.compute_shader == VK_NULL_HANDLE)
|
||||
{
|
||||
m_decoding_pipelines.emplace(key, pipeline);
|
||||
|
@ -782,8 +782,19 @@ bool TextureConverter::CreateDecodingTexture()
|
|||
m_decoding_texture = Texture2D::Create(
|
||||
DECODING_TEXTURE_WIDTH, DECODING_TEXTURE_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_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
|
||||
return static_cast<bool>(m_decoding_texture);
|
||||
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
|
||||
if (!m_decoding_texture)
|
||||
return false;
|
||||
|
||||
VkClearColorValue clear_value = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||
VkImageSubresourceRange clear_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||
m_decoding_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
vkCmdClearColorImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(),
|
||||
m_decoding_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
&clear_value, 1, &clear_range);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureConverter::CompileYUYVConversionShaders()
|
||||
|
@ -838,7 +849,7 @@ bool TextureConverter::CompileYUYVConversionShaders()
|
|||
}
|
||||
)";
|
||||
|
||||
std::string header = g_object_cache->GetUtilityShaderHeader();
|
||||
std::string header = g_shader_cache->GetUtilityShaderHeader();
|
||||
std::string source = header + RGB_TO_YUYV_SHADER_SOURCE;
|
||||
m_rgb_to_yuyv_shader = Util::CompileAndCreateFragmentShader(source);
|
||||
source = header + YUYV_TO_RGB_SHADER_SOURCE;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
||||
#include "VideoBackends/Vulkan/ObjectCache.h"
|
||||
#include "VideoBackends/Vulkan/ShaderCache.h"
|
||||
#include "VideoBackends/Vulkan/ShaderCompiler.h"
|
||||
#include "VideoBackends/Vulkan/StateTracker.h"
|
||||
#include "VideoBackends/Vulkan/StreamBuffer.h"
|
||||
|
@ -275,50 +276,38 @@ VkShaderModule CreateShaderModule(const u32* spv, size_t spv_word_count)
|
|||
return module;
|
||||
}
|
||||
|
||||
VkShaderModule CompileAndCreateVertexShader(const std::string& source_code, bool prepend_header)
|
||||
VkShaderModule CompileAndCreateVertexShader(const std::string& source_code)
|
||||
{
|
||||
ShaderCompiler::SPIRVCodeVector code;
|
||||
if (!ShaderCompiler::CompileVertexShader(&code, source_code.c_str(), source_code.length(),
|
||||
prepend_header))
|
||||
{
|
||||
if (!ShaderCompiler::CompileVertexShader(&code, source_code.c_str(), source_code.length()))
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return CreateShaderModule(code.data(), code.size());
|
||||
}
|
||||
|
||||
VkShaderModule CompileAndCreateGeometryShader(const std::string& source_code, bool prepend_header)
|
||||
VkShaderModule CompileAndCreateGeometryShader(const std::string& source_code)
|
||||
{
|
||||
ShaderCompiler::SPIRVCodeVector code;
|
||||
if (!ShaderCompiler::CompileGeometryShader(&code, source_code.c_str(), source_code.length(),
|
||||
prepend_header))
|
||||
{
|
||||
if (!ShaderCompiler::CompileGeometryShader(&code, source_code.c_str(), source_code.length()))
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return CreateShaderModule(code.data(), code.size());
|
||||
}
|
||||
|
||||
VkShaderModule CompileAndCreateFragmentShader(const std::string& source_code, bool prepend_header)
|
||||
VkShaderModule CompileAndCreateFragmentShader(const std::string& source_code)
|
||||
{
|
||||
ShaderCompiler::SPIRVCodeVector code;
|
||||
if (!ShaderCompiler::CompileFragmentShader(&code, source_code.c_str(), source_code.length(),
|
||||
prepend_header))
|
||||
{
|
||||
if (!ShaderCompiler::CompileFragmentShader(&code, source_code.c_str(), source_code.length()))
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return CreateShaderModule(code.data(), code.size());
|
||||
}
|
||||
|
||||
VkShaderModule CompileAndCreateComputeShader(const std::string& source_code, bool prepend_header)
|
||||
VkShaderModule CompileAndCreateComputeShader(const std::string& source_code)
|
||||
{
|
||||
ShaderCompiler::SPIRVCodeVector code;
|
||||
if (!ShaderCompiler::CompileComputeShader(&code, source_code.c_str(), source_code.length(),
|
||||
prepend_header))
|
||||
{
|
||||
if (!ShaderCompiler::CompileComputeShader(&code, source_code.c_str(), source_code.length()))
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return CreateShaderModule(code.data(), code.size());
|
||||
}
|
||||
|
@ -732,7 +721,7 @@ void UtilityShaderDraw::BindDescriptors()
|
|||
|
||||
bool UtilityShaderDraw::BindPipeline()
|
||||
{
|
||||
VkPipeline pipeline = g_object_cache->GetPipeline(m_pipeline_info);
|
||||
VkPipeline pipeline = g_shader_cache->GetPipeline(m_pipeline_info);
|
||||
if (pipeline == VK_NULL_HANDLE)
|
||||
{
|
||||
PanicAlert("Failed to get pipeline for backend shader draw");
|
||||
|
@ -885,7 +874,7 @@ void ComputeShaderDispatcher::BindDescriptors()
|
|||
|
||||
bool ComputeShaderDispatcher::BindPipeline()
|
||||
{
|
||||
VkPipeline pipeline = g_object_cache->GetComputePipeline(m_pipeline_info);
|
||||
VkPipeline pipeline = g_shader_cache->GetComputePipeline(m_pipeline_info);
|
||||
if (pipeline == VK_NULL_HANDLE)
|
||||
{
|
||||
PanicAlert("Failed to get pipeline for backend compute dispatch");
|
||||
|
|
|
@ -10,13 +10,13 @@
|
|||
#include "Common/CommonTypes.h"
|
||||
#include "VideoBackends/Vulkan/Constants.h"
|
||||
#include "VideoBackends/Vulkan/ObjectCache.h"
|
||||
#include "VideoBackends/Vulkan/ShaderCache.h"
|
||||
#include "VideoCommon/RenderState.h"
|
||||
#include "VideoCommon/TextureConfig.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class CommandBufferManager;
|
||||
class ObjectCache;
|
||||
class StateTracker;
|
||||
|
||||
namespace Util
|
||||
|
@ -61,20 +61,16 @@ void ExecuteCurrentCommandsAndRestoreState(bool execute_off_thread,
|
|||
VkShaderModule CreateShaderModule(const u32* spv, size_t spv_word_count);
|
||||
|
||||
// Compile a vertex shader and create a shader module, discarding the intermediate SPIR-V.
|
||||
VkShaderModule CompileAndCreateVertexShader(const std::string& source_code,
|
||||
bool prepend_header = true);
|
||||
VkShaderModule CompileAndCreateVertexShader(const std::string& source_code);
|
||||
|
||||
// Compile a geometry shader and create a shader module, discarding the intermediate SPIR-V.
|
||||
VkShaderModule CompileAndCreateGeometryShader(const std::string& source_code,
|
||||
bool prepend_header = true);
|
||||
VkShaderModule CompileAndCreateGeometryShader(const std::string& source_code);
|
||||
|
||||
// Compile a fragment shader and create a shader module, discarding the intermediate SPIR-V.
|
||||
VkShaderModule CompileAndCreateFragmentShader(const std::string& source_code,
|
||||
bool prepend_header = true);
|
||||
VkShaderModule CompileAndCreateFragmentShader(const std::string& source_code);
|
||||
|
||||
// Compile a compute shader and create a shader module, discarding the intermediate SPIR-V.
|
||||
VkShaderModule CompileAndCreateComputeShader(const std::string& source_code,
|
||||
bool prepend_header = true);
|
||||
VkShaderModule CompileAndCreateComputeShader(const std::string& source_code);
|
||||
}
|
||||
|
||||
// Utility shader vertex format
|
||||
|
|
|
@ -225,8 +225,8 @@ void VKTexture::ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
|
|||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
|
||||
TextureCache::GetInstance()->GetTextureCopyRenderPass(),
|
||||
g_object_cache->GetPassthroughVertexShader(),
|
||||
g_object_cache->GetPassthroughGeometryShader(),
|
||||
g_shader_cache->GetPassthroughVertexShader(),
|
||||
g_shader_cache->GetPassthroughGeometryShader(),
|
||||
TextureCache::GetInstance()->GetCopyShader());
|
||||
|
||||
VkRect2D region = {
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
<ClCompile Include="FramebufferManager.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="PostProcessing.cpp" />
|
||||
<ClCompile Include="ShaderCache.cpp" />
|
||||
<ClCompile Include="TextureConverter.cpp" />
|
||||
<ClCompile Include="PerfQuery.cpp" />
|
||||
<ClCompile Include="RasterFont.cpp" />
|
||||
|
@ -67,6 +68,7 @@
|
|||
<ClInclude Include="FramebufferManager.h" />
|
||||
<ClInclude Include="Constants.h" />
|
||||
<ClInclude Include="PostProcessing.h" />
|
||||
<ClInclude Include="ShaderCache.h" />
|
||||
<ClInclude Include="TextureConverter.h" />
|
||||
<ClInclude Include="RasterFont.h" />
|
||||
<ClInclude Include="StagingBuffer.h" />
|
||||
|
|
|
@ -397,7 +397,8 @@ bool VulkanContext::SelectDeviceExtensions(ExtensionList* extension_list, bool e
|
|||
for (const auto& extension_properties : available_extension_list)
|
||||
INFO_LOG(VIDEO, "Available extension: %s", extension_properties.extensionName);
|
||||
|
||||
auto CheckForExtension = [&](const char* name, bool required) -> bool {
|
||||
auto CheckForExtension = [&](const char* name, bool required,
|
||||
bool* has_extension = nullptr) -> bool {
|
||||
if (std::find_if(available_extension_list.begin(), available_extension_list.end(),
|
||||
[&](const VkExtensionProperties& properties) {
|
||||
return !strcmp(name, properties.extensionName);
|
||||
|
@ -405,9 +406,14 @@ bool VulkanContext::SelectDeviceExtensions(ExtensionList* extension_list, bool e
|
|||
{
|
||||
INFO_LOG(VIDEO, "Enabling extension: %s", name);
|
||||
extension_list->push_back(name);
|
||||
if (has_extension)
|
||||
*has_extension = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (has_extension)
|
||||
*has_extension = false;
|
||||
|
||||
if (required)
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Vulkan: Missing required extension %s.", name);
|
||||
|
@ -420,6 +426,7 @@ bool VulkanContext::SelectDeviceExtensions(ExtensionList* extension_list, bool e
|
|||
if (enable_surface && !CheckForExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true))
|
||||
return false;
|
||||
|
||||
CheckForExtension(VK_NV_GLSL_SHADER_EXTENSION_NAME, false, &m_supports_nv_glsl_extension);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -81,6 +81,7 @@ public:
|
|||
{
|
||||
return m_device_features.occlusionQueryPrecise == VK_TRUE;
|
||||
}
|
||||
bool SupportsNVGLSLExtension() const { return m_supports_nv_glsl_extension; }
|
||||
// Helpers for getting constants
|
||||
VkDeviceSize GetUniformBufferAlignment() const
|
||||
{
|
||||
|
@ -125,6 +126,8 @@ private:
|
|||
VkPhysicalDeviceFeatures m_device_features = {};
|
||||
VkPhysicalDeviceProperties m_device_properties = {};
|
||||
VkPhysicalDeviceMemoryProperties m_device_memory_properties = {};
|
||||
|
||||
bool m_supports_nv_glsl_extension = false;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<VulkanContext> g_vulkan_context;
|
||||
|
|
|
@ -215,19 +215,22 @@ bool VideoBackend::Initialize(void* window_handle)
|
|||
|
||||
// Create main wrapper instances.
|
||||
g_object_cache = std::make_unique<ObjectCache>();
|
||||
g_shader_cache = std::make_unique<ShaderCache>();
|
||||
g_framebuffer_manager = std::make_unique<FramebufferManager>();
|
||||
g_renderer = std::make_unique<Renderer>(std::move(swap_chain));
|
||||
|
||||
// Invoke init methods on main wrapper classes.
|
||||
// These have to be done before the others because the destructors
|
||||
// for the remaining classes may call methods on these.
|
||||
if (!g_object_cache->Initialize() || !FramebufferManager::GetInstance()->Initialize() ||
|
||||
!StateTracker::CreateInstance() || !Renderer::GetInstance()->Initialize())
|
||||
if (!g_object_cache->Initialize() || !g_shader_cache->Initialize() ||
|
||||
!FramebufferManager::GetInstance()->Initialize() || !StateTracker::CreateInstance() ||
|
||||
!Renderer::GetInstance()->Initialize())
|
||||
{
|
||||
PanicAlert("Failed to initialize Vulkan classes.");
|
||||
g_renderer.reset();
|
||||
StateTracker::DestroyInstance();
|
||||
g_framebuffer_manager.reset();
|
||||
g_shader_cache.reset();
|
||||
g_object_cache.reset();
|
||||
g_command_buffer_mgr.reset();
|
||||
g_vulkan_context.reset();
|
||||
|
@ -250,6 +253,7 @@ bool VideoBackend::Initialize(void* window_handle)
|
|||
g_renderer.reset();
|
||||
StateTracker::DestroyInstance();
|
||||
g_framebuffer_manager.reset();
|
||||
g_shader_cache.reset();
|
||||
g_object_cache.reset();
|
||||
g_command_buffer_mgr.reset();
|
||||
g_vulkan_context.reset();
|
||||
|
@ -276,6 +280,7 @@ void VideoBackend::Shutdown()
|
|||
{
|
||||
g_command_buffer_mgr->WaitForGPUIdle();
|
||||
|
||||
g_shader_cache.reset();
|
||||
g_object_cache.reset();
|
||||
g_command_buffer_mgr.reset();
|
||||
g_vulkan_context.reset();
|
||||
|
@ -291,7 +296,7 @@ void VideoBackend::Video_Cleanup()
|
|||
|
||||
// Save all cached pipelines out to disk for next time.
|
||||
if (g_ActiveConfig.bShaderCache)
|
||||
g_object_cache->SavePipelineCache();
|
||||
g_shader_cache->SavePipelineCache();
|
||||
|
||||
g_perf_query.reset();
|
||||
g_texture_cache.reset();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// Refer to the license.txt file included.
|
||||
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
|
@ -10,12 +11,11 @@
|
|||
#include "VideoCommon/FPSCounter.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
static constexpr u64 FPS_REFRESH_INTERVAL = 1000;
|
||||
static constexpr u64 FPS_REFRESH_INTERVAL = 250000;
|
||||
|
||||
FPSCounter::FPSCounter()
|
||||
{
|
||||
m_update_time.Update();
|
||||
m_render_time.Update();
|
||||
m_last_time = Common::Timer::GetTimeUs();
|
||||
}
|
||||
|
||||
void FPSCounter::LogRenderTimeToFile(u64 val)
|
||||
|
@ -23,24 +23,24 @@ void FPSCounter::LogRenderTimeToFile(u64 val)
|
|||
if (!m_bench_file.is_open())
|
||||
m_bench_file.open(File::GetUserPath(D_LOGS_IDX) + "render_time.txt");
|
||||
|
||||
m_bench_file << val << std::endl;
|
||||
m_bench_file << std::fixed << std::setprecision(8) << (val / 1000.0) << std::endl;
|
||||
}
|
||||
|
||||
void FPSCounter::Update()
|
||||
{
|
||||
if (m_update_time.GetTimeDifference() >= FPS_REFRESH_INTERVAL)
|
||||
{
|
||||
m_update_time.Update();
|
||||
m_fps = m_counter - m_fps_last_counter;
|
||||
m_fps_last_counter = m_counter;
|
||||
m_bench_file.flush();
|
||||
}
|
||||
|
||||
u64 time = Common::Timer::GetTimeUs();
|
||||
u64 diff = time - m_last_time;
|
||||
if (g_ActiveConfig.bLogRenderTimeToFile)
|
||||
{
|
||||
LogRenderTimeToFile(m_render_time.GetTimeDifference());
|
||||
m_render_time.Update();
|
||||
}
|
||||
LogRenderTimeToFile(diff);
|
||||
|
||||
m_counter++;
|
||||
m_frame_counter++;
|
||||
m_time_since_update += diff;
|
||||
m_last_time = time;
|
||||
|
||||
if (m_time_since_update >= FPS_REFRESH_INTERVAL)
|
||||
{
|
||||
m_fps = m_frame_counter / (m_time_since_update / 1000000.0);
|
||||
m_frame_counter = 0;
|
||||
m_time_since_update = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include <fstream>
|
||||
|
||||
#include "Common/Timer.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class FPSCounter
|
||||
{
|
||||
|
@ -17,14 +17,12 @@ public:
|
|||
// Called when a frame is rendered (updated every second).
|
||||
void Update();
|
||||
|
||||
unsigned int GetFPS() const { return m_fps; }
|
||||
float GetFPS() const { return m_fps; }
|
||||
private:
|
||||
unsigned int m_fps = 0;
|
||||
unsigned int m_counter = 0;
|
||||
unsigned int m_fps_last_counter = 0;
|
||||
Common::Timer m_update_time;
|
||||
|
||||
Common::Timer m_render_time;
|
||||
u64 m_last_time = 0;
|
||||
u64 m_time_since_update = 0;
|
||||
u32 m_frame_counter = 0;
|
||||
float m_fps = 0;
|
||||
std::ofstream m_bench_file;
|
||||
|
||||
void LogRenderTimeToFile(u64 val);
|
||||
|
|
|
@ -337,7 +337,7 @@ void Renderer::DrawDebugText()
|
|||
if (g_ActiveConfig.bShowFPS || SConfig::GetInstance().m_ShowFrameCount)
|
||||
{
|
||||
if (g_ActiveConfig.bShowFPS)
|
||||
final_cyan += StringFromFormat("FPS: %u", m_fps_counter.GetFPS());
|
||||
final_cyan += StringFromFormat("FPS: %.2f", m_fps_counter.GetFPS());
|
||||
|
||||
if (g_ActiveConfig.bShowFPS && SConfig::GetInstance().m_ShowFrameCount)
|
||||
final_cyan += " - ";
|
||||
|
|
|
@ -57,6 +57,9 @@ void Host_ShowVideoConfig(void*, const std::string&)
|
|||
void Host_YieldToUI()
|
||||
{
|
||||
}
|
||||
void Host_UpdateProgressDialog(const char* caption, int position, int total)
|
||||
{
|
||||
}
|
||||
std::unique_ptr<cInterfaceBase> HostGL_CreateGLInterface()
|
||||
{
|
||||
return nullptr;
|
||||
|
|
Loading…
Reference in New Issue